廖JS課程

1. 字符串


  • ASCII字符可以以\#XX形式的十六進制表示间景,例如:
'\x41' // 等同于A
  • 還可以用\u####表示一個Unicode字符:
'\u4e2d\u6587' // 等同于'中文'
  • 由于多行字符串用\n寫起來比較費事豌研,所以最新的ES6標準新增了一種多行字符串的表示方法,* ... *表示:
alert(`多行
字符串
測試`);

2. 數(shù)組


  • 多維數(shù)組
    舉例履肃,構(gòu)建二維數(shù)組,規(guī)模為mxn,值全部初始化為initial
    構(gòu)建一個二維數(shù)組

3. 對象


  • JS中有一點很奇特组力,就是JavaScript規(guī)定,訪問一個對象中不存在的屬性不報錯抖拴,而是返回undefined燎字。

4. Map和Set


JavaScript的默認對象表示方式{}可以視為其他語言中的MapDictionary的數(shù)據(jù)結(jié)構(gòu),即一組鍵值對阿宅。
 但是JavaScript的對象有個小問題候衍,就是鍵必須是字符串。但實際上Number或者其他數(shù)據(jù)類型作為鍵也是非常合理的洒放。
 為了解決這個問題蛉鹿,最新的ES6規(guī)范引入了新的數(shù)據(jù)類型Map

4.1 Map

Map是一組鍵值對的結(jié)構(gòu)往湿,具有極快的查找速度妖异。
 舉個例子,假設(shè)要根據(jù)同學的名字查找對應(yīng)的成績领追,如果用Array實現(xiàn)他膳,需要兩個Array

var names = ['Michael', 'Bob', 'Tracy'];
var scores = [95, 75, 85];

給定一個名字,要查找對應(yīng)的成績绒窑,就先要在names中找到對應(yīng)的位置棕孙,再從scores取出對應(yīng)的成績,Array越長些膨,耗時越長散罕。
 如果用Map實現(xiàn),只需要一個“名字”-“成績”的對照表傀蓉,直接根據(jù)名字查找成績欧漱,無論這個表有多大,查找速度都不會變慢葬燎。用JavaScript寫一個Map如下:

var m = new Map([['Michael', 95], ['Bob', 75], 
        ['Tracy', 85]]);
m.get('Michael'); // 95

通過上面的例子误甚,可以看出Map其實看成一個二維數(shù)組缚甩,Map中的每一個元素都是一個一維數(shù)組,有一個key 和 與之對應(yīng)的一個value窑邦。

  • Map的具體操作
     初始化Map需要一個二維數(shù)組擅威,或者直接初始化一個空Map
    Map的具體操作

     由于一個key只能對應(yīng)一個value冈钦,所以郊丛,多次對一個key放入value,后面的值會把前面的值沖掉:
    Map的值覆蓋

4.2 Set

SetMap類似瞧筛,也是一組key的集合厉熟,但不存儲value。由于key不能重復(fù)较幌,所以揍瑟,在Set中,沒有重復(fù)的key乍炉。
 要創(chuàng)建一個Set绢片,需要提供一個Array作為輸入,或者直接創(chuàng)建一個空Set

var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3

重復(fù)元素在Set中自動被過濾

var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}

通過add(key)方法可以添加元素到Set中岛琼,可以重復(fù)添加底循,但不會有效果

>>> 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可以采用下標循環(huán),遍歷MapSet就無法使用下標槐瑞。
 為了統(tǒng)一集合類型此叠,ES6標準引入了新的iterable類型,Array随珠、MapSet都屬于iterable類型
 具有iterable類型的集合可以通過新的for ... of循環(huán)來遍歷for ... of循環(huán)是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...infor...of的區(qū)別
     for ... in循環(huán)由于歷史遺留問題窗看,它遍歷的實際上是對象的屬性名稱。一個Array數(shù)組實際上也是一個對象倦炒,它的每個元素的索引被視為一個屬性显沈。
     當我們手動給Array對象添加了額外的屬性后,for ... in循環(huán)將帶來意想不到的意外效果:
    `for ... in`的缺陷

     上面的例子中逢唤,for ... in循環(huán)將把name包括在內(nèi)拉讯,但Arraylength屬性卻不包括在內(nèi)
     for ... of循環(huán)則完全修復(fù)了這些問題鳖藕,它只循環(huán)集合本身的元素
    `for...of`彌補了`for...in`的缺陷
  • iterable內(nèi)置的forEach方法
     for...of方法相比魔慷,forEach方法更好。它接收一個函數(shù)著恩,每次迭代就自動回調(diào)該函數(shù)院尔。
     1. ArrayforEach
 var a = ['A', 'B', 'C'];
    a.forEach(function (element, index, array) { 
         // element: 指向當前元素的值 
         // index: 指向當前索引 
         // array: 指向Array對象本身
         alert(element);
 });

