1. 程式人生 > >從零開始學 Web 之 ES6(四)ES6基礎語法二

從零開始學 Web 之 ES6(四)ES6基礎語法二

一、Promise

Promise是一個物件,代表了未來某個將要發生的事件(,這個事件通常是一個非同步操作)

有了Promise物件, 可以將非同步操作以同步的流程表達出來, 避免了層層巢狀的回撥函式(俗稱'回撥地獄')。

ES6的Promise是一個建構函式, 用來生成promise例項。

1、promise物件3個狀態

  • pending: 初始化狀態
  • fullfilled: 成功狀態
  • rejected: 失敗狀態

2、使用方法

1、建立一個promise例項物件,引數是一個匿名函式,這個匿名函式有兩個引數,resolve和reject,

2、每一個引數都是一個回撥函式。然後,函式體中一般執行的是非同步操作,比如發起Ajax請求,或者開啟定時器等。

3、非同步操作成功時,呼叫resolve回撥函式,非同步操作失敗時,呼叫reject回撥函式。

4、在初始化Promise例項物件的時候,Promise的狀態為pending;在呼叫resolve回撥函式的時候,Promise的狀態為fullfilled,表示成功狀態;在呼叫reject回撥函式的時候,Promise的狀態為rejected,表示失敗狀態;

5、 Promise的例項物件有一個方法then,引數為兩個匿名函式,第一個匿名函式處理Promise的狀態為fullfilled的情況;第二個匿名函式處理Promise的狀態為rejected的情況;

6、上面說到,在非同步操作成功或者失敗的時候,會呼叫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);
      });

二、Symbol

ES5 的物件屬性名都是字串,這容易造成屬性名的衝突。比如,你使用了一個他人提供的物件,但又想為這個物件新增新的方法(mixin 模式),新方法的名字就有可能與現有方法產生衝突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的衝突。這就是 ES6 引入Symbol的原因。

1、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)"

上面程式碼中,s1s2是兩個 Symbol 值。如果不加引數,它們在控制檯的輸出都是Symbol(),不利於區分。有了引數以後,就等於為它們加上了描述,輸出的時候就能夠分清,到底是哪一個值。

2、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

3、作為屬性名的 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!';

4、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);
}

5、內建的 Symbol 值

除了定義自己使用的 Symbol 值以外,ES6 還提供了 11 個內建的 Symbol 值,指向語言內部使用的方法。

6、Symbol.hasInstance

物件的Symbol.hasInstance屬性,指向一個內部方法。當其他物件使用instanceof運算子,判斷是否為該物件的例項時,會呼叫這個方法。比如,foo instanceof Foo在語言內部,實際呼叫的是Foo[Symbol.hasInstance](foo)

7、Symbol.iterator

物件的Symbol.iterator屬性,指向該物件的預設遍歷器方法。

三、Iterator

Iterator 是迭代器(遍歷器)的意思。

JavaScript 原有的表示“集合”的資料結構,主要是陣列(Array)和物件(Object),ES6 又添加了MapSet。這樣就有了四種資料集合,使用者還可以組合使用它們,定義自己的資料結構,比如陣列的成員是MapMap的成員是物件。這樣就需要一種統一的介面機制,來處理所有不同的資料結構。

遍歷器(Iterator)就是這樣一種機制。它是一種介面,為各種不同的資料結構提供統一的訪問機制。任何資料結構只要部署 Iterator 介面,就可以完成遍歷操作(即依次處理該資料結構的所有成員)。

Iterator 的作用:

  • 為各種資料結構,提供一個統一的、簡便的訪問介面
  • 使得資料結構的成員能夠按某種次序排列
  • ES6 創造了一種新的遍歷命令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: falsevalue: undefined屬性都是可以省略的,因此上面的makeIterator函式可以簡寫成下面的形式。

function makeIterator(array) {
  var nextIndex = 0;
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++]} :
        {done: true};
    }
  };
}

1、預設 Iterator 介面

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方法,都會返回一個代表當前成員的資訊物件,具有valuedone兩個屬性。

ES6 的有些資料結構原生具備 Iterator 介面(比如陣列),即不用任何處理,就可以被for...of迴圈遍歷。原因在於,這些資料結構原生部署了Symbol.iterator屬性(詳見下文),另外一些資料結構沒有(比如物件)。凡是部署了Symbol.iterator屬性的資料結構,就稱為部署了遍歷器介面。呼叫這個介面,就會返回一個遍歷器物件。

原生具備 Iterator 介面的資料結構如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函式的 arguments 物件
  • NodeList 物件

下面的例子是陣列的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是一個數組,原生就具有遍歷器介面,部署在arrSymbol.iterator屬性上面。所以,呼叫這個屬性,就得到遍歷器物件。

對於原生部署 Iterator 介面的資料結構,不用自己寫遍歷器生成函式,for...of迴圈會自動遍歷它們。除此之外,其他資料結構(主要是物件)的 Iterator 介面,都需要自己在Symbol.iterator屬性上面部署,這樣才會被for...of迴圈遍歷。

一個物件如果要具備可被for...of迴圈呼叫的 Iterator 介面,就必須在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的物件具有該方法也可)。

