你們好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新......前端
- github:https://github.com/Daotin/Web
- 微信公衆號:Web前端之巔
- 博客園:http://www.cnblogs.com/lvonve/
- CSDN:https://blog.csdn.net/lvonve/
在這裏我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的項目。如今就讓咱們一塊兒進入 Web 前端學習的冒險之旅吧!git
Promise是一個對象,表明了將來某個將要發生的事件(,這個事件一般是一個異步操做)es6
有了Promise對象, 能夠將異步操做以同步的流程表達出來, 避免了層層嵌套的回調函數(俗稱'回調地獄')。github
ES6的Promise是一個構造函數, 用來生成promise實例。ajax
pending
: 初始化狀態fullfilled
: 成功狀態rejected
: 失敗狀態一、建立一個promise實例對象,參數是一個匿名函數,這個匿名函數有兩個參數,resolve和reject,編程
二、每個參數都是一個回調函數。而後,函數體中通常執行的是異步操做,好比發起Ajax請求,或者開啓定時器等。json
三、異步操做成功時,調用resolve回調函數,異步操做失敗時,調用reject回調函數。數組
四、在初始化Promise實例對象的時候,Promise的狀態爲pending;在調用resolve回調函數的時候,Promise的狀態爲fullfilled,表示成功狀態;在調用reject回調函數的時候,Promise的狀態爲rejected,表示失敗狀態;promise
五、 Promise的實例對象有一個方法then,參數爲兩個匿名函數,第一個匿名函數處理Promise的狀態爲fullfilled的狀況;第二個匿名函數處理Promise的狀態爲rejected的狀況;微信
六、上面說到,在異步操做成功或者失敗的時候,會調用resolve和reject函數,在這兩個回調函數中能夠傳入參數,這個參數能夠直接帶入到then中兩個匿名函數的參數中使用。好比獲取到ajax的數據,能夠將獲取的數做爲參數傳入resolve的參數中,而後會自動將這個參數傳入then的第一個匿名函數中,reject也同樣。
用圖示的方法表示:
示例:
let promise = new Promise((resolve, reject) => { console.log(111); // 執行異步操做 setTimeout(() => { console.log(222); // 執行異步操做成功,此時修改promise的狀態fullfilled resolve("success!"); // 執行異步操做成功,此時修改promise的狀態rejected reject("failed!"); }, 2000); }); promise.then((data) => { // promise的狀態fullfilled的操做 console.log("成功", data); }, () => { // promise的狀態rejected的操做 console.log("失敗", data); });
注意:當執行到resolve("success!");修改promise的狀態fullfilled的時候,後面的reject("failed!");不會執行。也就不會打印console.log("失敗");的語句。
promise案例:獲取新聞內容和評論內容
// 定義一個請求news的方法 function getNews(url) { //建立一個promise對象 let promise = new Promise((resolve, reject) => { //初始化promise狀態爲pending //啓動異步任務,發起Ajax請求 //1.建立一個 XMLHttpRequest 類型的對象 let request = new XMLHttpRequest(); // 4. 指定 xhr 狀態變化事件處理函數 request.onreadystatechange = function () { if (request.readyState === 4) { if (request.status === 200) { let news = request.response; resolve(news); } else { reject('請求失敗了...'); } } }; request.responseType = 'json'; //設置返回的數據類型 // 2. 打開與一個網址之間的鏈接 request.open("GET", url); //規定請求的方法,建立連接 // 3. 經過連接發送一次請求 request.send(); //發送 }) // 只有將promise返回,才能夠調用then方法 return promise; }; // 調用getNews,獲取新聞內容,其中一個字節爲評論內容的地址 getNews('http://localhost:3000/news?id=2') .then((news) => { // 獲取到新聞內容 console.log(news); // document.write(JSON.stringify(news)); // 獲取新聞內容中的評論地址 console.log('http://localhost:3000' + news.commentsUrl); // 遞歸獲取新聞評論內容,而且返回promise對象,以便鏈式then方法。 return getNews('http://localhost:3000' + news.commentsUrl); }, (error) => { alert(error); }) .then((comments) => { // then方法能夠鏈式編程 console.log(comments); // 把新聞的評論部分已json的格式打印出來顯示 document.write('<br><br><br><br><br>' + JSON.stringify(comments)); }, (error) => { alert(error); });
ES5 的對象屬性名都是字符串,這容易形成屬性名的衝突。好比,你使用了一個他人提供的對象,但又想爲這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現有方法產生衝突。若是有一種機制,保證每一個屬性的名字都是獨一無二的就行了,這樣就從根本上防止屬性名的衝突。這就是 ES6 引入Symbol的緣由。
一、Symbol屬性對應的值是惟一的,解決命名衝突問題
Symbol 是一種新的數據類型,跟 String,Number,Object,Boolean,null,undefined 並列。
Symbol 值經過Symbol
函數生成。這就是說,對象的屬性名如今能夠有兩種類型,一種是原來就有的字符串,另外一種就是新增的 Symbol 類型。凡是屬性名屬於 Symbol 類型,就都是獨一無二的,能夠保證不會與其餘屬性名產生衝突。
let s = Symbol(); typeof s; // symbol
上面代碼中,變量s
就是一個獨一無二的值。typeof
運算符的結果,代表變量s
是 Symbol 數據類型,而不是字符串之類的其餘類型。
注意,
Symbol
函數前不能使用new
命令,不然會報錯。這是由於生成的 Symbol 是一個原始類型的值,不是對象。也就是說,因爲 Symbol 值不是對象,因此不能添加屬性。基本上,它是一種相似於字符串的數據類型。
Symbol
函數能夠接受一個字符串做爲參數,表示對 Symbol 實例的描述,主要是爲了在控制檯顯示,或者轉爲字符串時,比較容易區分。
let s1 = Symbol('foo'); let s2 = Symbol('bar'); s1 // Symbol(foo) s2 // Symbol(bar) s1.toString() // "Symbol(foo)" s2.toString() // "Symbol(bar)"
上面代碼中,s1
和s2
是兩個 Symbol 值。若是不加參數,它們在控制檯的輸出都是Symbol()
,不利於區分。有了參數之後,就等於爲它們加上了描述,輸出的時候就可以分清,究竟是哪個值。
二、Symbol值不能與其餘數據進行計算,包括同字符串拼串
let sym = Symbol('My symbol'); "your symbol is " + sym // TypeError: can't convert symbol to string `your symbol is ${sym}` // TypeError: can't convert symbol to string
三、做爲屬性名的 Symbol
因爲每個 Symbol 值都是不相等的,這意味着 Symbol 值能夠做爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性。這對於一個對象由多個模塊構成的狀況很是有用,能防止某一個鍵被不當心改寫或覆蓋。
let mySymbol = Symbol(); // 第一種寫法 let a = {}; a[mySymbol] = 'Hello!'; // 第二種寫法 let a = { [mySymbol]: 'Hello!' }; // 第三種寫法 let a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' }); // 以上寫法都獲得一樣結果 a[mySymbol] // "Hello!"
注意,Symbol 值做爲對象屬性名時,不能用點運算符。
a.mySymbol = 'Hello!';
四、for in, for of遍歷時不會遍歷symbol屬性
let obj = { username: 'Daotin', age: 18 }; obj[symbol] = 'hello'; obj[symbol] = 'symbol'; console.log(obj); for (let i in obj) { console.log(i); }
五、內置的 Symbol 值
除了定義本身使用的 Symbol 值之外,ES6 還提供了 11 個內置的 Symbol 值,指向語言內部使用的方法。
六、Symbol.hasInstance
對象的Symbol.hasInstance
屬性,指向一個內部方法。當其餘對象使用instanceof
運算符,判斷是否爲該對象的實例時,會調用這個方法。好比,foo instanceof Foo
在語言內部,實際調用的是Foo[Symbol.hasInstance](foo)
。
七、Symbol.iterator
對象的Symbol.iterator
屬性,指向該對象的默認遍歷器方法。
Iterator 是迭代器(遍歷器)的意思。
JavaScript 原有的表示「集合」的數據結構,主要是數組(Array
)和對象(Object
),ES6 又添加了Map
和Set
。這樣就有了四種數據集合,用戶還能夠組合使用它們,定義本身的數據結構,好比數組的成員是Map
,Map
的成員是對象。這樣就須要一種統一的接口機制,來處理全部不一樣的數據結構。
遍歷器(Iterator)就是這樣一種機制。它是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就能夠完成遍歷操做(即依次處理該數據結構的全部成員)。
Iterator 的做用:
for...of
循環,Iterator 接口主要供for...of
消費。Iterator 的遍歷過程:
(1)建立一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。
(2)第一次調用指針對象的next方法,能夠將指針指向數據結構的第一個成員。
(3)第二次調用指針對象的next方法,指針就指向數據結構的第二個成員。
(4)不斷調用指針對象的next方法,直到它指向數據結構的結束位置。
每一次調用next方法,都會返回數據結構的當前成員的信息。具體來講,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束。
下面是一個模擬next
方法返回值的例子。
var it = makeIterator(['a', 'b']); it.next() // { value: "a", done: false } it.next() // { value: "b", done: false } it.next() // { value: undefined, done: true } function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++], done: false} : {value: undefined, done: true}; } }; }
對於遍歷器對象來講,done: false
和value: undefined
屬性都是能夠省略的,所以上面的makeIterator
函數能夠簡寫成下面的形式。
function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++]} : {done: true}; } }; }
ES6 規定,默認的 Iterator 接口部署在數據結構的Symbol.iterator
屬性,或者說,一個數據結構只要具備Symbol.iterator
屬性,就能夠認爲是「可遍歷的」(iterable)。
Symbol.iterator
屬性自己是一個函數,就是當前數據結構默認的遍歷器生成函數。執行這個函數,就會返回一個遍歷器。至於屬性名Symbol.iterator
,它是一個表達式,返回Symbol
對象的iterator
屬性,這是一個預約義好的、類型爲 Symbol 的特殊值,因此要放在方括號內.
const obj = { [Symbol.iterator] : function () { return { next: function () { return { value: 1, done: true }; } }; } };
上面代碼中,對象obj
是可遍歷的(iterable),由於具備Symbol.iterator
屬性。執行這個屬性,會返回一個遍歷器對象。該對象的根本特徵就是具備next
方法。每次調用next
方法,都會返回一個表明當前成員的信息對象,具備value
和done
兩個屬性。
ES6 的有些數據結構原生具有 Iterator 接口(好比數組),即不用任何處理,就能夠被for...of
循環遍歷。緣由在於,這些數據結構原生部署了Symbol.iterator
屬性(詳見下文),另一些數據結構沒有(好比對象)。凡是部署了Symbol.iterator
屬性的數據結構,就稱爲部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。
原生具有 Iterator 接口的數據結構以下。
下面的例子是數組的Symbol.iterator
屬性。
let arr = ['a', 'b', 'c']; let iter = arr[Symbol.iterator](); iter.next() // { value: 'a', done: false } iter.next() // { value: 'b', done: false } iter.next() // { value: 'c', done: false } iter.next() // { value: undefined, done: true }
上面代碼中,變量arr
是一個數組,原生就具備遍歷器接口,部署在arr
的Symbol.iterator
屬性上面。因此,調用這個屬性,就獲得遍歷器對象。
對於原生部署 Iterator 接口的數據結構,不用本身寫遍歷器生成函數,for...of
循環會自動遍歷它們。除此以外,其餘數據結構(主要是對象)的 Iterator 接口,都須要本身在Symbol.iterator
屬性上面部署,這樣纔會被for...of
循環遍歷。
一個對象若是要具有可被for...of
循環調用的 Iterator 接口,就必須在Symbol.iterator
的屬性上部署遍歷器生成方法(原型鏈上的對象具備該方法也可)。
let arr1 = [1, 2, 3, 4, 5]; let [value1, ...arr2] = arr1;
for..of..遍歷
// 原生測試 數組 let arr3 = [1, 2, 'kobe', true]; for (let i of arr3) { console.log(i); } // 字符串 string let str = 'abcdefg'; for (let item of str) { console.log(item); }