2. SetArray類似蜻展,但Set沒有索引,因此回調(diào)函數(shù)最多兩個參數(shù)

    var s = new Set(['A', 'B', 'C']);
    s.forEach(function (element, set) {
       console.log(element); // 'A','B','C'
    });

3. Map的回調(diào)函數(shù)參數(shù)依次為value邀摆、keymap本身

    var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
    m.forEach(function (value, key, map) {
        console.log(value); // 'x','y','z'
    });

6. 函數(shù)


6.1 函數(shù)的定義與調(diào)用

  • arguments
     JavaScript還有一個免費贈送的關(guān)鍵字arguments纵顾,它只在函數(shù)內(nèi)部起作用,和this關(guān)鍵字一樣栋盹,并且永遠指向當前函數(shù)的調(diào)用者傳入的所有參數(shù)施逾。arguments類似Array,但它不是一個Array例获。
     利用arguments汉额,你可以獲得調(diào)用者傳入的所有參數(shù)。也就是說躏敢,即使函數(shù)不定義任何參數(shù)闷愤,還是可以拿到參數(shù)的值
     實際上arguments最常用于判斷傳入?yún)?shù)的個數(shù)件余。注意區(qū)別arguments.lengtharguments.callee.length讥脐。前者是實際傳入的參數(shù)的個數(shù),后者是定義函數(shù)時啼器,定義的形參的個數(shù)旬渠。

  • rest參數(shù)
     由于JavaScript函數(shù)允許接收任意個參數(shù),于是我們就不得不用arguments來獲取所有參數(shù):

    使用`arguments`獲取額外傳入的參數(shù)

     為了獲取除了已定義參數(shù)a端壳、b之外的參數(shù)告丢,我們不得不用arguments,并且循環(huán)要從索引2開始以便排除前兩個參數(shù),這種寫法很別扭亏栈,只是為了獲得額外的rest參數(shù)粒竖,有沒有更好的方法?
     DuangB妗!栗精!闯参,ES6標準引入了rest參數(shù),上面的函數(shù)可以改寫為:
    `rest`的使用

     注意:現(xiàn)在瀏覽器大都還不支持...rest的寫法悲立。

  • 小心你的return語句
     JavaScript引擎有一個在行末自動添加分號的機制鹿寨,這可能讓你栽到return語句的一個大坑:

function foo() {
    return
        { name: 'foo' };
}
console.log(foo()); // undefined
---> 相當于下面:
function foo() {
    return; // 自動添加了分號,相當于return undefined;
        { name: 'foo' }; // 這行語句已經(jīng)沒法執(zhí)行到了
}

所以正確的多行寫法是:

function foo() {
    return { // 這里不會自動加分號薪夕,因為{表示語句尚未結(jié)束
        name: 'foo'
    };
}

6.2 變量的作用域

  • 變量提升
     變量的聲明會提升脚草,但是賦值不會被提升
'use strict';
function foo() {
    var x = 'Hello, ' + y; // 'Hello, undefined'
    alert(x);
    var y = 'Bob';
}
foo();
  • 命名空間
     全局變量會綁定到window上原献,不同的JavaScript文件如果使用了相同的全局變量玩讳,或者定義了相同名字的頂層函數(shù)涩蜘,都會造成命名沖突,并且很難被發(fā)現(xiàn)熏纯。
     減少沖突的一個方法是把自己的所有變量和函數(shù)全部綁定到一個全局變量中:
// 唯一的全局變量MYAPP:
var MYAPP = {};
// 其他變量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函數(shù):
MYAPP.foo = function () {
    return 'foo';
};

把自己的代碼全部放入唯一的名字空間MYAPP中同诫,會大大減少全局變量沖突的可能。
 許多著名的JavaScript庫都是這么干的:jQuery樟澜,YUI误窖,underscore等等。

  • 局部作用域
     由于JavaScript的變量作用域?qū)嶋H上是函數(shù)內(nèi)部秩贰,是沒有塊級作用域這個概念的霹俺,我們在for循環(huán)等語句塊中是無法定義具有局部作用域的變量的。
     為了解決塊級作用域毒费,ES6引入了新的關(guān)鍵字let丙唧,let替代var可以申明一個塊級作用域的變量