2、呼叫 Iterator 介面的場合

  • 使用解構賦值以及...三點運算子時會呼叫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);
}

相關推薦

開始 Web DOM節點

def clas scrip while p標簽 設置 ner 操作 text 大家好,這裏是「 Daotin的夢囈 」從零開始學 Web 系列教程。此文首發於「 Daotin的夢囈 」公眾號,歡迎大家訂閱關註。在這裏我會從 Web 前端零基礎開始,一步步學習 Web 相

開始 Web BOMclient系列

一、client 系列 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style

開始 Web JavaScript陣列

大家好,這裡是「 Daotin的夢囈 」從零開始學 Web 系列教程。此文首發於「 Daotin的夢囈 」公眾號,歡迎大家訂閱關注。在這裡我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的專案。現在就讓我們一起進入 Web 前端學習的冒險之旅吧! 一、陣列 1、陣列

開始 Web Ajax介面文件,驗證使用者名稱唯一性案例

>大家好,這裡是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... > > - github:https://github.com/Daotin/Web > - 微信公眾號:[Web前端之巔](https://github.com/Daotin/pi

開始 Web HTML5拖拽介面,Web儲存,自定義播放器

>大家好,這裡是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... > > - github:https://github.com/Daotin/Web > - 微信公眾號:[Web前端之巔](https://github.com/Daotin/pi

開始 Web CSSCSS初始化、定位、overflow、標籤規範

大家好,這裡是「 Daotin的夢囈 」從零開始學 Web 系列教程。此文首發於「 Daotin的夢囈 」公眾號,歡迎大家訂閱關注。在這裡我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的專案。現在就讓我們一起進入 Web 前端學習的冒險之旅吧! 一、CSS初始化

開始 Web jQuery元素的建立新增與刪除,自定義屬性

一、元素的建立新增和刪除 1、方式一:以物件的方式建立元素 append,appendTo :在被選元素所有子元素的結尾插入內容(增加子元素)。 prepend,prependTo:在被選元素所有子元素的開頭插入元素(增加子元素)。 before:在當前被選元素之後插入內容(相當於增加兄弟元素)。 af

開始 Web CSS3邊框圖片,過渡

一、邊框圖片 邊框圖片:就是給邊框加圖片背景。 我們之前加的邊框都是純顏色的邊框,那麼我們怎麼給邊框加圖片呢? 原理:把一張圖片分成九宮格的形式,然後一一對應到需要新增邊框的元素上。 並且,新增邊框圖片是以背景的方式新增的,所以會有可能文字覆蓋在邊框的情況,後面也會介紹處理方法。 語法: /* bord

開始 Web DOMDOM的概念,對標簽操作

關註 1.5 pan 什麽 tin p標簽 nod text == 大家好,這裏是「 Daotin的夢囈 」從零開始學 Web 系列教程。此文首發於「 Daotin的夢囈 」公眾號,歡迎大家訂閱關註。在這裏我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,

開始 Web BOMoffset,scroll,變速動畫函數

樣式 清理 java mar dde sof mov har width 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web前端之巔 博客園:

開始 Web jQuery為元素綁定多個相同事件,解綁事件

png 好用 添加 方式 執行 存在 區別 也會 地址 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web前端之巔 博客園:http://ww

開始 Web jQuery事件冒泡,事件參數對象,鏈式編程原理

eval uri turn 定位 return 也會 否則 ont sele 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web前端之巔 博客

開始 Web AjaxAjax 概述,快速上手

lan 技術分享 php 概述 由於 val asc logs 更新 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web前端之巔 博客園:ht

開始 Web Ajax同步異步請求,數據格式

遊記 document 空閑 name center 20px 實現 resp 也會 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web前端之

開始 Web AjaxjQuery中的Ajax

var 技術分享 else parse cnblogs 我會 clas alt jquer 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web

開始 Web CSS3CSS3概述,選擇器

https 兼容問題 3.1 線性 web前端 不同 錨點 splay lock 大家好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公眾號:Web前端之巔

開始 Web CSS選擇器

大家好,這裡是「 Daotin的夢囈 」從零開始學 Web 系列教程。此文首發於「 Daotin的夢囈 」公眾號,歡迎大家訂閱關注。在這裡我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的專案。現在就讓我們一起進入 W

開始 Web CSS3transform

transform transform 字面上就是變形,改變的意思。在CSS3中transform主要包括以下幾種:移動 translate,縮放scale,旋轉rotate,翻轉skew,改變旋轉軸心等。 1、元素的移動:translate 作用:使用transform實現元素的移動 語法: /*使用t

開始 Web jQuery元素操作,鏈式程式設計,動畫方法

一、使用css操作元素樣式 1、常規寫法 $("#dv").css("width", "200px"); $("#dv").css("height", "100px"); $("#dv").css("background", "red"); 2、鏈式寫法 $("#dv").css("width", "20

開始 Web DOM為元素繫結與解綁事件

大家好,這裡是「 從零開始學 Web 系列教程 」,並在下列地址同步更新...... +------------------------------------------------------------ github:https://github.com/Daotin/Web 微信公眾號:Web前端之