這篇文章是因為之前看過一篇文章,總結了一些技能,但是並不詳細,之後陸續還會有新的文章。
事件模型
js中有兩種事件,DOM0和DOM2.
- DOM0
DOM0事件很簡單,就是在dom上綁定事件,代碼如下
document.getElementById('click').onclick = function(){
console.log('123')
}
解除綁定很簡單,賦值為null即可。
document.getElementById('click').onclick = null
但是這種事件雖然在所有瀏覽器都能運行,但是有個很明顯的缺陷,就是不能綁定多個相同事件,如果綁定了多個相同事件,後面的會覆蓋前面的
- DOM2
第二種就是事件捕獲和事件冒泡。代碼上來説就是註冊事件是addEventListener,解除綁定是removeEventListener。阻止冒泡的方法是stopPropagation。
聲明提升
其實js中的聲明提升很簡單,就是指變量聲明提升和函數的聲明提升。聲明提示的意思就是把變量或者函數在編譯的時候提升到環境的頂部,並賦值undefined
- 變量聲明
變量聲明,js會把變量聲明分為兩個部分,一個是聲明操作(var a),一個是賦值操作(a=1),這裏的聲明提升針對的是聲明操作,賦值操作還是在原來的地方等待執行。
- 函數聲明
定義函數的方式有兩種,函數聲明和函數表達式,函數表達式和變量聲明同等。
在js編譯時,函數聲明會把函數聲明和整個函數體都提升到環境的頂部,所以可以在函數聲明前調用這個函數,如以下代碼
foo()
function foo(){
console.log(111)
}
- 聲明的順序
函數聲明是優先於變量聲明的
1.js編譯時,會把所有的函數聲明提到頂部,如果有重複的進行覆蓋
2.把所有的變量提到頂部,並賦值undefined,如果有重複的進行覆蓋
接下來看兩個典型的例子
alert(a)
a();
var a=3;
function a(){
alert(10)
}
alert(a)
a=6;
a();
------------分割線------------------
alert(a)
a();
var a=3;
var a=function(){
alert(10)
}
alert(a)
a=6;
a();
第一部分:
1.函數聲明優先於變量聲明,所以,剛開始,a就是function a(){alert(10)} ,就會看到這個函數。
2.a(),執行函數,就是出現alert(10)
3.執行了var a=3; 所以alert(a)就是顯示3
4.由於a不是一個函數了,所以往下在執行到a()的時候, 報錯。
第二部分運行結果:
1.underfind
2.報錯
在之前説過,預解析是把帶有var和function關鍵字的事先聲明,但不會賦值。所以一開始是underfind,然後報錯是因為執行到a()的時候,a並不是一個函數。
繼承
js繼承的幾種方法
1.原型鏈繼承,將父類的實例作為子類的原型
function Cat(){}
Cat.prototype = New Animal()
2.構造繼承,利用父元素的構造函數來增強子元素的實例,其實就是把父類的實例屬性給了子類
function Cat(){
Animal.call(this);
}
3.實例繼承,父類的實例增加屬性作為子類的實例返回
function Cat(){
var instance = new Animal();
intance.name = 'aaa';
return instance;
}
4.拷貝繼承,無法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in 訪問到)
function Cat(name){
var animal = new Animal();
for(var p in animal){
Cat.prototype[p] = animal[p];
}
Cat.prototype.name = name || 'Tom';
}
5.組合繼承,通過調用父類,實現了繼承父類的屬性和保留傳參的優點,父類的實例作為子類的原型,實現了函數的複用
function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;//組合繼承也是需要修復構造函數指向的。
6.寄生組合繼承,通過第三方的一個空類,來繼承父類的函數複用,父類的屬性的繼承通過在子類中調用父類實現。
/**
* 通用方法實現子類繼承父類
* @param {function} child 子類構造函數
* @param {function} father 被繼承的父類構造函數
*/
function inheritPrototype(child, father) {
var prototype = object(father.prototype); //創建一個指定原型的對象
prototype.constructor = child; //增強對象
child.prototype = prototype; //子類的原型等於該對象
}
function father(name) {
this.faName = 'father';
}
father.prototype.getfaName = function() {
console.log(this.faName);
};
function child(args) {
this.chName = 'child';
father.apply(this,[]);
}
inheritPrototype(child, father); //子類的原型等於new 空函數(), 而new 空函數()出來的對象的原型等於父類的原型
child.prototype.getchName = function() {
console.log(this.chName);
};
console.log( child.prototype.isPrototypeOf(new child()) ); //true
console.log(new child() instanceof child); //true
優點:1.只調用一次父類的構造函數,避免了在子類原型上創建不必要的,多餘的屬性
2.原型鏈保持不變
https://blog.csdn.net/xuqingg...
參考https://www.cnblogs.com/humin...
跨域
跨域,是指不能執行其他網站的腳本,是因為瀏覽器的同源策略(域名,協議,端口均相同)引起的,是瀏覽器對js實施的安全限制。但是script標籤本身就可以訪問其它域的資源,不受瀏覽器同源策略的限制,可以通過在頁面動態創建script標籤。
限制:
1.cookie,localstorage,indexDB無法讀取
2.DOM和js對象無法獲取
3.ajax請求發不出去
解決辦法:
1.jsonp 但是隻限於get請求
1.1 通過動態創建script標籤,請求一個帶參數的網址實現跨域
var script = document.createElement('script');
script.src = 'http://www.aaa.com/username=aaaa&callback=cb';
document.body.appendChild(script);
function cb(res){}
1.2 jquery的ajax支持jsonp
$.ajax({
url:'http://www.aaa.com',
type:'get',
dataType:'json',
jsonCallback:'cb',
data:{
username:'111'
}
})
2.document.domain+iframe,但是要求主域名相同
3.window.name + iframe 跨域
4.location.hash + iframe 跨域
5.postMessage
1.html
<iframe id="iframe" src="http://www.neal.cn/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向neal傳送跨域數據
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.neal.cn');
};
// 接受domain2返回數據
window.addEventListener('message', function(e) {
alert('data from neal ---> ' + e.data);
}, false);
</script>
2.html(另一端口)
script>
// 接收domain1的數據
window.addEventListener('message', function(e) {
alert('data from nealyang ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 處理後再發回nealyang
window.parent.postMessage(JSON.stringify(data), 'http://www.nealyang.cn');
}
}, false);
</script>
6.跨域資源共享CORS
CORS是目前主流的跨域解決方案。
它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了ajax的同源策略的限制。
目前所有的瀏覽器都支持這個功能。IE要求IE10以上,IE8,9需要用XDomainRequest對象來支持這個功能。
CORS需要客户端和服務端同時支持。
整個CORS通信過程,都是瀏覽器完成的,對於開發者來説,CORS和ajax沒有什麼區別,發送請求後,瀏覽器識別到這是跨源請求,就會在頭部添加附加信息,有時還會多一次請求。所以實現CORS的關鍵是服務器,只要服務器實現了CORS接口,就可以跨源通信。
這時候就要説到請求的種類了,分為簡單請求和非簡單請求。
簡單請求
簡單請求(請求方式為HEAD,GET,POST,頭信息不超過以下字段,Accept,Accept-language,Content-language,Last-Event-ID,Content-type【application/x-www-form-urlencoded、multipart/form-data、text/plain】)。
瀏覽器直接發出CORS請求,在頭信息中會增加origin屬性(origin:http://www.baidu.com),這個屬性表明請求的來源,服務器根據這個是否同意這次請求。如果origin指定的源不在許可範圍內,服務端會返回http迴應,瀏覽器會發現沒有Access-Control-Allow-Origin字段,就會拋出異常,到XMLHttpRequest的onerror函數。
如果Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段
上面的頭信息之中,有三個與CORS請求相關的字段,都以Access-Control-開頭
- Access-Control-Allow-Origin :該字段是必須的。它的值要麼是請求時Origin字段的值,要麼是一個*,表示接受任意域名的請求
- Access-Control-Allow-Credentials: 該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設為true,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。這個值也只能設為true,如果服務器不要瀏覽器發送Cookie,刪除該字段即可。
- Access-Control-Expose-Headers:該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers裏面指定。
withCredentials 屬性
上面説到,CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials字段。
另一方面,開發者必須在AJAX請求中打開withCredentials屬性。
否則,即使服務器同意發送Cookie,瀏覽器也不會發送。或者,服務器要求設置Cookie,瀏覽器也不會處理。
但是,如果省略withCredentials設置,有的瀏覽器還是會一起發送Cookie。這時,可以顯式關閉withCredentials。
需要注意的是,如果要發送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁代碼中的document.cookie也無法讀取服務器域名下的Cookie。
非簡單請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。
瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
瀏覽器發現,這是一個非簡單請求,就自動發出一個"預檢"請求,要求服務器確認可以這樣請求。下面是這個"預檢"請求的HTTP頭信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息裏面,關鍵字段是Origin,表示請求來自哪個源。
除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。
- Access-Control-Request-Method:該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT。
- Access-Control-Request-Headers:該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header
預檢請求的迴應
服務器收到"預檢"請求以後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以後,確認允許跨源請求,就可以做出迴應.
上面的HTTP迴應中,關鍵的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以請求數據。該字段也可以設為星號,表示同意任意跨源請求。
如果瀏覽器否定了"預檢"請求,會返回一個正常的HTTP迴應,但是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不同意預檢請求,因此觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制枱會打印出報錯信息。
服務器迴應的其他CORS相關字段如下:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
- Access-Control-Allow-Methods:該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次"預檢"請求。
- Access-Control-Allow-Headers:如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
- Access-Control-Allow-Credentials: 該字段與簡單請求時的含義相同。
- Access-Control-Max-Age: 該字段可選,用來指定本次預檢請求的有效期,單位為秒。上面結果中,有效期是20天(1728000秒),即允許緩存該條迴應1728000秒(即20天),在此期間,不用發出另一條預檢請求。
瀏覽器正常請求迴應
一旦服務器通過了"預檢"請求,以後每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。
7.websocket
8.nginx