使用`let`聲明一個塊級作用域
  • 常量
     由于varlet申明的是變量觅玻,如果要申明一個常量想际,在ES6之前是不行的,我們通常用全部大寫的變量來表示“這是一個常量溪厘,不要修改它的值”胡本。
     ES6標準引入了新的關(guān)鍵字const來定義常量,constlet都具有塊級作用域畸悬。
'use strict';
 const PI = 3.14;
 PI = 3; // 某些瀏覽器不報錯侧甫,但是無效果!
 PI; // 3.14

6.3 generator

generator(生成器)是ES6標準引入的新的數(shù)據(jù)類型蹋宦。一個generator看上去像一個函數(shù)披粟,但可以返回多次
 先看一個generator的栗子:

`generator`的小栗子

 再舉個栗子冷冗,fibnacci數(shù)列:
`generator`對象中的 `next( )`方法介紹

 直接調(diào)用一個generator和調(diào)用函數(shù)不一樣守屉,fib(5)僅僅是創(chuàng)建了一個generator對象,還沒有去執(zhí)行它贾惦。
 調(diào)用generator對象有兩個方法,一是不斷地調(diào)用generator對象的next()方法敦捧,如上面的栗子所示须板。

  • generator對象的next( )方法
     next( )方法會執(zhí)行generator的代碼,然后兢卵,每次遇到yield x;就返回一個對象{value: x, done: true/false}习瑰,然后“暫停”秽荤。返回的value就是yield的返回值甜奄,done表示這個generator是否已經(jīng)執(zhí)行結(jié)束了柠横。如果donetrue,則value就是return的返回值课兄。
     當執(zhí)行到donetrue時牍氛,這個generator對象就已經(jīng)全部執(zhí)行完畢,不要再繼續(xù)調(diào)用next()了烟阐。

  • 使用for...of遍歷generator對象
     第二個方法是直接用for ... of循環(huán)迭代generator對象搬俊,這種方式不需要我們自己判斷done需要注意蜒茄,使用for...of方法遍歷不到return返回的值唉擂。

  • generator的用處
     因為generator可以在執(zhí)行過程中多次返回,所以它看上去就像一個可以記住執(zhí)行狀態(tài)的函數(shù)檀葛。利用這一點玩祟,寫一個generator就可以實現(xiàn)需要用面向?qū)ο蟛拍軐崿F(xiàn)的功能。
      generator還有另一個巨大的好處屿聋,就是把異步回調(diào)代碼變成“同步”代碼空扎。例如Ajax,舉個栗子:

**為了保證異步事件的執(zhí)行順序胜臊,需要嵌套勺卢,十分丑陋**

像上面的例子中,回調(diào)越多象对,為了保證代碼的執(zhí)行順序黑忱,代碼嵌套的層級越多,代碼越難看勒魔。
  有了generator的美好時代甫煞,用AJAX時可以這么寫:

使用`generator`展開異步事件

  看上去是同步的代碼,實際執(zhí)行是異步的冠绢。

7. 標準對象


7.1 Date

  • 獲取時區(qū)
     Date對象表示的時間總是按瀏覽器所在時區(qū)顯示的抚吠,不過我們既可以顯示本地時間,也可以顯示調(diào)整后的UTC時間
    獲取時區(qū)

     那么在JavaScript中如何進行時區(qū)轉(zhuǎn)換呢弟胀?實際上楷力,只要我們傳遞的是一個number類型的時間戳,我們就不用關(guān)心時區(qū)轉(zhuǎn)換孵户。任何瀏覽器都可以把一個時間戳正確轉(zhuǎn)換為本地時間萧朝。
  • 時間戳
     時間戳是個什么東西?時間戳是一個自增的整數(shù)夏哭,它表示從1970年1月1日零時整的GMT時區(qū)開始的那一刻检柬,到現(xiàn)在的毫秒數(shù)。假設(shè)瀏覽器所在電腦的時間是準確的竖配,那么世界上無論哪個時區(qū)的電腦何址,它們此刻產(chǎn)生的時間戳數(shù)字都是一樣的里逆,所以,時間戳可以精確地表示一個時刻用爪,并且與時區(qū)無關(guān)原押。
     所以,我們只需要傳遞時間戳项钮,或者把時間戳從數(shù)據(jù)庫里讀出來班眯,再讓JavaScript自動轉(zhuǎn)換為當?shù)貢r間就可以了。
     要獲取當前時間戳烁巫,可以用:
    獲取當前時間戳

