[複習面試資料] es6,es7部分新特性概括整理

一. 簡介

        如今網上不少es6,es7的資料整理以及深刻分析,而這篇文章算是一個比較簡單的概括整理,比較簡單,主要涵蓋一些面試當中最多問道的一些問題,不過若是後期有時間會整理出來一篇更加詳細的,例如數組去重的方法概括大全之類的.歡迎各位大神批評指正.html

二. es6

1. 聲明表達式let與const


1.1 let聲明表達式

let 命令的用法和 var 類似,可是 let 只在所在代碼塊內有效。
基礎用法vue

let a = 1;
    let b = 2;
複製代碼

而且 let 有如下特色:node

  • 不存在變量提高:
    在ES6以前,咱們 var 聲明一個變量一個函數,都會伴隨着變量提高的問題,致使實際開發過程常常出現一些邏輯上的疑惑,按照通常思惟習慣,變量都是須要先聲明後使用。
// var 
console.log(v1); // undefined
var v1 = 2;
// 因爲變量提高 代碼實際以下
var v1;
console.log(v1)
v1 = 2;

// let 
console.log(v2); // ReferenceError
let v2 = 2;
複製代碼
  • 不容許重複聲明:
    letconst相同做用域下,都不能重複聲明同一變量,而且不能在函數內從新聲明參數
  • // 1. 不能重複聲明同一變量
    // 報錯
    function f1 (){
        let a = 1;
        var a = 2;
    }
    // 報錯
    function f2 (){
        let a = 1;
        let a = 2;
    }
    
    // 2. 不能在函數內從新聲明參數
    // 報錯
    function f3 (a1){
        let a1; 
    }
    // 不報錯
    function f4 (a2){
        {
            let a2
        }
    }
    複製代碼

    1.2 const聲明表達式

    const 聲明一個只讀常量
    基礎用法git

    const PI = 3.1415926;
    PI = 3; 
    // TypeError: Assignment to constant variable.
    複製代碼
    • const 聲明時,必須賦值;
    const a ; 
    // SyntaxError: Missing initializer in const declaration.
    複製代碼
    • const 聲明的常量,let 不能重複聲明;
    const PI = 3.1415926;
    let PI = 0;  
    // Uncaught SyntaxError: Identifier 'PI' has already been declared
    複製代碼

    1.3 let和const聲明表達式總結

            1.3.1let 和const都是塊級做用域(var沒有會計做用域),而且都不會有變量提高(var有變量提高)

            1.3.2const聲明的變量都會被認爲是常量,不能被修改,但若是被const修飾的是對象,對象中的屬性值能夠被修改

            1.3.3et在同一個塊中不能定義兩個相同名稱的變量(var能夠定義多個)

    2.解構與賦值


    先講一個普通的例子:es6

    var people = {
        name: 'lux',
        age: 20
    }
    //es5
    var name = people.name
    var age = people.age
    //es6
    const {name, age} = people
    
    //數組一樣適用
    
    var arr = [1,2,3];
    const {one, , three} = arr;
    複製代碼

    2.1 對象的解構賦值

    與數組解構不一樣的是,對象解構不須要嚴格按照順序取值,而只要按照變量名去取對應屬性名的值,若取不到對應屬性名的值,則爲undefined面試

    注意點算法

    • 變量名屬性名不一致,則須要修更名稱。
    let {a:b} = {a:1, c:2}; 
    // error: a is not defined
    // b => 1
    複製代碼

    對象的解構賦值的內部機制,是先找到同名屬性,而後再賦給對應的變量。真正被賦值的是後者,而不是前者。
    上面代碼中,a 是匹配的模式,b纔是變量。真正被賦值的是變量b,而不是模式aexpress

    • 對象解構也支持嵌套解構
    let obj = {
        a:[ 1, { b: 2}]
    };
    let {a, a: [c, {b}]} = obj;
    // a=>[1, {b: 2}], b => 2, c => 1
    複製代碼

    指定解構的默認值json

    let {a=1} = {};        // a => 1
    let {a, b=1} = {a:2};  // a => 2, b => 1
    
    let {a:b=3} = {};      // b => 3
    let {a:b=3} = {a:4};   // b = >4
    // a是模式,b是變量 牢記
    
    let {a=1} = {a:undefined};  // a => 1
    let {a=1} = {a:null};   // a => null
    // 由於null與undefined不嚴格相等,因此賦值有效
    // 致使默認值1不會生效。
    複製代碼

    2.2 字符串的解構賦值

    字符串的解構賦值中,字符串被轉換成了一個相似數組的對象基礎用法數組

    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"
    
    let {length:len} = 'hello';// len => 5
    複製代碼

    2.3 數值和布爾值的解構賦值

    解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉爲對象。因爲undefinednull沒法轉爲對象,因此對它們進行解構賦值,都會報錯。

    let {toString: s} = 123;
    s === Number.prototype.toString // true
    let {toString: s} = true;
    s === Boolean.prototype.toString // true
    
    let { prop: x } = undefined; // TypeError
    let { prop: y } = null;      // TypeError
    複製代碼

    2.4 函數參數的解構賦值

    基礎用法

    function fun ([a, b]){
        return a + b;
    }
    fun ([1, 2]); // 3
    複製代碼

    指定默認值的解構:

    function fun ({a=0, b=0} = {}){
        return [a, b];
    }
    fun ({a:1, b:2}); // [1, 2]
    fun ({a:1});      // [1, 0]
    fun ({});         // [0, 0]
    fun ();           // [0, 0]
    
    function fun ({a, b} = {a:0, b:0}){
        return [a, b];
    }
    fun ({a:1, b:2}); // [1, 2]
    fun ({a:1});      // [1, undefined]
    fun ({});         // [undefined, undefined]
    fun ();           // [0, 0]
    複製代碼

    2.5 應用

    • 交換變量的值:
    let a = 1,b = 2;
    [a, b] = [b, a]; // a =>2 , b => 1 
    複製代碼
    • 函數返回多個值:
    // 返回一個數組
    function f (){
        return [1, 2, 3];
    }
    let [a, b, c] = f(); // a=>1, b=>2, c=>3
    
    // 返回一個對象
    function f (){
        return {a:1, b:2};
    }
    let {a, b} = f();    // a=>1, b=>2
    複製代碼
    • 快速對應參數: 快速的將一組參數與變量名對應。
    function f([a, b, c]) {...}
    f([1, 2, 3]);
    
    function f({a, b, c}) {...}
    f({b:2, c:3, a:1});
    複製代碼
    • 提取JSON數據
    let json = {
        name : 'leo',
        age: 18
    }
    let {name, age} = json;
    console.log(name,age); // leo, 18
    複製代碼
    • 遍歷Map結構:
    const m = new Map();
    m.set('a',1);
    m.set('b',2);
    for (let [k, v] of m){
        console.log(k + ' : ' + v);
    }
    // 獲取鍵名
    for (let [k] of m){...}
    // 獲取鍵值
    for (let [,k] of m){...}
    複製代碼
    • 輸入模塊的指定方法: 用於按需加載模塊中須要用到的方法。
    const {log, sin, cos} = require('math');
    複製代碼

    3.深拷貝與淺拷貝


    • Object.assign() 方法用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。
    const object1 = {
      a: 1,
      b: 2,
      c: 3
    };
     
    const object2 = Object.assign({c: 4, d: 5}, object1);
    console.log(object2);
     
    Object { c: 3, d: 5, a: 1, b: 2 }
     
    // 這是淺拷貝,返回的不是一個新對象,而是把一個或多個源對象添加到目標對象
    複製代碼
    • 結構賦值
    let object1 = {
      a: 1,
      b: 2,
      c: 3
    };
     
    let object2 = {...object1};
    object1.a=11;
     
    console.log(object2);
    Object { a: 11, b: 2, c: 3 }
     
    // 這也是淺拷貝
    
    複製代碼
    • 最簡單的深拷貝(JSON.stringify() 和JSON.parse())
    • 先把對象使用JSON.stringify()轉爲字符串,再賦值給另一個變量,而後使用JSON.parse()轉回來便可。
    let object1 = {
      a: 1,
      b: 2,
      c: 3
    };
     
    let object2 =JSON.parse( JSON.stringify(object1));
    object2.a=11;
     
    console.log(object1,object2);
     
    Object { a: 1, b: 2, c: 3 } Object { a: 11, b: 2, c: 3 }
    複製代碼

    4.Set和Map數據結構


    4.1 Set

    介紹:
    Set數據結構相似數組,但全部成員的值惟一
    Set自己爲一個構造函數,用來生成Set數據結構,使用add方法來添加新成員。

    let a = new Set();
    [1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x));
    for(let k of a){
        console.log(k)
    };
    // 1 2 3 4 5
    複製代碼

    基礎使用

    let a = new Set([1,2,3,3,4]);
    [...a]; // [1,2,3,4]
    a.size; // 4
    
    // 數組去重
    [...new Set([1,2,3,4,4,4])];// [1,2,3,4]
    複製代碼

    注意

    • Set中添加值的時候,不會類型轉換,即5'5'是不一樣的。
    [...new Set([5,'5'])]; // [5, "5"]
    複製代碼

    屬性和方法

    • 屬性:

      • Set.prototype.constructor:構造函數,默認就是Set函數。
      • Set.prototype.size:返回Set實例的成員總數。
    • 操做方法:

      • add(value):添加某個值,返回 Set 結構自己。
      • delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
      • has(value):返回一個布爾值,表示該值是否爲Set的成員。
      • clear():清除全部成員,沒有返回值。
    let a = new Set();
    a.add(1).add(2); // a => Set(2) {1, 2}
    a.has(2);        // true
    a.has(3);        // false
    a.delete(2);     // true  a => Set(1) {1}
    a.clear();       // a => Set(0) {}
    
    複製代碼

    數組去重

    let a = new Set([1,2,3,3,3,3]);
    
    複製代碼

    4.2 Map

    因爲傳統的JavaScript對象只能用字符串當作鍵,給開發帶來很大限制,ES6增長Map數據結構,使得各類類型的值(包括對象)均可以做爲鍵。
    Map結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。 基礎使用

    let a = new Map();
    let b = {name: 'leo' };
    a.set(b,'my name'); // 添加值
    a.get(b);           // 獲取值
    a.size;      // 獲取總數
    a.has(b);    // 查詢是否存在
    a.delete(b); // 刪除一個值
    a.clear();   // 清空全部成員 無返回
    
    複製代碼

    注意

    • 傳入數組做爲參數,指定鍵值對的數組
    let a = new Map([
        ['name','leo'],
        ['age',18]
    ])
    複製代碼
    • 若是對同一個鍵屢次賦值,後面的值將覆蓋前面的值
    let a = new Map();
    a.set(1,'aaa').set(1,'bbb');
    a.get(1); // 'bbb'
    複製代碼
    • 若是讀取一個未知的鍵,則返回undefined
    new Map().get('abcdef'); // undefined
    複製代碼
    • 一樣的值的兩個實例,在 Map 結構中被視爲兩個鍵。
    let a = new Map();
    let a1 = ['aaa'];
    let a2 = ['aaa'];
    a.set(a1,111).set(a2,222);
    a.get(a1); // 111
    a.get(a2); // 222
    複製代碼

    遍歷方法: Map 的遍歷順序就是插入順序。

    • keys():返回鍵名的遍歷器。
    • values():返回鍵值的遍歷器。
    • entries():返回全部成員的遍歷器。
    • forEach():遍歷 Map 的全部成員。
    let a = new Map([
        ['name','leo'],
        ['age',18]
    ])
    
    let a1 = [...a.keys()];   // a1 => ["name", "age"]
    let a2 = [...a.values()]; // a2 =>  ["leo", 18]
    let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]]
    
    複製代碼

    4.3 Set進行數組去重

    經過上面的講述應該明白了Set的用法,那麼話很少說直接上代碼

    let a=[1,2,3,4,5,6,1,2,3,2,1,2]
          let b=new Set()
          let c=[];
          a.forEach(function (value,key,arr){
            b.add(value)
          })
          b.forEach(function (value,key,set) {
            c.push(value)
          })
          console.log(c)
          // [1,2,3,4,5,6]
    複製代碼

    5.Promise,async/await


    5.1 Promise

    5.1.1 Promise 是什麼

    Promise 是一種規範,在es6之前,瀏覽器沒有內置的promise,不一樣框架有各自的promise實現,本文中的Promise以es6中的Promise爲例。

    一個Promise封裝了一個操做(異步或同步)的結果和其狀態,按個人理解來看,它的出現有如下幾點緣由,一方面是爲了解決回調函數的嵌套問題,若是沒有promise 咱們能夠想象一下,應該如何控制流程,假設有如下場景,執行操做A,若是失敗了,執行 操做B,執行操做C,同時B 和 C 失敗和成功了也會有對應的處理,這裏爲了簡化,統一使用操做D來處理最終結果,那麼最後可能會寫出以下的代碼

    function stepA(arg, onSuccess, onFailure) {
       var output = dosomethingA(arg);
       var res = output.res;
       if (output.success) {
           onSuccess(res);
       } else {
           onFaliure(res);
       }
    }
    
    function stepB(arg, onSuccess, onFailure) {
       var output = dosomethingB(arg);
       var res = output.res;
       if (output.success) {
           onSuccess(res);
       } else {
           onFaliure(res);
       }
    }
    
    function stepC(arg, onSuccess, onFailure) {
       var output = dosomethingC(arg);
       var res = output.res;
       if (output.success) {
           onSuccess(res);
       } else {
           onFaliure(res);
       }
    }
    
    function stepD(arg) {
       dosomethingD(arg);
    }
    
    stepA('test', function(res) {
       stepB(res, stepD, stepD);
    }, function(res) {
       stepC(res, stepD, ste
    複製代碼

    這裏若是流程鏈更長或者將其中的幾個step換成是匿名函數的話,就會出現更多的回調嵌套,同時代碼會很是的長,根本沒法閱讀。 還有一個問題是,人們習慣於用線性的思想去思考爲題,promise這種鏈式調用的模式能夠將異步代碼看起來像同步代碼同樣執行。

    5.1.2 Promsie 的用法

    仍是剛剛的那個例子,若是用Promise該怎麼改寫

    function executeStep(arg, dosomething) {
        return new Promise(function(resolve, reject) {
            var result = dosomething(arg);
            var res = result.res;
            if (result.success) {
                resolve(res);
            } else {
                reject(res);
            }
        });
    }
    
    function dosomethingA(arg) {
        // ...
        return result;
    }
    
    function dosomethingB(arg) {
        // ...
        return result;
    }
    
    function dosomethingC(arg) {
        // ...
        return result;
    }
    
    function dosomethingD(arg) {
        // ...
        console.log(arg);
    }
    
    function wrap(dosomething, result) {
        return executeStep(result, dosomething);
    }
    
    wrap(dosomethingA, 'test').then(function(result) {
        return wrap(dosomethingB, result);
    }, function(reason) {
        return wrap(dosomethingC, reason);
    }).then(dosomethingD, dosomethingD);
    複製代碼

    根據PromiseA+規範,Promise有3種狀態,分別是pending,rejected和fullfilled,其中rejected 和fullfilled又稱爲settled,promise可由pending轉到settled,處於settled狀態的promise狀態不可再變化。

    5.1.3 Promise經常使用API

    Promise.prototype.constructor

    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            var res = Math.random();
            if (res < 0.5) {
                resolve(res);
            } else {
                reject(res);
            }
        });
    });
    複製代碼

    其內部實現大致以下

    function MyPromise(resolver){
            if(typeof resolver !== 'function'){
                throw new TypeError(resolver + ' is not a function');
            }
            if(!(this instanceof MyPromise) return new MyPromise(resolver);
    
            var self = this;
    
            self.status_ = 'PENDING';
            self.data_ = undefined;
            self.resolveCallBacks = [];
            self.rejectCallBacks = [];
    
            function resolve(value){
                setTimeout(function(){
                    if(self.status_ === 'PENDING'){
                        self.status_ = 'RESOLVED';
                        self.data_ = value;
                        for(var i=0; i<self.rejectCallBacks.length; i++){
                            self.rejectCallBacks[i](value);
                        }
                    }
                });
            }
    
            function reject(reason){
                setTimeout(function(){
                    if(self.status_ === 'PENDING'){
                        self.status_ = 'REJECTED';
                        self.data_ = reason;
                        for(var i=0; i<self.resolveCallBacks.length; i++){
                            self.resolveCallBacks[i](value);
                        }
                    }
                });
            }
    
            try{
                resolver(resolve, reject);
            }catch(reason){
                reject(reason);
            }
        }
    複製代碼

    當經過構造函數建立一個Promise的時候,首先會將其狀態設置爲PENDING,同時初始化resolve和reject的回調數組,在這裏,resolvereject都是異步調用的,只有噹噹前Promise狀態爲PENDING時,rejectresolve內部的代碼纔會執行, 更改Promise的狀態,將結果設置爲Promise的值,同時遍歷對應的回調數組並執行。
    Promise.prototype.then(onFulfilled, onRejected)
    這個是Promise中使用頻率最高的一個方法了,它會更具Promise的狀態選擇執行回調或將回掉添加到Promise的回調數組上,大體實現以下

    MyPromise.prototype.then = function(onResolved, onRejected) {
            onResolved = typeof onResolved === 'function' ? onResolved : function(value){return value};
            onRejected = typeof onRejected === 'function' ? onRejected : function(reason){throw reason};
    
            var self = this;
            var newPromise;
            if(self.status_ === 'RESOLVED'){
                newPromise = new MyPromise(function(resolve, reject){
                    setTimeout(function(){
                        try{
                            var x = onResolved(self.data_);
                            resolvePromise(newPromise, x, resolve, reject);
                        }catch(reason){
                            reject(reason);
                        }
                    });
                });
            }else if(self.status_ === 'REJECTED'){
                newPromise = new MyPromise(function(resolve, reject){
                    setTimeout(function(){
                        try{
                            var x = onRejected(self.data_);
                            resolvePromise(newPromise, x, resolve, reject);
                        }catch(reason){
                            reject(reason);
                        }
                    });
                });
            }else{
                newPromise = new MyPromise(function(resolve, reject){
                    self.resolveCallBacks.push(function(){
                        try{
                            var x = onResolved(self.data_);
                            resolvePromise(newPromise, x, resolve, reject);
                        }catch(reason){
                            rejcet(reason);
                        }
                    });
                    self.rejectCallBacks.push(function(){
                        try{
                            var x = onRejected(self.data_);
                            resolvePromise(newPromise, x, resolve, reject);
                        }catch(reason){
                            reject(reason);
                        }
                    });
                });
            }
            return newPromise;
        };
    複製代碼

    首先會對傳入的回調函數進行檢測,若是不是function的話就會使用默認的function(這裏的resolvePromise將會在下文講到),以後判斷當前Promise的狀態,若是Promise已經決議,則將當前Promise的值傳給對應的回調,同時執行resolvePromise,這些操做會在新建立的Promise中經過異步的方式進行執行,若是當前Promise處於PENDING狀態,則會將回調添加到當前Promise的回調數組中。下面看一下resolvePromise

    function resolvePromise(mPromise, x, resolve, reject){
            var then;
            var thenCalledOrThrow = false;
            if(mPromise === x){
                return reject(new TypeError('Circle is not expected to exist'));
            } // Promises/A+ 2.3.1
    
            if(x instanceof MyPromise){ // 2.3.2
                if(x.status_ === 'PENDING'){
                    x.then(function(value){
                        resolvePromise(mPromise, value, resolve, reject)
                    }, reject);
                }else{
                    x.then(resolve, reject);
                }
                return;
            }
    
            if((x != null) && ((typeof x === 'object') || (typeof x === 'function'))){ // 2.3.3
                try{
                    then = x.then; // Maybe then is a getter 2.3.3.1
                    if(typeof then === 'function'){ // 2.3.3.3
                        then.call(x, function rs(y){
                            if(thenCalledOrThrow) return;
                            thenCalledOrThrow = true;
                            return resolvePromise(mPromise, y, resolve, reject); // 2.3.3.3.1
                        }, function rj(r){
                            if(thenCalledOrThrow) return;
                            thenCalledOrThrow = true;
                            return reject(r); // 2.3.3.3.2
                        });
                    }else{
                        return resolve(x); // 2.3.3.4 
                    }
                }catch(reason){ // 2.3.3.2
                    if(thenCalledOrThrow) return;
                    thenCalledOrThrow = true;
                    return reject(reason);
                }
            }else{
                return resolve(x); // 2.3.4
            }
        }
    複製代碼

    這裏註釋中的標記表明上文提到的PromiseA+規範中的條目
    Promise.prototype.catch(onRejected)
    至關於then(null, onRejected), 注意的是在Promise裏,發生的異常不會輸出到控制檯,而會被存爲Promise的值

    5.2 async/await

    這部份內容原本是要放在es7下講的可是Promise和async/await結合起來會解決回調地獄的問題因此es7那塊就放上文章連接,這邊稍微講下async/await就好.

    在async/await以前,咱們有三種方式寫異步代碼

    1. 嵌套回調

    2. 以Promise爲主的鏈式回調

    3. 使用Generators

    可是,這三種寫起來都不夠優雅,ES7作了優化改進,async/await應運而生,async/await相比較Promise 對象then 函數的嵌套,與Generator 執行的繁瑣(須要藉助co才能自動執行,不然得手動調用next() ), Async/Await 可讓你輕鬆寫出同步風格的代碼同時又擁有異步機制,更加簡潔,邏輯更加清晰。

    5.2.1 async/await特色

    1. async/await更加語義化,async 是「異步」的簡寫,async function 用於申明一個function 是異步的; await,能夠認爲是async wait的簡寫, 用於等待一個異步方法執行完成;

    2. async/await是一個用同步思惟解決異步問題的方案(等結果出來以後,代碼纔會繼續往下執行)

    3. 能夠經過多層async function 的同步寫法代替傳統的callback嵌套

    5.2.2 async function語法

    • 自動將常規函數轉換成Promise,返回值也是一個Promise對象

    • 只有async函數內部的異步操做執行完,纔會執行then方法指定的回調函數

    • 異步函數內部可使用await

    async function name([param[, param[, ... param]]]) { statements }
    name: 函數名稱。
    param:  要傳遞給函數的參數的名稱
    statements: 函數體語句。
    返回值: 返回的Promise對象會以async function的返回值進行解析,或者以該函數拋出的異常進行回絕。
    複製代碼

    5.2.3 await語法

    • await 放置在Promise調用以前,await 強制後面點代碼等待,直到Promise對象resolve,獲得resolve的值做爲await表達式的運算結果

    • await只能在async函數內部使用,用在普通函數裏就會報錯

    [return_value] = await expression;
    
    expression:  一個 Promise  對象或者任何要等待的值。
    
    返回值:返回 Promise 對象的處理結果。若是等待的不是 Promise 對象,則返回該值自己。
    複製代碼

    5.2.4 錯誤處理

    在async函數裏,不管是Promise reject的數據仍是邏輯報錯,都會被默默吞掉,因此最好把await放入try{}catch{}中,catch可以捕捉到Promise對象rejected的數據或者拋出的異常

    function timeout(ms) {
    
      return new Promise((resolve, reject) => {
    
        setTimeout(() => {reject('error')}, ms);  //reject模擬出錯,返回error
    
      });
    
    }
    
    async function asyncPrint(ms) {
    
      try {
    
         console.log('start');
    
         await timeout(ms);  //這裏返回了錯誤
    
         console.log('end');  //因此這句代碼不會被執行了
    
      } catch(err) {
    
         console.log(err); //這裏捕捉到錯誤error
    
      }
    
    }
    
    asyncPrint(1000);
    複製代碼

    若是不用try/catch的話,也能夠像下面這樣處理錯誤(由於async函數執行後返回一個promise)

    function timeout(ms) {
    
      return new Promise((resolve, reject) => {
    
        setTimeout(() => {reject('error')}, ms);  //reject模擬出錯,返回error
    
      });
    
    }
    
    async function asyncPrint(ms) {
    
      console.log('start');
    
      await timeout(ms)
    
      console.log('end');  //這句代碼不會被執行了
    
    }
    
    asyncPrint(1000).catch(err => {
    
        console.log(err); // 從這裏捕捉到錯誤
    
    });
    
    複製代碼

    若是你不想讓錯誤中斷後面代碼的執行,能夠提早截留住錯誤,像下面

    function timeout(ms) {
    
      return new Promise((resolve, reject) => {
    
        setTimeout(() => {
    
            reject('error')
    
        }, ms);  //reject模擬出錯,返回error
    
      });
    
    }
    
    async function asyncPrint(ms) {
    
      console.log('start');
    
      await timeout(ms).catch(err => {  // 注意要用catch
    
    console.log(err) 
    
      })
    
      console.log('end');  //這句代碼會被執行
    
    }
    
    asyncPrint(1000);
    複製代碼

    5.2.5 使用場景

    多個await命令的異步操做,若是不存在依賴關係(後面的await不依賴前一個await返回的結果),用Promise.all()讓它們同時觸發

    function test1 () {
        return new Promise((resolve, reject) => {
    
            setTimeout(() => {
    
                resolve(1)
    
            }, 1000)
    
        })
    
    }
    
    function test2 () {
    
        return new Promise((resolve, reject) => {
    
            setTimeout(() => {
    
                resolve(2)
    
            }, 2000)
    
        })
    
    }
    
    async function exc1 () {
    
        console.log('exc1 start:',Date.now())
    
        let res1 = await test1();
    
        let res2 = await test2(); // 不依賴 res1 的值
    
        console.log('exc1 end:', Date.now())
    
    }
    
    async function exc2 () {
    
        console.log('exc2 start:',Date.now())
    
        let [res1, res2] = await Promise.all([test1(), test2()])
    
        console.log('exc2 end:', Date.now())
    
    }
    
    exc1();
    
    exc2();
    
    複製代碼

    exc1 的兩個並列await的寫法,比較耗時,只有test1執行完了纔會執行test2

    你能夠在瀏覽器的Console裏嘗試一下,會發現exc2的用Promise.all執行更快一些

    6.箭頭函數


    6.1 箭頭函數基本形式

    let func = (num) => num;
    let func = () => num;
    let sum = (num1,num2) => num1 + num2;
    [1,2,3].map(x => x * x);
    複製代碼

    6.2 箭頭函數基本特色

    (1). 箭頭函數this爲父做用域的this,不是調用時的this

    箭頭函數的this永遠指向其父做用域,任何方法都改變不了,包括call,apply,bind。
    普通函數的this指向調用它的那個對象。

    let person = {
        name:'jike',
        init:function(){
            //爲body添加一個點擊事件,看看這個點擊後的this屬性有什麼不一樣
            document.body.onclick = ()=>{
                alert(this.name);//?? this在瀏覽器默認是調用時的對象,可變的?                  
            }
        }
    }
    person.init();
    複製代碼

    上例中,init是function,以person.init調用,其內部this就是person自己,而onclick回調是箭頭函數,
    其內部的this,就是父做用域的this,就是person,能獲得name。

    let person = {
        name:'jike',
        init:()=>{
            //爲body添加一個點擊事件,看看這個點擊後的this屬性有什麼不一樣
            document.body.onclick = ()=>{
                alert(this.name);//?? this在瀏覽器默認是調用時的對象,可變的?                  
            }
        }
    }
    person.init();
    複製代碼

    上例中,init爲箭頭函數,其內部的this爲全局window,onclick的this也就是init函數的this,也是window,
    獲得的this.name就爲undefined。

    (2). 箭頭函數不能做爲構造函數,不能使用new

    //構造函數以下:
    function Person(p){
        this.name = p.name;
    }
    //若是用箭頭函數做爲構造函數,則以下
    var Person = (p) => {
        this.name = p.name;
    }
    複製代碼

    因爲this必須是對象實例,而箭頭函數是沒有實例的,此處的this指向別處,不能產生person實例,自相矛盾。

    (3). 箭頭函數沒有arguments,caller,callee

    箭頭函數自己沒有arguments,若是箭頭函數在一個function內部,它會將外部函數的arguments拿過來使用。
    箭頭函數中要想接收不定參數,應該使用rest參數...解決。

    let B = (b)=>{
      console.log(arguments);
    }
    B(2,92,32,32);   // Uncaught ReferenceError: arguments is not defined
    
    let C = (...c) => {
      console.log(c);
    }
    C(3,82,32,11323);  // [3, 82, 32, 11323]
    複製代碼

    (4). 箭頭函數經過call和apply調用,不會改變this指向,只會傳入參數

    let obj2 = {
        a: 10,
        b: function(n) {
            let f = (n) => n + this.a;
            return f(n);
        },
        c: function(n) {
            let f = (n) => n + this.a;
            let m = {
                a: 20
            };
            return f.call(m,n);
        }
    };
    console.log(obj2.b(1));  // 11
    console.log(obj2.c(1)); // 11
    複製代碼

    (5). 箭頭函數沒有原型屬性

    var a = ()=>{
      return 1;
    }
    
    function b(){
      return 2;
    }
    
    console.log(a.prototype);  // undefined
    console.log(b.prototype);   // {constructor: ƒ}
    複製代碼

    (6). 箭頭函數不能做爲Generator函數,不能使用yield關鍵字

    (7). 箭頭函數返回對象時,要加一個小括號

    var func = () => ({ foo: 1 }); //正確
    var func = () => { foo: 1 };   //錯誤
    複製代碼

    (8). 箭頭函數在ES6 class中聲明的方法爲實例方法,不是原型方法

    //deom1
    class Super{
        sayName(){
            //do some thing here
        }
    }
    //經過Super.prototype能夠訪問到sayName方法,這種形式定義的方法,都是定義在prototype上
    var a = new Super()
    var b = new Super()
    a.sayName === b.sayName //true
    //全部實例化以後的對象共享prototypy上的sayName方法
    
    
    //demo2
    class Super{
        sayName =()=>{
            //do some thing here
        }
    }
    //經過Super.prototype訪問不到sayName方法,該方法沒有定義在prototype上
    var a = new Super()
    var b = new Super()
    a.sayName === b.sayName //false
    //實例化以後的對象各自擁有本身的sayName方法,比demo1須要更多的內存空間
    複製代碼

    所以,在class中儘可能少用箭頭函數聲明方法。

    (9). 多重箭頭函數就是一個高階函數,至關於內嵌函數

    const add = x => y => y + x;
    //至關於
    function add(x){
      return function(y){
        return y + x;
      };
    }
    複製代碼

    (10). 箭頭函數常見錯誤

    let a = {
      foo: 1,
      bar: () => console.log(this.foo)
    }
    
    a.bar()  //undefined
    複製代碼

    bar函數中的this指向父做用域,而a對象沒有做用域,所以this不是a,打印結果爲undefined

    function A() {
      this.foo = 1
    }
    
    A.prototype.bar = () => console.log(this.foo)
    
    let a = new A()
    a.bar()  //undefined
    複製代碼

    原型上使用箭頭函數,this指向是其父做用域,並非對象a,所以得不到預期結果

    7.for ...of


    在談for...of前;咱們來比較for和for...in的弊端;

    1:其中for 循環的最大缺點是須要跟蹤計數器和退出條件。老生常談就很少說了。

    2:for...in  它消除了跟蹤技術器和退出條件,可是依然須要使用 index 來訪問數組的值,讓人感受有點不人性化,此外,當你須要向數組中添加額外的方法(或另外一個對象)時,for...in 循環會帶來很大的麻煩。由於 for...in 循環循環訪問全部可枚舉的屬性,意味着若是向數組的原型中添加任何其餘屬性,這些屬性也會出如今循環中.

    Array.prototype.decimalfy = function() {
      for (let i = 0; i < this.length; i++) {
        this[i] = this[i].toFixed(2);
      }
    };
     
    const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
     
    for (const index in digits) {
      console.log(digits[index]);
    }
    複製代碼
    Prints:
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function() {
     for (let i = 0; i < this.length; i++) {
      this[i] = this[i].toFixed(2);
     }
    }
    複製代碼

    7.1 For...of 循環

    for...of 循環用於循環訪問任何可迭代的數據類型。

    for...of 循環的編寫方式和 for...in 循環的基本同樣,只是將 in 替換爲 of,能夠忽略索引。簡潔方便;

    const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
     
    for (const digit of digits) {
      console.log(digit);
    }
    複製代碼

    7.import和export


    ES6以前已經出現了js模塊加載的方案,最主要的是CommonJS和AMD規範。commonjs主要應用於服務器,實現同步加載,如nodejs。AMD規範應用於瀏覽器,如requirejs,爲異步加載。同時還有CMD規範,爲同步加載方案如seaJS。

    ES6在語言規格的層面上,實現了模塊功能,並且實現得至關簡單,徹底能夠取代現有的CommonJS和AMD規範,成爲瀏覽器和服務器通用的模塊解決方案。

    ES6模塊主要有兩個功能:export和import

    export用於對外輸出本模塊(一個文件能夠理解爲一個模塊)變量的接口

    import用於在一個模塊中加載另外一個含有export接口的模塊。

    也就是說使用export命令定義了模塊的對外接口之後,其餘JS文件就能夠經過import命令加載這個模塊(文件)。以下圖(假設a和b文件在同一目錄下)

    // a.js
     
    var sex="boy";
    var echo=function(value){
    &emsp;&emsp;console.log(value)
    }
    export {sex,echo}  
    //經過向大括號中添加sex,echo變量而且export輸出,就能夠將對應變量值以sex、echo變量標識符形式暴露給其餘文件而被讀取到
    //不能寫成export sex這樣的方式,若是這樣就至關於export "boy",外部文件就獲取不到該文件的內部變量sex的值,由於沒有對外輸出變量接口,只是輸出的字符串。
    複製代碼
    // b.js
    經過import獲取a.js文件的內部變量,{}括號內的變量來自於a.js文件export出的變量標識符。
    import {sex,echo} from "./a.js" 
    console.log(sex)   // boy
    echo(sex) // boy
    複製代碼

    a.js文件也能夠按以下export語法寫,但不如上邊直觀,不太推薦。

    // a.js
    export var sex="boy";
    export var echo=function(value){
    &emsp;&emsp;console.log(value)
    }
     
    //由於function echo(){}等價於 var echo=function(){}因此也能夠寫成
    export function echo(value){
    &emsp;&emsp;&emsp;console.log(value)
    }
    複製代碼

    以上是export與module的基本用法,再進行拓展學習

    前面的例子能夠看出,b.js使用import命令的時候,用戶須要知道a.js所暴露出的變量標識符,不然沒法加載。可使用export default命令,爲模塊指定默認輸出,這樣就不須要知道所要加載模塊的變量名。

    //a.js
    var sex="boy";
    export default sex(sex不能加大括號)
    //本來直接export sex外部是沒法識別的,加上default就能夠了.可是一個文件內最多隻能有一個export default。
    其實此處至關於爲sex變量值"boy"起了一個系統默認的變量名default,天然default只能有一個值,因此一個文件內不能有多個export default。
    複製代碼
    // b.js
    本質上,a.js文件的export default輸出一個叫作default的變量,而後系統容許你爲它取任意名字。因此能夠爲import的模塊起任何變量名,且不須要用大括號包含
    import any from "./a.js"
    import any12 from "./a.js" 
    console.log(any,any12)   // boy,boy
    複製代碼

    8. Math對象的拓展


    Math.trunc() - 取整,去掉一個數的小數部分

    console.log(Math.trunc(3.5)); // 3
        console.log(Math.trunc(-3.5)); // -3
        // 對於非數值,Math.trunc() 內部使用Number()方法先將其轉化爲數值
        console.log(Math.trunc('123.456')); //123
        console.log(Math.trunc(NaN)); // NaN
        console.log(Math.trunc('abc')); // NaN
        console.log(Math.trunc()); // NaN
    複製代碼

    Math.sign()

    // Math.sign() // 用來判斷一個數是正數仍是負數 0,對於非數值,先轉化,後判斷
        /*
        * 參數爲正數 返回+1
        * 參數爲負數,返回-1
        * 參數爲0,返回0
        * 參數爲-0,返回-0
        * 參數爲NaN,返回NaN
        * */
    複製代碼

    Math.cbrt()

    // 計算立方根
        console.log(Math.cbrt(-8)); // -2
    複製代碼

    Math.hypot()

    // 返回全部參數的平方和的平凡根
        console.log(Math.hypot(3,4,5)); // 7.0710678118654755
    複製代碼

    三. es7

    1. includes()方法


    Array.prototype.includes方法返回一個布爾值,表示某個數組是否包含給定的值,與字符串的includes方法相似。

    [1, 2, 3].includes(2)  // true
    [1, 2, 3].includes(4) // true
    [1, 2, NaN].includes(NaN) // true
    複製代碼

    該方法的第二個參數表示搜索的起始位置,默認爲0。若是第二個參數爲負數,則表示倒數的位置,若是這時它大於數組長度(好比第二個參數爲-4, 但數組長度爲3),則會重置爲0開始。

    [1, 2, 3].includes(3, 3); // false
    [1, 2, 3].includes(3, -1); // true
    複製代碼

    沒有該方法以前,咱們一般使用數組的indexOf方法,檢查是否包含某個值。

    if (arr.indexOf(el) !== -1) {
    	// ...
    }
    複製代碼

    indexOf方法有兩個缺點,一是不夠語義化,它的含義是找到參數值的第一個出現位置,因此要去比較是否不等於-1,表達起來不夠直觀。二是,它內部使用嚴格相等運算符進行判斷,這會致使對NaN的誤判。

    [NaN].indexOf(NaN) // -1
    複製代碼

    includes使用的是不同的判斷算法,就沒有這個問題。

    NaN].includes(NaN) // true
    複製代碼

    下面代碼用來檢查當前環境是否支持該方法,若是不支持,部署一個簡易的替代版本。

    const contains = (() => Array.prototype.includes 
    ? (arr, value) => arr.includes(value)
    :(arr, value) => arr.some(el => el === value) 
    )()
    複製代碼

    另外,Map和Set數據結構有一個has方法須要注意與includes區分。

    -Map結構的has方法,是用來查找鍵名的,好比Map.prototype.has(key), WeakMap.prototype.has(key), Reflect.has(target, propertyKey)

    -Set結構的has方法,是用來查找值的,好比Set.prototype.has(value), WeakSet.prototype.has(value)

    2. async\await


    這裏貼一下解決地獄回調方法的連接.

    Promise,async/await解決回調地獄: www.cnblogs.com/fanghl/p/94…

    2. 求冪運算符


    基本用法:

    let a = 3 ** 2 ; // 9
    // 等效於
    Math.pow(3, 2);  // 9
    複製代碼

    **是一個運算符,也能夠知足相似假髮的操做,以下:

    let a = 3;
    a **= 2;    // 9
    複製代碼

    四.參考文章

    【複習資料】ES6/ES7/ES8/ES9資料整理(我的整理): juejin.im/post/5c02b1…

    ES六、ES7特性回顧(爲本身以前的周馬觀花買單,面試有問哦): blog.csdn.net/zww19847743…

    ES六、ES7新特性: my.oschina.net/kimyeongnam…

    es6 對象深拷貝和淺拷貝: blog.csdn.net/Dong8508/ar…

    ES6 Set進行數組去重: blog.csdn.net/qq_33350717…

    Promise 簡介和使用: www.jianshu.com/p/113d426e8…

    淺談async/await: www.jianshu.com/p/1e75bd387…

    ES6箭頭函數總結: www.cnblogs.com/mengff/p/96…

    ES6:循環 for ...of..: blog.csdn.net/daikunfa/ar…

    ES6模塊的import和export用法總結: blog.csdn.net/qq_20069429…

    ES6 之 Math對象的擴展: www.cnblogs.com/houfee/p/10…

    Promise,async/await解決回調地獄: www.cnblogs.com/fanghl/p/94…

    五.結語

    雖然這篇文章,大部分都是借鑑別人的文章內容,可是大部分都是我在看過以後以爲不錯的才放到上面講的,雖然乾貨比較少,本身總結的東西比較少吧,可是你們仍是能夠看看其推薦的文章,這樣應該能理解更深入,在這裏仍是請各位大大在評論區批評指正,最後大概下次會寫與vue相關的面試題之類的,盡請期待.

    相關文章
    相關標籤/搜索