1. 字符串
- ASCII字符可以以
\#XX
形式的十六進制表示,例如:
'\x41' // 等同于A
- 還可以用
\u####
表示一個Unicode字符:
'\u4e2d\u6587' // 等同于'中文'
- 由于多行字符串用
\n
寫起來比較費事,所以最新的ES6標準新增了一種多行字符串的表示方法,* ... *
表示:
alert(`多行
字符串
測試`);
2. 數組
- 多維數組
舉例,構建二維數組,規模為mxn
,值全部初始化為initial
構建一個二維數組
3. 對象
- JS中有一點很奇特,就是JavaScript規定,訪問一個對象中不存在的屬性不報錯,而是返回
undefined
。
4. Map和Set
JavaScript的默認對象表示方式{}
可以視為其他語言中的Map
或Dictionary
的數據結構,即一組鍵值對。
但是JavaScript的對象有個小問題,就是鍵必須是字符串。但實際上Number或者其他數據類型作為鍵也是非常合理的。
為了解決這個問題,最新的ES6規范引入了新的數據類型Map
。
4.1 Map
Map
是一組鍵值對的結構,具有極快的查找速度。
舉個例子,假設要根據同學的名字查找對應的成績,如果用Array
實現,需要兩個Array
:
var names = ['Michael', 'Bob', 'Tracy'];
var scores = [95, 75, 85];
給定一個名字,要查找對應的成績,就先要在names中找到對應的位置,再從scores取出對應的成績,Array越長,耗時越長。
如果用Map實現,只需要一個“名字”-“成績”的對照表,直接根據名字查找成績,無論這個表有多大,查找速度都不會變慢。用JavaScript寫一個Map如下:
var m = new Map([['Michael', 95], ['Bob', 75],
['Tracy', 85]]);
m.get('Michael'); // 95
通過上面的例子,可以看出Map
其實看成一個二維數組,Map
中的每一個元素都是一個一維數組,有一個key
和 與之對應的一個value
。
- Map的具體操作
初始化Map
需要一個二維數組,或者直接初始化一個空Map
。
Map的具體操作
由于一個key
只能對應一個value
,所以,多次對一個key
放入value
,后面的值會把前面的值沖掉:
Map的值覆蓋
4.2 Set
Set
和Map
類似,也是一組key
的集合,但不存儲value
。由于key
不能重復,所以,在Set
中,沒有重復的key
。
要創建一個Set
,需要提供一個Array
作為輸入,或者直接創建一個空Set
:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
重復元素在Set
中自動被過濾:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
通過add(key)
方法可以添加元素到Set中,可以重復添加,但不會有效果:
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}
通過delete(key)
方法可以刪除元素:
var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}
5. iterable
遍歷Array
可以采用下標循環,遍歷Map
和Set
就無法使用下標。
為了統一集合類型,ES6標準引入了新的iterable
類型,Array
、Map
和Set
都屬于iterable
類型
具有iterable
類型的集合可以通過新的for ... of
循環來遍歷。for ... of
循環是ES6引入的新的語法。
- iterable的使用
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍歷Array
alert(x);
}
for (var x of s) { // 遍歷Set
alert(x);
}
for (var x of m) { // 遍歷Map
alert(x[0] + '=' + x[1]);
}
-
for...in
和for...of
的區別
for ... in
循環由于歷史遺留問題,它遍歷的實際上是對象的屬性名稱。一個Array
數組實際上也是一個對象,它的每個元素的索引被視為一個屬性。
當我們手動給Array
對象添加了額外的屬性后,for ... in
循環將帶來意想不到的意外效果:
`for ... in`的缺陷
上面的例子中,for ... in
循環將把name
包括在內,但Array
的length
屬性卻不包括在內。
for ... of
循環則完全修復了這些問題,它只循環集合本身的元素:
`for...of`彌補了`for...in`的缺陷 -
iterable
內置的forEach
方法
與for...of
方法相比,forEach
方法更好。它接收一個函數,每次迭代就自動回調該函數。
1.Array
的forEach
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
// element: 指向當前元素的值
// index: 指向當前索引
// array: 指向Array對象本身
alert(element);
});
2. Set
與Array
類似,但Set
沒有索引,因此回調函數最多兩個參數:
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, set) {
console.log(element); // 'A','B','C'
});
3. Map
的回調函數參數依次為value
、key
和map
本身
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value); // 'x','y','z'
});
6. 函數
6.1 函數的定義與調用
arguments
JavaScript還有一個免費贈送的關鍵字arguments
,它只在函數內部起作用,和this
關鍵字一樣,并且永遠指向當前函數的調用者傳入的所有參數。arguments
類似Array
,但它不是一個Array
。
利用arguments
,你可以獲得調用者傳入的所有參數。也就是說,即使函數不定義任何參數,還是可以拿到參數的值。
實際上arguments
最常用于判斷傳入參數的個數。注意區別arguments.length
和arguments.callee.length
。前者是實際傳入的參數的個數,后者是定義函數時,定義的形參的個數。-
rest參數
由于JavaScript函數允許接收任意個參數,于是我們就不得不用arguments
來獲取所有參數:
使用`arguments`獲取額外傳入的參數
為了獲取除了已定義參數a
、b
之外的參數,我們不得不用arguments
,并且循環要從索引2開始以便排除前兩個參數,這種寫法很別扭,只是為了獲得額外的rest
參數,有沒有更好的方法?
Duang!??!,ES6標準引入了rest
參數,上面的函數可以改寫為:
`rest`的使用
注意:現在瀏覽器大都還不支持...rest
的寫法。 小心你的return語句
JavaScript引擎有一個在行末自動添加分號的機制,這可能讓你栽到return語句的一個大坑:
function foo() {
return
{ name: 'foo' };
}
console.log(foo()); // undefined
---> 相當于下面:
function foo() {
return; // 自動添加了分號,相當于return undefined;
{ name: 'foo' }; // 這行語句已經沒法執行到了
}
所以正確的多行寫法是:
function foo() {
return { // 這里不會自動加分號,因為{表示語句尚未結束
name: 'foo'
};
}
6.2 變量的作用域
- 變量提升
變量的聲明會提升,但是賦值不會被提升。
'use strict';
function foo() {
var x = 'Hello, ' + y; // 'Hello, undefined'
alert(x);
var y = 'Bob';
}
foo();
- 命名空間
全局變量會綁定到window
上,不同的JavaScript文件如果使用了相同的全局變量,或者定義了相同名字的頂層函數,都會造成命名沖突,并且很難被發現。
減少沖突的一個方法是把自己的所有變量和函數全部綁定到一個全局變量中:
// 唯一的全局變量MYAPP:
var MYAPP = {};
// 其他變量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函數:
MYAPP.foo = function () {
return 'foo';
};
把自己的代碼全部放入唯一的名字空間MYAPP
中,會大大減少全局變量沖突的可能。
許多著名的JavaScript庫都是這么干的:jQuery
,YUI
,underscore
等等。
- 局部作用域
由于JavaScript的變量作用域實際上是函數內部,是沒有塊級作用域這個概念的,我們在for循環等語句塊中是無法定義具有局部作用域的變量的。
為了解決塊級作用域,ES6引入了新的關鍵字let
,用let
替代var
可以申明一個塊級作用域的變量。
- 常量
由于var
和let
申明的是變量,如果要申明一個常量,在ES6之前是不行的,我們通常用全部大寫的變量來表示“這是一個常量,不要修改它的值”。
ES6標準引入了新的關鍵字const
來定義常量,const
與let
都具有塊級作用域。
'use strict';
const PI = 3.14;
PI = 3; // 某些瀏覽器不報錯,但是無效果!
PI; // 3.14
6.3 generator
generator
(生成器)是ES6標準引入的新的數據類型。一個generator
看上去像一個函數,但可以返回多次。
先看一個generator
的栗子:
再舉個栗子,
fibnacci
數列:直接調用一個
generator
和調用函數不一樣,fib(5)
僅僅是創建了一個generator
對象,還沒有去執行它。調用
generator
對象有兩個方法,一是不斷地調用generator
對象的next()
方法,如上面的栗子所示。
generator
對象的next( )
方法
next( )
方法會執行generator
的代碼,然后,每次遇到yield x
;就返回一個對象{value: x, done: true/false}
,然后“暫停”。返回的value
就是yield
的返回值,done
表示這個generator
是否已經執行結束了。如果done
為true
,則value
就是return
的返回值。
當執行到done
為true
時,這個generator
對象就已經全部執行完畢,不要再繼續調用next()
了。使用
for...of
遍歷generator
對象
第二個方法是直接用for ... of
循環迭代generator
對象,這種方式不需要我們自己判斷done
。需要注意,使用for...of
方法遍歷不到return
返回的值。generator
的用處
因為generator
可以在執行過程中多次返回,所以它看上去就像一個可以記住執行狀態的函數。利用這一點,寫一個generator
就可以實現需要用面向對象才能實現的功能。
generator
還有另一個巨大的好處,就是把異步回調代碼變成“同步”代碼。例如Ajax,舉個栗子:
像上面的例子中,回調越多,為了保證代碼的執行順序,代碼嵌套的層級越多,代碼越難看。
有了generator
的美好時代,用AJAX時可以這么寫:
看上去是同步的代碼,實際執行是異步的。
7. 標準對象
7.1 Date
- 獲取時區
Date
對象表示的時間總是按瀏覽器所在時區顯示的,不過我們既可以顯示本地時間,也可以顯示調整后的UTC時間:
獲取時區
那么在JavaScript中如何進行時區轉換呢?實際上,只要我們傳遞的是一個number
類型的時間戳,我們就不用關心時區轉換。任何瀏覽器都可以把一個時間戳正確轉換為本地時間。 - 時間戳
時間戳是個什么東西?時間戳是一個自增的整數,它表示從1970年1月1日零時整的GMT時區開始的那一刻,到現在的毫秒數。假設瀏覽器所在電腦的時間是準確的,那么世界上無論哪個時區的電腦,它們此刻產生的時間戳數字都是一樣的,所以,時間戳可以精確地表示一個時刻,并且與時區無關。
所以,我們只需要傳遞時間戳,或者把時間戳從數據庫里讀出來,再讓JavaScript自動轉換為當地時間就可以了。
要獲取當前時間戳,可以用:
獲取當前時間戳
7.2 正則表達式
- 貪婪匹配
需要特別指出的是,正則匹配默認是貪婪匹配,也就是匹配盡可能多的字符。
貪婪匹配
由于\d+
采用貪婪匹配,直接把后面的0全部匹配了,結果0*
只能匹配空字符串了。
必須讓\d+
采用非貪婪匹配(也就是盡可能少匹配),才能把后面的0匹配出來,加個?
就可以讓\d+
采用非貪婪匹配。
非貪婪匹配
注意:上面兩個例子中使用了^
和$
,這樣可以在全局上進行一次匹配。 - 全局匹配
全局匹配類似搜索,因此不能使用/^...$/
,那樣只會最多匹配一次。
8. 面向對象編程
8.1 原型繼承
在傳統的基于Class的語言如Java、C++中,繼承的本質是擴展一個已有的Class,并生成新的Subclass。
由于這類語言嚴格區分類和實例,繼承實際上是類型的擴展。但是JavaScript由于采用原型繼承,我們無法直接擴展一個Class,因為根本不存在Class這種類型。
比較新穎的繼承方法的實現:
9. 瀏覽器
由于JavaScript的出現就是為了能在瀏覽器中運行,所以,瀏覽器自然JavaScript開發者必須要關注的。
目前主流的瀏覽器分這么幾種:
-
IE 6~11
:國內用得最多的IE瀏覽器,歷來對W3C標準支持差。從IE10
開始支持ES6標準; -
Chrome
:Google
出品的基于Webkit
內核瀏覽器,內置了非常強悍的JavaScript引擎——V8
。由于Chrome
一經安裝就時刻保持自升級,所以不用管它的版本,最新版早就支持ES6了; -
Sarafi
:Apple
的Mac系統自帶的基于Webkit
內核的瀏覽器,從OS X
10.7 Lion自帶的6.1版本開始支持ES6,目前最新的OS X
10.10 Yosemite自帶的Sarafi版本是8.x,早已支持ES6; -
Firefox
:Mozilla
自己研制的Gecko
內核和JavaScript引擎OdinMonkey
。早起的Firefox
按版本發布,后來終于聰明地學習Chrome的做法進行自升級,時刻保持最新; -
移動設備上目前
iOS
和Android
兩大陣營分別主要使用Apple
的Safari
和Google
的Chrome
,由于兩者都是Webkit
核心,結果HTML5
首先在手機上全面普及(桌面絕對是Microsoft拖了后腿),對JavaScript的標準支持也很好,最新版本均支持ES6。
其他瀏覽器如Opera
等由于市場份額太小就被自動忽略了。
另外還要注意識別各種國產瀏覽器,如某某安全瀏覽器,某某旋風瀏覽器,它們只是做了一個殼,其核心調用的是IE,也有號稱同時支持IE和Webkit的“雙核”瀏覽器。
不同的瀏覽器對JavaScript支持的差異主要是,有些API的接口不一樣,比如AJAX,File接口。對于ES6標準,不同的瀏覽器對各個特性支持也不一樣。
在編寫JavaScript的時候,就要充分考慮到瀏覽器的差異,盡量讓同一份JavaScript代碼能運行在不同的瀏覽器上。
JavaScript可以獲取瀏覽器提供的很多對象,并進行操作: - window
window
對象不但充當全局作用域,而且表示瀏覽器窗口。
window
對象有innerWidth
和innerHeight
屬性,可以獲取瀏覽器窗口的內部寬度和高度。內部寬高是指除去菜單欄、工具欄、邊框等占位元素后,用于顯示網頁的凈寬高。
注意:IE <= 8 不兼容這兩個屬性的。 - navigator
navigator
對象表示瀏覽器的信息,最常用的屬性包括:
navigator.appName:瀏覽器名稱;
navigator.appVersion:瀏覽器版本;
navigator.language:瀏覽器設置的語言;
navigator.platform:操作系統類型;
navigator.userAgent:瀏覽器設定的User-Agent字符串。
請注意,navigator
的信息可以很容易地被用戶修改,所以JavaScript讀取的值不一定是正確的,盡量不要使用navigator.userAgent
提供的用戶代理字符串去判斷客戶端瀏覽器。
正確的方法是充分利用JavaScript對不存在屬性返回undefined
的特性,直接用短路運算符||
計算:
var width = window.innerWidth || document.body.clientWidth;
- screen
screen
對象表示屏幕的信息,常用的屬性有:
screen.width:屏幕寬度,以像素為單位;
screen.height:屏幕高度,以像素為單位;
screen.colorDepth:返回顏色位數,如8、16、24;
-
location
location
對象表示當前頁面的URL
信息。例如,一個完整的URL
:
一條完整的URL
可以用location.href
獲取。要獲得URL
各個部分的值,可以這么寫:
使用`location` 對象的不同屬性獲取 `URL` 中各部分的值
要加載一個新頁面,可以調用location.assign()
,使用location.href
也是可以的。如果要重新加載當前頁面,調用location.reload()
方法非常方便。 document
document
對象表示當前頁面。由于HTML
在瀏覽器中以DOM
形式表示為樹形結構,document
對象就是整個DOM
數的根節點。
document
對象還有一個cookie
屬性,可以獲取當前頁面的Cookie
。
聊聊Cookie
:
Cookie
是由服務器發送的key-value標示符。因為HTTP協議是無狀態的,但是服務器要區分到底是哪個用戶發過來的請求,就可以用Cookie來區分。當一個用戶成功登錄后,服務器發送一個Cookie
給瀏覽器,例如user=ABC123XYZ
(加密的字符串)...,此后,瀏覽器訪問該網站時,會在請求頭附上這個Cookie
,服務器根據Cookie
即可區分出用戶。
為了防止XSS
盜取Cookie
中重要的信息,服務器在設置Cookie
時可以使用httpOnly
,設定了httpOnly
的Cookie
將不能被JavaScript讀取。這一類Cookie
稱為HTTP專有cookie,HTTP專有cookie可以從瀏覽器或者服務器設置,但是只能從服務器端讀取,因此JavaScript無法獲取HTTP專有cookie的值。
為了確保安全,服務器端在設置Cookie時,應該始終堅持使用httpOnly。history
history
對象保存了瀏覽器的歷史記錄,JavaScript可以調用history
對象的back()
或forward ()
,相當于用戶點擊了瀏覽器的“后退”或“前進”按鈕。
這個對象屬于歷史遺留對象,對于現代Web頁面來說,由于大量使用AJAX
和頁面交互,簡單粗暴地調用history.back()
可能會讓用戶感到非常憤怒。
新手開始設計Web頁面時喜歡在登錄頁登錄成功時調用history.back()
,試圖回到登錄前的頁面。這是一種錯誤的方法。
任何情況,你都不應該使用history
這個對象了。
10. DOM操作
- element.insertBefore(
newNode
,referenceNode
); - element.insertAdjacentHTML(
positionString
,insertHTML
);
positionString:
beforeBegin afterBegin beforeEnd afterEnd
11. 表單操作
-
form
元素的submit
事件的事件監聽函數中,return true
告訴來告訴瀏覽器繼續提交;return false
表示瀏覽器將不會提交表單,這種情況通常對應用戶輸入有誤,提示用戶錯誤信息后終止提交form。 -
沒有
name
屬性的<input>
的數據不會被提交。
12. 操作文件
在HTML表單中,可以上傳文件的唯一控件就是<input type="file">
。
尤其需要注意:
當一個表單包含
<input type="file">
時,表單的enctype
必須指定為multipart/form-data
,method
必須指定為post
,瀏覽器才能正確編碼并以multipart/form-data
格式發送表單的數據。
出于安全考慮,瀏覽器只允許用戶點擊<input type="file">
來選擇本地文件,用JavaScript對<input type="file">
的value
賦值是沒有任何效果的。當用戶選擇了上傳某個文件后,JavaScript也無法獲得該文件的真實路徑:
通常,上傳的文件都由后臺服務器處理,JavaScript可以在提交表單時對文件擴展名做檢查,以便防止用戶上傳無效格式的文件。
-
File API
由于JavaScript對用戶上傳的文件操作非常有限,尤其是無法讀取文件內容,使得很多需要操作文件的網頁不得不用Flash這樣的第三方插件來實現。
隨著HTML5
的普及,新增的File API
允許JavaScript讀取文件內容,獲得更多的文件信息。
HTML5
的File API
提供了File
和FileReader
兩個主要對象,可以獲得文件信息并讀取文件。
13. Ajax
-
兼容模式生成XHR對象
兼容模式生成XHR對象 - 原生Ajax寫法步驟
當創建了XMLHttpRequest
對象后,要先設置onreadystatechange
的回調函數。在回調函數中,通常我們只需通過readyState === 4
判斷請求是否完成,如果已完成,再根據status === 200
(也可以寫成(status >= 200 && status < 300) || status === 304
)判斷是否是一個成功的響應。
XMLHttpRequest
對象的open()
方法有3個參數,第一個參數指定是GET
還是POST
,第二個參數指定URL
地址,第三個參數指定是否使用異步,默認是true
,所以不用寫。
注意,千萬不要把第三個參數指定為false
,否則瀏覽器將停止響應,直到AJAX請求完成。如果這個請求耗時10秒,那么10秒內你會發現瀏覽器處于“假死”狀態。
最后調用send()
方法才真正發送請求。GET
請求不需要參數,POST
請求需要把body
部分以字符串或者FormData
對象傳進去。 - 安全限制
默認情況下,JavaScript在發送AJAX請求時,URL的域名必須和當前頁面完全一致。這就是瀏覽器的同源策略造成的。
完全一致的意思是,域名要相同(www.example.com和example.com不同),協議要相同(http和https不同),端口號要相同(默認是:80端口,它和:8080就不同)。有的瀏覽器口子松一點,允許端口不同,大多數瀏覽器都會嚴格遵守這個限制。
那是不是用JavaScript無法請求外域(就是其他網站)的URL了呢?方法還是有的,大概有這么幾種:
1. 通過Flash插件發送HTTP請求,這種方式可以繞過瀏覽器的安全限制,但必須安裝Flash,并且跟Flash交互。不過Flash用起來麻煩,而且現在用得也越來越少了。
2. 通過在同源域名下架設一個代理服務器來轉發,JavaScript負責把請求發送到代理服務器。
代理服務器實現外域訪問
代理服務器再把結果返回,這樣就遵守了瀏覽器的同源策略。這種方式麻煩之處在于需要服務器端額外做開發。
3.** 稱為JSONP
,它有個限制,只能用GET
請求,并且要求返回JavaScript
**。這種方式跨域實際上是利用了瀏覽器允許跨域引用JavaScript
資源。
以163的股票查詢URL為例,對于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice,你將得到如下返回:
refreshPrice({"0000001":{"code": "0000001", ... });
因此我們需要首先在頁面中準備好回調函數:
function refreshPrice(data) {
var p = document.getElementById('test-jsonp');
p.innerHTML = '當前價格:' +
data['0000001'].name +': ' +
data['0000001'].price + ';' +
data['1399001'].name + ': ' +
data['1399001'].price;
}
最后用getPrice( )
函數觸發,就完成了跨域加載數據:
4. CORS
如果瀏覽器支持
HTML5
,那么就可以一勞永逸地使用新的跨域策略:CORS
了。CORS
全稱Cross-Origin Resource Sharing
,是HTML5
規范定義的如何跨域訪問資源。了解CORS前,我們先搞明白概念:
Origin
表示本域,也就是瀏覽器當前頁面的域。當JavaScript向外域(如sina.com)發起請求后,瀏覽器收到響應后,首先檢查Access-Control-Allow-Origin
是否包含本域,如果是,則此次跨域請求成功,如果不是,則請求失敗,JavaScript將無法獲取到響應的任何數據。可見,跨域能否成功,取決于對方服務器是否愿意給你設置一個正確
Access-Control-Allow-Origin
,決定權始終在對方手中。上面這種跨域請求,稱之為“簡單請求”。簡單請求包括
GET
、HEAD
和POST
(POST
的Content-Type
類型僅限application/x-www-form-urlencoded
、multipart/form-data
和text/plain
),并且不能出現任何自定義頭(例如,X-Custom: 12345
),通常能滿足90%的需求。在引用外域資源時,除了JavaScript和CSS外,都要驗證CORS。例如,當你引用了某個第三方CDN上的字體文件時:
對于
PUT
、DELETE
以及其他類型如application/json
的POST
請求,在發送AJAX
請求之前,瀏覽器會先發送一個OPTIONS
請求(稱為preflighted
請求)到這個URL上,詢問目標服務器是否接受:
OPTIONS /path/to/resource HTTP/1.1
Host: bar.com
Origin: http://bar.com
Access-Control-Request-Method: POST
服務器必須響應并明確指出允許的Method:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.com
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS
Access-Control-Max-Age: 86400
瀏覽器確認服務器響應的Access-Control-Allow-Methods
頭確實包含將要發送的AJAX
請求的Method,才會繼續發送AJAX,否則,拋出一個錯誤。
由于以POST
、PUT
方式傳送JSON格式的數據在REST中很常見,所以要跨域正確處理POST
和PUT
請求,服務器端必須正確響應OPTIONS
請求。
瀏覽器對CORS
的實現情況:
- IE8中引入了XDR類型
XDR與XHR有一些不同之處:
cookie不會隨請求發送,也不會隨響應返回;
只能設置請求頭部信息中的Content-Type字段;
不能訪問響應頭部信息;
只支持GET和POST方法;
所有的XDR請求都是異步執行的,請求返回后,會觸發load事件,但只能訪問響應的原始文本(responseText),不能訪問status,并且,只要響應有效就會觸發load事件。如果失敗(包括響應中缺少Access-Control-Allow-Origin頭部)就會觸發error事件。
為了支持POST請求,XDR對象提供了contentType屬性,用來表示發送數據的格式。
- 其他瀏覽器對CORS的支持
其他支持HTML5的瀏覽器,都通過XHR對象實現對CORS的原生支持。要請求位于另一個域中的資源,使用標準的XHR對象并在open( )方法中傳入絕對URL即可。
跨域的XHR對象可以訪問status屬性,而且還支持同步請求。不過,跨域XHR對象也會有一些限制:
不同使用setRequestHeader( )設置自定義頭部;
不能發送和接收cookie;
調用getAllResponseHeader( )方法總會返回空字符串;
注意以下內容,XDR和跨域XHR有如下共同屬性/方法:
abort( ):用于停止正在進行的請求
onerror:用于替代onreadystatechange檢測錯誤
onload:用于替代onreadystatechange檢測成功
responseText:用于取得響應內容
send( ):用于發送請求
跨瀏覽器的CORS
14. promise
在JavaScript的世界中,所有代碼都是單線程執行的。
由于這個“缺陷”,導致JavaScript的所有網絡操作,瀏覽器事件,都必須是異步執行。異步操作會在將來的某個時間點觸發一個函數調用,AJAX就是典型的異步操作。
這種鏈式寫法的好處在于,先統一執行AJAX邏輯,不關心如何處理結果,然后,根據結果是成功還是失敗,在將來的某個時候調success函數或fail函數。
古人云:“君子一諾千金”,這種“承諾將來會執行”的對象在JavaScript中稱為Promise對象。
Promise有各種開源實現,在ES6中被統一規范,由瀏覽器直接支持。