7.2 正則表達式

  • 貪婪匹配
     需要特別指出的是署隘,正則匹配默認是貪婪匹配,也就是匹配盡可能多的字符亚隙。
    貪婪匹配

     由于\d+采用貪婪匹配磁餐,直接把后面的0全部匹配了,結(jié)果0*只能匹配空字符串了阿弃。
     必須讓\d+采用非貪婪匹配(也就是盡可能少匹配)诊霹,才能把后面的0匹配出來,加個?就可以讓\d+采用非貪婪匹配渣淳。
    非貪婪匹配

     注意:上面兩個例子中使用了^$脾还,這樣可以在全局上進行一次匹配。
  • 全局匹配
     全局匹配類似搜索入愧,因此不能使用/^...$/鄙漏,那樣只會最多匹配一次

8. 面向?qū)ο缶幊?/h1>

8.1 原型繼承

在傳統(tǒng)的基于Class的語言如Java棺蛛、C++中怔蚌,繼承的本質(zhì)是擴展一個已有的Class,并生成新的Subclass旁赊。
 由于這類語言嚴格區(qū)分類和實例桦踊,繼承實際上是類型的擴展。但是JavaScript由于采用原型繼承终畅,我們無法直接擴展一個Class籍胯,因為根本不存在Class這種類型
 比較新穎的繼承方法的實現(xiàn):

原型繼承

9. 瀏覽器


由于JavaScript的出現(xiàn)就是為了能在瀏覽器中運行离福,所以杖狼,瀏覽器自然JavaScript開發(fā)者必須要關(guān)注的。
 目前主流的瀏覽器分這么幾種:

  • IE 6~11:國內(nèi)用得最多的IE瀏覽器术徊,歷來對W3C標準支持差本刽。從IE10開始支持ES6標準鲸湃;
  • ChromeGoogle出品的基于Webkit內(nèi)核瀏覽器赠涮,內(nèi)置了非常強悍的JavaScript引擎——V8子寓。由于Chrome一經(jīng)安裝就時刻保持自升級,所以不用管它的版本笋除,最新版早就支持ES6了斜友;
  • SarafiApple的Mac系統(tǒng)自帶的基于Webkit內(nèi)核的瀏覽器,從OS X10.7 Lion自帶的6.1版本開始支持ES6垃它,目前最新的OS X 10.10 Yosemite自帶的Sarafi版本是8.x鲜屏,早已支持ES6;
  • FirefoxMozilla自己研制的Gecko內(nèi)核和JavaScript引擎OdinMonkey国拇。早起的Firefox按版本發(fā)布洛史,后來終于聰明地學習Chrome的做法進行自升級,時刻保持最新酱吝;
  • 移動設(shè)備上目前iOSAndroid兩大陣營分別主要使用AppleSafariGoogleChrome也殖,由于兩者都是Webkit核心,結(jié)果HTML5首先在手機上全面普及(桌面絕對是Microsoft拖了后腿)务热,對JavaScript的標準支持也很好忆嗜,最新版本均支持ES6
     其他瀏覽器如Opera等由于市場份額太小就被自動忽略了崎岂。
     另外還要注意識別各種國產(chǎn)瀏覽器捆毫,如某某安全瀏覽器,某某旋風瀏覽器冲甘,它們只是做了一個殼绩卤,其核心調(diào)用的是IE,也有號稱同時支持IE和Webkit的“雙核”瀏覽器损合。
     不同的瀏覽器對JavaScript支持的差異主要是省艳,有些API的接口不一樣,比如AJAX嫁审,F(xiàn)ile接口跋炕。對于ES6標準,不同的瀏覽器對各個特性支持也不一樣律适。
     在編寫JavaScript的時候辐烂,就要充分考慮到瀏覽器的差異,盡量讓同一份JavaScript代碼能運行在不同的瀏覽器上捂贿。
     JavaScript可以獲取瀏覽器提供的很多對象纠修,并進行操作:
  • window
     window對象不但充當全局作用域,而且表示瀏覽器窗口厂僧。
     window對象有innerWidthinnerHeight屬性扣草,可以獲取瀏覽器窗口的內(nèi)部寬度和高度。內(nèi)部寬高是指除去菜單欄、工具欄辰妙、邊框等占位元素后鹰祸,用于顯示網(wǎng)頁的凈寬高
     注意:IE <= 8 不兼容這兩個屬性的密浑。
  • navigator
     navigator對象表示瀏覽器的信息蛙婴,最常用的屬性包括:

navigator.appName:瀏覽器名稱;
navigator.appVersion:瀏覽器版本尔破;
navigator.language:瀏覽器設(shè)置的語言街图;
navigator.platform:操作系統(tǒng)類型;
navigator.userAgent:瀏覽器設(shè)定的User-Agent字符串懒构。

請注意餐济,navigator的信息可以很容易地被用戶修改,所以JavaScript讀取的值不一定是正確的胆剧,盡量不要使用navigator.userAgent提供的用戶代理字符串去判斷客戶端瀏覽器颤介。
 正確的方法是充分利用JavaScript對不存在屬性返回undefined的特性,直接用短路運算符||計算

var width = window.innerWidth || document.body.clientWidth;
  • screen
     screen對象表示屏幕的信息赞赖,常用的屬性有:

screen.width:屏幕寬度滚朵,以像素為單位;
screen.height:屏幕高度前域,以像素為單位辕近;
screen.colorDepth:返回顏色位數(shù),如8匿垄、16移宅、24;

  • location
     location對象表示當前頁面的URL信息。例如椿疗,一個完整的URL

    一條完整的URL

     可以用location.href獲取漏峰。要獲得URL各個部分的值,可以這么寫:
    使用`location` 對象的不同屬性獲取 `URL` 中各部分的值

     要加載一個新頁面届榄,可以調(diào)用location.assign()浅乔,使用location.href也是可以的。如果要重新加載當前頁面铝条,調(diào)用location.reload()方法非常方便靖苇。

  • document
     document對象表示當前頁面。由于HTML在瀏覽器中以DOM形式表示為樹形結(jié)構(gòu)班缰,document對象就是整個DOM數(shù)的根節(jié)點贤壁。
     document對象還有一個cookie屬性,可以獲取當前頁面的Cookie埠忘。
     聊聊Cookie
     Cookie是由服務(wù)器發(fā)送的key-value標示符脾拆。因為HTTP協(xié)議是無狀態(tài)的馒索,但是服務(wù)器要區(qū)分到底是哪個用戶發(fā)過來的請求,就可以用Cookie來區(qū)分名船。當一個用戶成功登錄后双揪,服務(wù)器發(fā)送一個Cookie給瀏覽器,例如user=ABC123XYZ(加密的字符串)...包帚,此后,瀏覽器訪問該網(wǎng)站時运吓,會在請求頭附上這個Cookie渴邦,服務(wù)器根據(jù)Cookie即可區(qū)分出用戶。
     為了防止XSS盜取Cookie中重要的信息拘哨,服務(wù)器在設(shè)置Cookie時可以使用httpOnly谋梭,設(shè)定了httpOnlyCookie將不能被JavaScript讀取。這一類Cookie稱為HTTP專有cookie倦青,HTTP專有cookie可以從瀏覽器或者服務(wù)器設(shè)置瓮床,但是只能從服務(wù)器端讀取,因此JavaScript無法獲取HTTP專有cookie的值产镐。
     為了確保安全隘庄,服務(wù)器端在設(shè)置Cookie時,應(yīng)該始終堅持使用httpOnly癣亚。

  • history
     history對象保存了瀏覽器的歷史記錄丑掺,JavaScript可以調(diào)用history對象的back()forward (),相當于用戶點擊了瀏覽器的“后退”或“前進”按鈕述雾。
     這個對象屬于歷史遺留對象街州,對于現(xiàn)代Web頁面來說,由于大量使用AJAX頁面交互玻孟,簡單粗暴地調(diào)用history.back()可能會讓用戶感到非常憤怒唆缴。
     新手開始設(shè)計Web頁面時喜歡在登錄頁登錄成功時調(diào)用history.back(),試圖回到登錄前的頁面黍翎。這是一種錯誤的方法面徽。
     任何情況,你都不應(yīng)該使用history這個對象了匣掸。

10. DOM操作


  • element.insertBefore(newNode, referenceNode);
  • element.insertAdjacentHTML(positionString,insertHTML);

