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

你們好,這裏是「 從零開始學 Web 系列教程 」,並在下列地址同步更新......前端

在這裏我會從 Web 前端零基礎開始,一步步學習 Web 相關的知識點,期間也會分享一些好玩的項目。如今就讓咱們一塊兒進入 Web 前端學習的冒險之旅吧!git

1、Promise

Promise是一個對象,表明了將來某個將要發生的事件(,這個事件一般是一個異步操做)es6

有了Promise對象, 能夠將異步操做以同步的流程表達出來, 避免了層層嵌套的回調函數(俗稱'回調地獄')。github

ES6的Promise是一個構造函數, 用來生成promise實例。ajax

一、promise對象3個狀態

  • 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);
      });

2、Symbol

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

上面代碼中,s1s2是兩個 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屬性,指向該對象的默認遍歷器方法。

3、Iterator

如下來自 ECMAScript 6 入門 - 阮一峯

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};
    }
  };
}

一、默認 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的屬性上部署遍歷器生成方法(原型鏈上的對象具備該方法也可)。

二、調用 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);
}

相關文章
相關標籤/搜索