positionString:
beforeBegin afterBegin beforeEnd afterEnd

11. 表單操作

  • form元素的submit事件的事件監(jiān)聽函數(shù)中斗忌,return true告訴來告訴瀏覽器繼續(xù)提交return false表示瀏覽器將不會提交表單旺聚,這種情況通常對應(yīng)用戶輸入有誤织阳,提示用戶錯誤信息后終止提交form。
  • 沒有name屬性的<input>的數(shù)據(jù)不會被提交砰粹。

12. 操作文件


在HTML表單中唧躲,可以上傳文件的唯一控件就是<input type="file">造挽。
 尤其需要注意:

當一個表單包含<input type="file">時,表單的enctype必須指定為multipart/form-data弄痹,method必須指定為post饭入,瀏覽器才能正確編碼并以multipart/form-data格式發(fā)送表單的數(shù)據(jù)

出于安全考慮肛真,瀏覽器只允許用戶點擊<input type="file">來選擇本地文件谐丢,用JavaScript對<input type="file">value賦值是沒有任何效果的。當用戶選擇了上傳某個文件后蚓让,JavaScript也無法獲得該文件的真實路徑

JS無法獲取上傳文件的真實路徑

 通常乾忱,上傳的文件都由后臺服務(wù)器處理,JavaScript可以在提交表單時對文件擴展名做檢查历极,以便防止用戶上傳無效格式的文件窄瘟。

  • File API
     由于JavaScript對用戶上傳的文件操作非常有限,尤其是無法讀取文件內(nèi)容趟卸,使得很多需要操作文件的網(wǎng)頁不得不用Flash這樣的第三方插件來實現(xiàn)蹄葱。
     隨著HTML5的普及,新增的File API允許JavaScript讀取文件內(nèi)容锄列,獲得更多的文件信息图云。
     HTML5File API提供了FileFileReader兩個主要對象,可以獲得文件信息并讀取文件邻邮。

13. Ajax


  • 兼容模式生成XHR對象


    兼容模式生成XHR對象
  • 原生Ajax寫法步驟
     當創(chuàng)建了XMLHttpRequest對象后琼稻,要先設(shè)置onreadystatechange的回調(diào)函數(shù)。在回調(diào)函數(shù)中饶囚,通常我們只需通過readyState === 4判斷請求是否完成帕翻,如果已完成,再根據(jù)status === 200(也可以寫成(status >= 200 && status < 300) || status === 304)判斷是否是一個成功的響應(yīng)萝风。
     XMLHttpRequest對象的open()方法有3個參數(shù)嘀掸,第一個參數(shù)指定是GET還是POST,第二個參數(shù)指定URL地址规惰,第三個參數(shù)指定是否使用異步睬塌,默認是true,所以不用寫歇万。
     注意揩晴,千萬不要把第三個參數(shù)指定為false,否則瀏覽器將停止響應(yīng)贪磺,直到AJAX請求完成硫兰。如果這個請求耗時10秒,那么10秒內(nèi)你會發(fā)現(xiàn)瀏覽器處于“假死”狀態(tài)寒锚。
     最后調(diào)用send()方法才真正發(fā)送請求劫映。GET請求不需要參數(shù)违孝,POST請求需要把body部分以字符串或者FormData對象傳進去
  • 安全限制
     默認情況下泳赋,JavaScript在發(fā)送AJAX請求時雌桑,URL的域名必須和當前頁面完全一致。這就是瀏覽器的同源策略造成的祖今。
     完全一致的意思是校坑,域名要相同(www.example.comexample.com不同),協(xié)議要相同(http和https不同)千诬,端口號要相同(默認是:80端口耍目,它和:8080就不同)。有的瀏覽器口子松一點大渤,允許端口不同,大多數(shù)瀏覽器都會嚴格遵守這個限制掸绞。
     那是不是用JavaScript無法請求外域(就是其他網(wǎng)站)的URL了呢泵三?方法還是有的,大概有這么幾種
     1. 通過Flash插件發(fā)送HTTP請求衔掸,這種方式可以繞過瀏覽器的安全限制烫幕,但必須安裝Flash,并且跟Flash交互敞映。不過Flash用起來麻煩较曼,而且現(xiàn)在用得也越來越少了。
     2. 通過在同源域名下架設(shè)一個代理服務(wù)器來轉(zhuǎn)發(fā)振愿,JavaScript負責把請求發(fā)送到代理服務(wù)器捷犹。
    代理服務(wù)器實現(xiàn)外域訪問

     代理服務(wù)器再把結(jié)果返回,這樣就遵守了瀏覽器的同源策略冕末。這種方式麻煩之處在于需要服務(wù)器端額外做開發(fā)萍歉。
     3.** 稱為JSONP,它有個限制档桃,只能用GET請求枪孩,并且要求返回JavaScript**。這種方式跨域?qū)嶋H上是利用了瀏覽器允許跨域引用JavaScript資源藻肄。
     以163的股票查詢URL為例蔑舞,對于URL:http://api.money.126.net/data/feed/0000001,1399001?callback=refreshPrice,你將得到如下返回:
refreshPrice({"0000001":{"code": "0000001", ... });

因此我們需要首先在頁面中準備好回調(diào)函數(shù):

function refreshPrice(data) {
    var p = document.getElementById('test-jsonp');
    p.innerHTML = '當前價格:' +
        data['0000001'].name +': ' + 
        data['0000001'].price + '嘹屯;' +
        data['1399001'].name + ': ' +
        data['1399001'].price;
  }

最后用getPrice( )函數(shù)觸發(fā)攻询,就完成了跨域加載數(shù)據(jù):

動態(tài)加入`<script>`元素

 4. CORS
 如果瀏覽器支持HTML5,那么就可以一勞永逸地使用新的跨域策略:CORS州弟。
 CORS全稱Cross-Origin Resource Sharing蜕窿,是HTML5規(guī)范定義的如何跨域訪問資源谋逻。
 了解CORS前,我們先搞明白概念:
 Origin表示本域桐经,也就是瀏覽器當前頁面的域毁兆。當JavaScript向外域(如sina.com)發(fā)起請求后,瀏覽器收到響應(yīng)后阴挣,首先檢查Access-Control-Allow-Origin是否包含本域气堕,如果是,則此次跨域請求成功畔咧,如果不是茎芭,則請求失敗,JavaScript將無法獲取到響應(yīng)的任何數(shù)據(jù)誓沸。
 可見梅桩,跨域能否成功,取決于對方服務(wù)器是否愿意給你設(shè)置一個正確Access-Control-Allow-Origin拜隧,決定權(quán)始終在對方手中宿百。
 上面這種跨域請求,稱之為“簡單請求”洪添。簡單請求包括GET垦页、HEADPOSTPOSTContent-Type類型僅限application/x-www-form-urlencodedmultipart/form-datatext/plain)干奢,并且不能出現(xiàn)任何自定義頭(例如痊焊,X-Custom: 12345),通常能滿足90%的需求忿峻。
 在引用外域資源時薄啥,除了JavaScript和CSS外,都要驗證CORS逛尚。例如罪佳,當你引用了某個第三方CDN上的字體文件時:
使用@font-face引用 `第三方CDN`上的字體

 對于PUTDELETE以及其他類型如application/jsonPOST請求黑低,在發(fā)送AJAX請求之前赘艳,瀏覽器會先發(fā)送一個OPTIONS請求(稱為preflighted請求)到這個URL上,詢問目標服務(wù)器是否接受:

OPTIONS /path/to/resource HTTP/1.1
Host: bar.com
Origin: http://bar.com
Access-Control-Request-Method: POST

服務(wù)器必須響應(yīng)并明確指出允許的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

瀏覽器確認服務(wù)器響應(yīng)的Access-Control-Allow-Methods頭確實包含將要發(fā)送的AJAX請求的Method克握,才會繼續(xù)發(fā)送AJAX蕾管,否則粘优,拋出一個錯誤古劲。
 由于以POSTPUT方式傳送JSON格式的數(shù)據(jù)在REST中很常見倘是,所以要跨域正確處理POSTPUT請求停团,服務(wù)器端必須正確響應(yīng)OPTIONS請求旷坦。
 瀏覽器對CORS的實現(xiàn)情況:

  • IE8中引入了XDR類型
    XDR與XHR有一些不同之處:
    cookie不會隨請求發(fā)送掏熬,也不會隨響應(yīng)返回;
    只能設(shè)置請求頭部信息中的Content-Type字段秒梅;
    不能訪問響應(yīng)頭部信息旗芬;
    只支持GET和POST方法;
     所有的XDR請求都是異步執(zhí)行的捆蜀,請求返回后疮丛,會觸發(fā)load事件,但只能訪問響應(yīng)的原始文本(responseText)辆它,不能訪問status誊薄,并且,只要響應(yīng)有效就會觸發(fā)load事件锰茉。如果失敗(包括響應(yīng)中缺少Access-Control-Allow-Origin頭部)就會觸發(fā)error事件呢蔫。
     為了支持POST請求,XDR對象提供了contentType屬性飒筑,用來表示發(fā)送數(shù)據(jù)的格式片吊。
  • 其他瀏覽器對CORS的支持
     其他支持HTML5的瀏覽器,都通過XHR對象實現(xiàn)對CORS的原生支持扬霜。要請求位于另一個域中的資源定鸟,使用標準的XHR對象并在open( )方法中傳入絕對URL即可而涉。
     跨域的XHR對象可以訪問status屬性著瓶,而且還支持同步請求。不過啼县,跨域XHR對象也會有一些限制
    不同使用setRequestHeader( )設(shè)置自定義頭部材原;
    不能發(fā)送和接收cookie;
    調(diào)用getAllResponseHeader( )方法總會返回空字符串季眷;
    注意以下內(nèi)容余蟹,XDR和跨域XHR有如下共同屬性/方法:
    abort( ):用于停止正在進行的請求
    onerror:用于替代onreadystatechange檢測錯誤
    onload:用于替代onreadystatechange檢測成功
    responseText:用于取得響應(yīng)內(nèi)容
    send( ):用于發(fā)送請求

跨瀏覽器的CORS

跨瀏覽器的`CORS`實現(xiàn)

14. promise


在JavaScript的世界中,所有代碼都是單線程執(zhí)行的子刮。
 由于這個“缺陷”威酒,導致JavaScript的所有網(wǎng)絡(luò)操作,瀏覽器事件挺峡,都必須是異步執(zhí)行葵孤。異步操作會在將來的某個時間點觸發(fā)一個函數(shù)調(diào)用,AJAX就是典型的異步操作橱赠。

使用promise可以實現(xiàn)ajax的鏈式操作

 這種鏈式寫法的好處在于尤仍,先統(tǒng)一執(zhí)行AJAX邏輯,不關(guān)心如何處理結(jié)果狭姨,然后宰啦,根據(jù)結(jié)果是成功還是失敗苏遥,在將來的某個時候調(diào)success函數(shù)或fail函數(shù)
 古人云:“君子一諾千金”赡模,這種“承諾將來會執(zhí)行”的對象在JavaScript中稱為Promise對象田炭。
 Promise有各種開源實現(xiàn),在ES6中被統(tǒng)一規(guī)范纺裁,由瀏覽器直接支持诫肠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市欺缘,隨后出現(xiàn)的幾起案子栋豫,更是在濱河造成了極大的恐慌,老刑警劉巖谚殊,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丧鸯,死亡現(xiàn)場離奇詭異,居然都是意外死亡嫩絮,警方通過查閱死者的電腦和手機丛肢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剿干,“玉大人蜂怎,你說我怎么就攤上這事≈枚” “怎么了杠步?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長榜轿。 經(jīng)常有香客問我幽歼,道長,這世上最難降的妖魔是什么谬盐? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任甸私,我火速辦了婚禮,結(jié)果婚禮上飞傀,老公的妹妹穿的比我還像新娘皇型。我一直安慰自己,他們只是感情好砸烦,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布弃鸦。 她就那樣靜靜地躺著,像睡著了一般外冀。 火紅的嫁衣襯著肌膚如雪寡键。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音西轩,去河邊找鬼员舵。 笑死,一個胖子當著我的面吹牛藕畔,可吹牛的內(nèi)容都是我干的马僻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼注服,長吁一口氣:“原來是場噩夢啊……” “哼韭邓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起溶弟,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤女淑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后辜御,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸭你,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年擒权,在試婚紗的時候發(fā)現(xiàn)自己被綠了袱巨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡碳抄,死狀恐怖愉老,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剖效,我是刑警寧澤嫉入,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站贱鄙,受9級特大地震影響劝贸,放射性物質(zhì)發(fā)生泄漏姨谷。R本人自食惡果不足惜逗宁,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梦湘。 院中可真熱鬧瞎颗,春花似錦、人聲如沸捌议。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓣颅。三九已至倦逐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宫补,已是汗流浹背檬姥。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工曾我, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人健民。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓抒巢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秉犹。 傳聞我的和親對象是個殘疾皇子蛉谜,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容