ECMAScript 6學習筆記

由于受新型冠狀病毒的影響墙歪,假期又延長了,又不能出門贝奇,只好在家認真學習了虹菲,就借此機會閱讀完阮一峰老師的ECMAScript 6入門教程,順便將一些es6中常用的特性進行一個整理掉瞳。相當于讀書時代的劃重點

1:let和const

用來聲明變量毕源,用法類似于var,但所聲明的變量只能在變量所在的代碼塊內(nèi)有效陕习。
相同點:
都是用于塊級作用域霎褐,不存在變量提升,一定要在聲明后才可以使用该镣,在聲明前使用會報錯冻璃。
不同點:
let定義的變量可以修改值。const定義的變量為常量,賦值后不能在修改省艳。

2:變量解構(gòu)賦值

解構(gòu)是指按照一定模式暇咆,從數(shù)組和對象中提取值星著,對變量進行賦值傻丝。

1.數(shù)組解構(gòu)

let [a, b, c] = [1, 2, 3];
相當于

let a = 1;
let b = 2;
let c = 3;

2.對象解構(gòu)

let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined

3.解構(gòu)用途

交換變量的值

let x = 1;
let y = 2;
[x, y] = [y, x];

從函數(shù)返回多個值

// 返回一個數(shù)組
function example() {
  return [1, 2, 3];}
let [a, b, c] = example();
// 返回一個對象
function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

提取JSON數(shù)據(jù)

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]

輸入模塊的指定方法

const { SourceMapConsumer, SourceNode } = require("source-map");

3.字符串擴展

1.使用for...of遍歷字符串茵肃。

      var str = 'abcd';
        for (let value of str){
            console.log(value);
        }

2.模板字符串

模板字符串是增強版的字符串,使用`來表示字符串的使用辐烂,也可以用來定義多行字符串遏插,或者在字符串中使用變量。

//普通字符串
        let a = `this is str`;
        console.log(a);
        //多行字符串
        let glsl = `void main(){
            var a;
        }`
        console.log(glsl);
        //包含變量的字符串
        let x = 1,y = 2;
        let b = `x:${x}\n y:${y}`
        console.log(b);

3.includes(), startsWith(), endsWith()

之前判斷一個字符串中是否包含某個字符串時纠修,使用的是indexOf方法涩堤,在es6中新增了三個方法進行判斷字符串中是否包含指定的字符。

includes():判斷字符串中是否包含某個字符串分瘾。
startsWith():判斷字符串是否以某個字符串開頭。
endsWith():判斷字符串是否以某個字符串結(jié)尾吁系。

        var string = `這是一個字符串德召。`
        console.log(string.includes('字'));//true
        console.log(string.startsWith('這'));//true
        console.log(string.endsWith('。'));//true

4.repeat(n)

表示將原字符串復制n次汽纤,返回一個新的字符串上岗。

console.log(string.repeat(3));
//這是一個字符串。這是一個字符串蕴坪。這是一個字符串肴掷。

5.padStart(),padEnd()

根據(jù)指定的參數(shù)在開頭或者結(jié)尾補全字符串背传。如:

console.log('x'.padStart(5, 'ab')) // 'ababx'
console.log('x'.padStart(4, 'ab')) // 'abax'
console.log('x'.padEnd(5, 'ab')) // 'xabab'
console.log('x'.padEnd(4, 'ab')) // 'xaba'

6.trimStart()呆瞻,trimEnd()

trim()用于消除前后的空格,trimStart()用于消除字符串開頭的空格径玖。trimEnd()用于消除字符串結(jié)尾的空格痴脾。

4.數(shù)值的擴展

1.Number.isFinite(), Number.isNaN()

isFinite用于判斷一個數(shù)值是否是有限的(即不是Infinity)。如果參數(shù)類型不是數(shù)值梳星,Number.isFinite一律返回false赞赖。

Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false

Number.isNaN()用來檢查一個值是否為NaN。如果參數(shù)類型不是NaN冤灾,Number.isNaN一律返回false前域。

Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(9/NaN) // true
Number.isNaN('true' / 0) // true
Number.isNaN('true' / 'true') // true

與全局的isFinite()isNaN()有什么區(qū)別?
全局的isFinite()isNaN()會先調(diào)用Number()將非數(shù)值轉(zhuǎn)成數(shù)值韵吨。而這兩個新方法只對數(shù)值有效匿垄,Number.isFinite()對于非數(shù)值一律返回false, Number.isNaN()只有對于NaN才返回true,非NaN一律返回false
1.Number.isInteger()
使用Number.isInteger()可以判斷變量是否為整數(shù)類型年堆。

console.log(Number.isInteger(12.5));//false
console.log(Number.isInteger(12));//true

2.Math.trunc()

Math.trunc()去除小數(shù)部分吞杭,返回整數(shù)部分。

console.log(Math.trunc(12.33));//12

5.函數(shù)的擴展

1.函數(shù)參數(shù)的默認值

在定義函數(shù)的時候变丧,可以在函數(shù)中使用默認值芽狗,當調(diào)用函數(shù)時,傳遞的變量值為空時痒蓬,如果有默認值童擎,使用變量的時候?qū)⑹褂媚J值,通常情況下攻晒,定義了默認值的參數(shù)顾复,應該是函數(shù)的尾參數(shù)。因為這樣比較容易看出來鲁捏,到底省略了哪些參數(shù)芯砸。如果非尾部的參數(shù)設(shè)置默認值,實際上這個參數(shù)是沒法省略的给梅。如下所示:

  function init(a,b,c = 10){
            console.log(a);//12
            console.log(b);//1
            console.log(c);//10
        }
        init(12,1);

2.箭頭函數(shù)

ES6 允許使用“箭頭”(=>)定義函數(shù)假丧。如下兩個函數(shù)是一樣的。

var fun = function(a,b){
            console.log(a+b);
        }
        var fun1 = (a,b) => {
            console.log(a+b);
        }
        fun(1,2);
        fun1(1,2);

箭頭函數(shù)注意點

  • 函數(shù)體內(nèi)的this對象动羽,是定義時所在的對象包帚,不是使用是所在的對象。
  • 不可當作構(gòu)造函數(shù)來使用运吓,也就是不能使用new來創(chuàng)建對象渴邦。
  • 不可使用arguments對象,如果要使用類似的功能拘哨,可以使用 rest
  • 不可以使用yield命令谋梭,因此箭頭函數(shù)不能用作 Generator 函數(shù)。
    關(guān)于第一點函數(shù)體內(nèi)的this對象倦青,是定義時所在的對象章蚣,示例如下所示:
      function foo() {
            setTimeout(() => {
                console.log('id:', this.id);//42
            }, 100);
            setTimeout(function(){
                console.log('id:', this.id);//21
            },200)
        }
        var id = 21;
        foo.call({ id: 42 });

不適用場合

  • 第一個場合是定義對象的方法,且該方法內(nèi)部包括this姨夹。
  • 第二個場合是需要動態(tài)this的時候纤垂,也不應使用箭頭函數(shù)。

3.尾調(diào)用

尾調(diào)用就是指某個函數(shù)的最后一步是調(diào)用另一個函數(shù)磷账。如下所示:

function f(x) {
  if (x > 0) {
    return m(x)
  }
  return n(x);
}

上面代碼中峭沦,函數(shù)mn都屬于尾調(diào)用,因為它們都是函數(shù)f的最后一步操作逃糟。

6.數(shù)組擴展

1.擴展運算符

擴展運算符是三個點(...)吼鱼。它好比 rest 參數(shù)的逆運算蓬豁,將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列。

擴展運算符的作用

  • 替代函數(shù)的 apply 方法
    由于擴展運算符可以展開數(shù)組菇肃,所以不再需要apply方法地粪,將數(shù)組轉(zhuǎn)為函數(shù)的參數(shù)了。
function fun(a,b,c){
            console.log(a+b+c);
        }
        fun.apply(null,[1,2,3]);
        fun(...[1,2,3])
  • 復制數(shù)組
        var a = [1,2,3];
        var b = [...a];
        b[1]= 0;
        console.log(a);//1,2,3
        console.log(b);//1,0,3
  • 合并數(shù)組
        var a = [1,2,3];
        var b = [...a];
        b[1]= 0;
        console.log(a);//1,2,3
        console.log(b);//1,0,3
        var c = [...a,...b];
        console.log(c);

2.Array.from()

Array.from方法用于將兩類對象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數(shù)據(jù)結(jié)構(gòu) Set 和 Map)琐谤。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

3.Array.of()

Array.of方法用于將一組值蟆技,轉(zhuǎn)換為數(shù)組。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

7.對象的擴展

1.屬性的可枚舉性和遍歷

對象的每個屬性都有一個描述對象(Descriptor)斗忌,用來控制該屬性的行為质礼。Object.getOwnPropertyDescriptor方法可以獲取該屬性的描述對象。述對象的enumerable屬性织阳,稱為“可枚舉性”眶蕉,如果該屬性為false,就表示某些操作會忽略當前屬性唧躲。

目前造挽,有四個操作會忽略enumerable為false的屬性。

  • for...in循環(huán):只遍歷對象自身的和繼承的可枚舉的屬性弄痹。
  • Object.keys():返回對象自身的所有可枚舉的屬性的鍵名刽宪。
  • JSON.stringify():只串行化對象自身的可枚舉的屬性。
  • Object.assign(): 忽略enumerable為false的屬性界酒,只拷貝對象自身的可枚舉的屬性。

2.屬性遍歷

對象的屬性遍歷一共有5種方法嘴秸。

1.for...in

for...in循環(huán)遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)毁欣。

var obj = {
            a:'a1',
            b:'b1',
            c:12
        }
        ergodic();
        function ergodic(){
            for(let key in obj){
                console.log(key + '=' + obj[key]);
            }
        }

2.Object.keys(obj)

Object.keys返回一個數(shù)組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名岳掐。

var obj = {
            a:'a1',
            b:'b1',
            c:12
        }
        ergodic();
        function ergodic(){
            var array = Object.keys(obj);
            array.forEach(key => {
                console.log(key + '=' + obj[key]);
            })
        }

3.Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一個數(shù)組凭疮,包含對象自身的所有屬性(不含 Symbol 屬性,但是包括不可枚舉屬性)的鍵名串述。

var obj = {
            a:'a1',
            b:'b1',
            c:12
        }
        ergodic();
        function ergodic(){
            var array = Object.getOwnPropertyNames(obj);
            array.forEach(key => {
                console.log(key + '=' + obj[key]);
            })
        }

4.Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一個數(shù)組执解,包含對象自身的所有 Symbol 屬性的鍵名。

5.Reflect.ownKeys(obj)

Reflect.ownKeys返回一個數(shù)組纲酗,包含對象自身的所有鍵名衰腌,不管鍵名是 Symbol 或字符串,也不管是否可枚舉觅赊。

var obj = {
            a:'a1',
            b:'b1',
            c:12
        }
        ergodic();
        function ergodic(){
            var array = Reflect.ownKeys(obj);
            array.forEach(key => {
                console.log(key + '=' + obj[key]);
            })
        }

3.擴展運算符

對象的擴展運算符(...)用于取出參數(shù)對象的所有可遍歷屬性右蕊,拷貝到當前對象之中。對象的擴展運算符等同于使用Object.assign()方法吮螺。

var obj = {
            a:'a1',
            b:'b1',
            c:12,
        }
        var obj1 = {...obj};
        console.log(obj1);

4.Null 判斷運算符

ES2020引入了一個新的Null判斷運算符??饶囚。它的行為類似||帕翻,但是只有運算符左側(cè)的值為nullundefined時,才會返回右側(cè)的值萝风。

5.Object.is()

用來比較兩個值是否嚴格相等嘀掸,與嚴格比較運算符(===)的行為基本一致。但與===不同之處只有兩個:一是+0不等于-0规惰,二是NaN等于自身睬塌。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

6.Object.assign()

Object.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性卿拴,復制到目標對象(target)衫仑。

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

注意點

  • Object.assign方法實行的是淺拷貝,而不是深拷貝堕花。也就是說文狱,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用缘挽。
    -一旦遇到同名屬性瞄崇,Object.assign的處理方法是替換,而不是添加壕曼。

8.Symbol

Symbol是一種新的數(shù)據(jù)類型苏研,屬性名使用Symbol類型是獨一無二的,不會跟其它的屬性名發(fā)生沖突腮郊。Symbol是一個原始類型的值摹蘑,不是對象,不能添加給它添加屬性轧飞。Symbol可以接受一個字符串作為參數(shù)衅鹿,如果參數(shù)是一個對象,會先調(diào)用它的toString方法轉(zhuǎn)成字符过咬,再生成一個Symbol值大渤。Symbol函數(shù)的參數(shù)只是當前Symbol值的一個描述,因些兩個參數(shù)相同的Symbol是不相等的掸绞。Symbol值不能與其它類型的值進行運算泵三。

// 沒有參數(shù)的情況
let s1 = Symbol();
let s2 = Symbol();

s1 === s2 // false
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值可以轉(zhuǎn)化為字符串和布爾類型的值。

let sym = Symbol('My symbol');

String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'

let sym = Symbol();
Boolean(sym) // true

1.Symbol.for()

Symbol.for()可以接受一個字符串作為參數(shù)衔掸,然后生成一個Symbol值烫幕,如果參數(shù)相同,生成的Symbol值也相等敞映。

console.log(Symbol.for('s') === Symbol.for('s'));//true
console.log(Symbol('s') === Symbol('s'));//false

通過Symbol.for()創(chuàng)建的Symbol值可以使用Symbol.keyFor()獲取參數(shù)名纬霞。

let s3 = Symbol.for('s3');
console.log(Symbol.keyFor(s3));//s3
let s4 = Symbol('s4');
console.log(Symbol.keyFor(s4));//undefined

2.Symbol.hasInstance

對象的Symbol.hasInstance屬性指向一個內(nèi)部方法,當其它對象在使用instanceof時驱显,會調(diào)用這個方法诗芜。

       const Even = {
            [Symbol.hasInstance](obj){
                return Number(obj) % 2 === 0;
            }
        }
        console.log(1 instanceof Even) // false
        console.log(2 instanceof Even) // true

3.Symbol.match

對象的Symbol.match屬性指向一個函數(shù)瞳抓,當執(zhí)行str.match(myObject)時,如果該屬性存在時伏恐,將返回該方法的返回值孩哑。

        const MyMatcher = {
            [Symbol.match](str){
                return 'haha'
            }
        }
        console.log('test'.match(MyMatcher));//haha

9.Set 和 Map 數(shù)據(jù)結(jié)構(gòu)

1.Set

Set類似于數(shù)組,但成員的值是唯一的翠桦,沒有重復的值横蜒。

Set常用方法

  • Set.prototype.constructor:造函數(shù),默認就是Set函數(shù)销凑。
  • Set.prototype.size:返回成員的數(shù)量丛晌。
  • Set.prototype.add(value):往Set中添加成員。
  • Set.prototype.delete(value):刪除指定的成員斗幼。
  • Set.prototype.has(value):判斷是否包含value澎蛛。
  • Set.prototype.clear():清空所有成員。
  • Set.prototype.keys():返回鍵名的遍歷器
  • Set.prototype.values():返回鍵值的遍歷器
  • Set.prototype.entries():返回鍵值對的遍歷器
  • Set.prototype.forEach():使用回調(diào)函數(shù)遍歷每個成員

2.WeakSet

WeakSetSet類似蜕窿,成員的值是唯一的,沒有重復的值桐经。但WeakSet只能存儲對象毁兆。并且WeakSet中存儲的是弱引用,當其它對象不再引用該對象時阴挣,垃圾回收器將自動回收該對象的內(nèi)存气堕。WeakSet適合臨時存放一組對象,以及存放跟對象綁定的信息畔咧。只要這些對象在外部消失茎芭,它在 WeakSet 里面的引用就會自動消失。WeakSet不能被遍歷盒卸。

常用方法

  • WeakSet.prototype.add(value):向 WeakSet實例添加一個新成員。
  • WeakSet.prototype.delete(value):清除 WeakSet 實例的指定成員次氨。
  • WeakSet.prototype.has(value):返回一個布爾值蔽介,表示某個值是否在 WeakSet實例之中。

3.Map

標準對象的鍵值是字符串或者Symbol,不能使用對象作為鍵煮寡,如果使用對象作為鍵虹蓄,將自動轉(zhuǎn)成字符串。如果需要以對象作為鍵幸撕,將會用到Map薇组。如果對同一個鍵多次賦值,后面的值將覆蓋前面的值坐儿。

        var map = new Map();
        var obj = {a:'a'};
        map.set(obj,'這是對象作為key');
        map.set('key','haha');
        map.set('key','haha1');
        console.log(map.get('key'));//haha1
        console.log(map.get(obj));//這是對象作為key

常用方法

  • Map.prototype.size():返回Map的長度律胀。
  • Map.prototype.set(key, value)set方法設(shè)置鍵名key對應的鍵值為value宋光,然后返回整個 Map結(jié)構(gòu)。如果key已經(jīng)有值炭菌,則鍵值會被更新罪佳,否則就新生成該鍵。
  • Map.prototype.get(key)get方法讀取key對應的鍵值黑低,如果找不到key赘艳,返回undefined
  • Map.prototype.has(key)has方法返回一個布爾值克握,表示某個鍵是否在當前 Map 對象之中蕾管。
  • Map.prototype.delete(key)delete方法刪除某個鍵,返回true菩暗。如果刪除失敗掰曾,返回false
  • Map.prototype.clear():清空所有值勋眯。
  • Map.prototype.keys():返回鍵名的遍歷器婴梧。
  • Map.prototype.values():返回鍵值的遍歷器。
  • Map.prototype.entries():返回所有成員的遍歷器客蹋。
  • Map.prototype.forEach():遍歷 Map 的所有成員塞蹭。

WeakMap

WeakMap結(jié)構(gòu)與Map結(jié)構(gòu)類似,也是用于生成鍵值對的集合讶坯。

WeakMap與Map的區(qū)別

  • 首先番电,WeakMap只接受對象作為鍵名(null除外),不接受其他類型的值作為鍵名辆琅。
  • 其次漱办,WeakMap的鍵名所指向的對象,不計入垃圾回收機制婉烟。一旦不再需要時娩井,我們就必須手動刪除引用。

10.Proxy

Proxy是一種代理似袁,在目標對象之前設(shè)置一層‘攔截’洞辣,在訪問該對象時,都要先訪問這個代理對象昙衅,通過這個代理對象可以進行過濾和修改等操作扬霜。

var proxy = new Proxy(target, handler);

new Proxy生成一個Proxy實例,target表示需要攔截的目標對象而涉,handler用于定制攔截行為著瓶。如下所示為一個攔截get方法的代碼。

        var o = new Proxy({},{
            get:function(target,key){
                return 'haha'
            }
        })
        console.log(o.name);//haha
        console.log(o['type']);//haha

注意啼县,要使得Proxy起作用材原,必須針對Proxy實例(上例是proxy對象)進行操作沸久,而不是針對目標對象(上例是空對象)進行操作。

Proxy 可攔截的13個方法

  • get(target, propKey, receiver):攔截對象屬性的讀取华糖,比如proxy.fooproxy['foo']麦向。
  • set(target, propKey, value, receiver):攔截對象屬性的設(shè)置,比如proxy.foo = vproxy['foo'] = v客叉,返回一個布爾值诵竭。
  • has(target, propKey):攔截propKey in proxy的操作,返回一個布爾值兼搏。
  • deleteProperty(target, propKey):攔截delete proxy[propKey]的操作卵慰,返回一個布爾值。
  • ownKeys(target):攔截Object.getOwnPropertyNames(proxy)佛呻、Object.getOwnPropertySymbols(proxy)裳朋、Object.keys(proxy)for...in循環(huán)吓著,返回一個數(shù)組芹助。該方法返回目標對象所有自身的屬性的屬性名坷澡,而Object.keys()的返回結(jié)果僅包括目標對象自身的可遍歷屬性。
  • getOwnPropertyDescriptor(target, propKey):攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對象酣倾。
  • defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc)簿盅、Object.defineProperties(proxy, propDescs)祟身,返回一個布爾值菩貌。
  • preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個布爾值欺缘。
  • getPrototypeOf(target):攔截Object.getPrototypeOf(proxy)栋豫,返回一個對象。
  • isExtensible(target):攔截Object.isExtensible(proxy)谚殊,返回一個布爾值丧鸯。
  • setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個布爾值嫩絮。如果目標對象是函數(shù)丛肢,那么還有兩種額外操作可以攔截。
  • apply(target, object, args):攔截Proxy實例作為函數(shù)調(diào)用的操作絮记,比如proxy(...args)摔踱、proxy.call(object, ...args)虐先、proxy.apply(...)怨愤。
  • construct(target, args):攔截 Proxy 實例作為構(gòu)造函數(shù)調(diào)用的操作,比如new proxy(...args)蛹批。

11.Reflect

ReflectProxy一樣都可以操作對象撰洗。使用Reflect可以拿到對象原始的方法篮愉。

// 老寫法
'assign' in Object // true

// 新寫法
Reflect.has(Object, 'assign') // true

靜態(tài)方法

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

12.Promise

Promise是一種異步編程的解決方案,相對傳統(tǒng)的回調(diào)函數(shù)和事件要強大差导。Promise是一個對象试躏,它可以獲取異步操作的消息。

Promise特點

  • Promise的狀態(tài)不受外界的影響设褐。Promise對象代表一個操作颠蕴,它包含三種狀態(tài):pending(進行中),fulfilled(已成功)和rejected(已失敗)助析。只有異步操作的結(jié)果才能決定是哪種狀態(tài)犀被,其它操作無法改變Promise的狀態(tài)。
  • 一旦狀態(tài)改變外冀,就不會再變寡键, 任何時候都可以獲取到這個結(jié)果。Promise的狀態(tài)變化只有兩種雪隧,從
    pending(進行中)變成fulfilled(已成功)西轩,或者從pending(進行中)變成rejected(已失敗)脑沿。
    Promise缺點
  • Promise創(chuàng)建后會立即執(zhí)行藕畔,無法中途取消。
  • 如果不設(shè)置回調(diào)函數(shù)捅伤,Promise內(nèi)部發(fā)生錯誤時無法反應到外部劫流。
  • 當處于pending狀態(tài)時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)丛忆。

1.基本用法

Promise對象是一個構(gòu)造函數(shù)祠汇,通過構(gòu)造函數(shù)來創(chuàng)建Promise實例。如下所示:

        let promise = new Promise(function(resolve,reject){
            setTimeout(resolve,500);
        });
        promise.then(function(value){
            console.log('promise執(zhí)行結(jié)束')
        },function(error){
            console.log('promise執(zhí)行時發(fā)生了錯誤')
        })

Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù)熄诡,函數(shù)包括resolvereject兩個函數(shù)可很,resolve的作用是將Promise對象的狀態(tài)從pending(進行中)變成fulfilled(已成功)。在異步操作成功時調(diào)用凰浮,并將異步操作的結(jié)果我抠,作為參數(shù)傳遞出去;reject函數(shù)的作用是袜茧,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending變?yōu)?rejected)菜拓,在異步操作失敗時調(diào)用,并將異步操作報出的錯誤笛厦,作為參數(shù)傳遞出去纳鼎。

注意
因為立即 resolved 的 Promise 是在本輪事件循環(huán)的末尾執(zhí)行,總是晚于本輪循環(huán)的同步任務。,如下所示:

      let p = new Promise(function(resolve,reject){
            console.log(1);
            resolve(2);
            console.log(3);
        })
        p.then(function(value){
            console.log(value);
        })
        //1
        //3
        //2

resolve函數(shù)的參數(shù)除了正常的值以外贱鄙,還可能是另一個 Promise實例劝贸,比如像下面這樣。

let p1 = new Promise(function(resolve,reject){
            setTimeout(() => {
                reject('執(zhí)行錯誤')
            }, 3000);
        })
        
        let p2 = new Promise(function(resolve,reject){
            setTimeout(() => {
                resolve(p1);
            }, 1500);
        })
        p2.then(function(value){
            console.log(value)
        }).catch(error =>{
            console.log(error)
        })

2.Promise.prototype.then()

then方法是Promise原型對象上的一個方法逗宁,該方法的作用是Promise在發(fā)生狀態(tài)改變后的回調(diào)函數(shù)映九。then方法的第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)瞎颗。then方法將返回一個Promise對象件甥,因此可以采用鏈式寫法。如下所示:

        let p3 = new Promise(function(resolve,reject){
            resolve('p3');
        })
        p3.then(function(value){
            console.log(value);
            return new Promise(function(resolve,reject){
                setTimeout(() => {
                    resolve('p3返回的Promise');
                }, 1000);
            })
        }).then(function(value){
            console.log(value)
        })
        //p3
        //p3返回的Promise'

3.Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)的別名哼拔,用于指定發(fā)生錯誤時的回調(diào)函數(shù)嚼蚀。如下所示:

let p3 = new Promise(function(resolve,reject){
            resolve('p3');
        })
        p3.then(function(value){
            console.log(value);
            return new Promise(function(resolve,reject){
                setTimeout(() => {
                    reject(new Error('p3的Promise返回錯誤信息'));
                }, 1000);
            })
        }).then(function(value){
            console.log('result'+value)
        }).catch(function(error){
            console.log(error);
        })
        //p3
        //p3的Promise返回錯誤信息'

如果Promise的狀態(tài)已經(jīng)變成了resolved,再拋出異常是無效的管挟。因為 Promise 的狀態(tài)一旦改變僻孝,就永久保持該狀態(tài)您单,不會再變了凤优。如下所示:

      const promise = new Promise(function(resolve, reject) {
            resolve('ok');
            throw new Error('test');
        });
        promise.then(function(value) { 
            console.log(value) 
        })
        .catch(function(error) { 
            console.log(error) 
        });

Promise對象的錯誤具有‘冒泡’性質(zhì)暮现,它會一直向后傳遞塘幅,直到捕獲為止匆背。錯誤總是被下一個catch捕獲珍促,一般來說穴翩,不要在then方法里面定義 Reject狀態(tài)的回調(diào)函數(shù)(即then的第二個參數(shù)),總是使用catch方法。如下所示:

const promise = new Promise(function(resolve, reject) {
            reject('發(fā)生了錯誤');
        });
        promise.then(function(value) { 
            console.log("success-"+value) 
        })
        .catch(function(error) { 
            console.log("error="+error) 
        });
        //error=發(fā)生了錯誤

4.Promise.prototype.finally()

finally方法是不論Promise的狀態(tài)是什么樣的带膀,都會執(zhí)行finally方法钢悲,如下所示:

const promise = new Promise(function(resolve, reject) {
            reject('發(fā)生了錯誤');
        });
        promise.then(function(value) { 
            console.log("success-"+value) 
        })
        .catch(function(error) { 
            console.log("error="+error) 
        }).finally(function(value){
            console.log('finally');
        });
        //error=發(fā)生了錯誤
        //finally

finally方法不接受任何參數(shù)。finally方法總是會返回原來的值。

5.Promise.all()

Promise.all()將多個Promise包裝成一個Promise實例。

let p1 = new Promise(function(resolve,reject){
            resolve('p1');
        })
        let p2 = new Promise(function(resolve,reject){
            resolve('p2');
        })
        let p3 = new Promise(function(resolve,reject){
            resolve('p3');
        })
        let p= Promise.all([p1,p2,p3]).then(function(value){
            console.log('success:' + value)
        }).catch(function(error){
            console.log('fail='+error);
        })

p的狀態(tài)由p1厦坛、p2p3決定,分成兩種情況稼钩。

  • 只有p1坝撑、p2p3的狀態(tài)全為fulfilled時膨蛮,p的狀態(tài)才為fulfilled持偏。此時p1p2怎囚、p3的返回值組成一個數(shù)組卿叽,傳遞給p的回調(diào)函數(shù)。
  • 只要p1桩了、p2p3中有一個狀態(tài)為rejected時埠戳,p的狀態(tài)就為rejected井誉。此時第一個被reject的實例的返回值,會傳遞給p的回調(diào)函數(shù)整胃。

注意颗圣,如果作為參數(shù)的 Promise實例,自己定義了catch方法屁使,那么它一旦被rejected在岂,并不會觸發(fā)Promise.all()catch方法。如下所示:

let p1 = new Promise(function(resolve,reject){
            resolve('p1');
        })
        let p2 = new Promise(function(resolve,reject){
            reject('p2');
        }).catch(function(error){

        })
        let p3 = new Promise(function(resolve,reject){
            resolve('p3');
        })
        let promise = Promise.all([p1,p2,p3]).then(function(value){
            console.log('success:' + value)
        }).catch(function(error){
            console.log('fail='+error);
        })

6.Promise.race()

Promise.race()Promise.all()類似蛮寂,都可以將多個Promise包裝成一個Promise實例蔽午。 不同之處是只要有一個實例率先改變狀態(tài),Promise.race()實例的結(jié)果也將改變狀態(tài)酬蹋,如下所示:

let p1 = new Promise(function(resolve,reject){
            resolve('p1');
        })
        let p2 = new Promise(function(resolve,reject){
            reject('p2');
        }).catch(function(error){

        })
        let p3 = new Promise(function(resolve,reject){
            resolve('p3');
        })
        let promise = Promise.race([p1,p2,p3]).then(function(value){
            console.log('success:' + value)
        }).catch(function(error){
            console.log('fail='+error);
        })
        //success:p1

7.Promise.allSettled()

Promise.allSettled()也是接受一組 Promise 實例作為參數(shù)及老,包裝成一個新的 Promise實例。只有等到所有這些參數(shù)實例都返回結(jié)果骄恶,不管是fulfilled還是rejected,包裝實例才會結(jié)束匕垫。該方法由 ES2020僧鲁。如下所示:

      let p1 = new Promise(function(resolve,reject){
            resolve('p1');
        })
        let p2 = new Promise(function(resolve,reject){
            reject('p2');
        })
        let p3 = new Promise(function(resolve,reject){
            resolve('p3');
        })
        let promise = Promise.allSettled([p1,p2,p3]).then(function(value){
            debugger
            console.log('success:' + value)
        }).catch(function(error){
            console.log('fail='+error);
        })

該方法返回的新的 Promise 實例,一旦結(jié)束象泵,狀態(tài)總是fulfilled寞秃,不會變成rejected。狀態(tài)變成fulfilled后偶惠,Promise 的監(jiān)聽函數(shù)接收到的參數(shù)是一個數(shù)組蜕该,每個成員對應一個傳入Promise.allSettled()Promise實例。

8.Promise.resolve()

Promise.resolve()將現(xiàn)有對象轉(zhuǎn)化為Promise對象洲鸠。如下所示:

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

Promise.resolve方法的參數(shù)分成四種情況堂淡。

  • 參數(shù)是一個 Promise 實例馋缅,如果參數(shù)是 Promise 實例,那么Promise.resolve將不做任何修改绢淀、原封不動地返回這個實例萤悴。
  • 參數(shù)是一個thenable對象。thenable對象指的是具有then方法的對象皆的,Promise.resolve方法會將這個對象轉(zhuǎn)為 Promise對象覆履,然后就立即執(zhí)行thenable對象的then方法。
  • 參數(shù)不是具有then方法的對象费薄,或根本就不是對象硝全。如果參數(shù)是一個原始值,或者是一個不具有then方法的對象楞抡,則Promise.resolve方法返回一個新的 Promise 對象伟众,狀態(tài)為resolved
  • 不帶有任何參數(shù)召廷。Promise.resolve()方法允許調(diào)用時不帶參數(shù)凳厢,直接返回一個resolved狀態(tài)的 Promise對象。

9.Promise.reject()

Promise.reject()返回一個新的Promise對象竞慢,該對象的狀態(tài)為rejected先紫。

13.Iterator 和 for...of 循環(huán)

Iterator是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制筹煮。任何數(shù)據(jù)類型只要部署了Iterator接口遮精,就能完成遍歷操作。

Iterator作用

  • 為各種數(shù)據(jù)類型提供一個統(tǒng)一的败潦,簡便的訪問接口仑鸥。
  • 使用數(shù)據(jù)結(jié)構(gòu)的成員按照某種次序進行排列。
  • 使用 for...of進行遍歷变屁。

1.默認的Iterator 接口

凡是部署了Symbol.iterator屬性的數(shù)據(jù)結(jié)構(gòu)眼俊,就稱為部署了遍歷器接口。調(diào)用這個接口粟关,就會返回一個遍歷器對象疮胖。

原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函數(shù)的 arguments 對象
  • NodeList 對象

如下所示:

       var array = [1,2,3,4];
        var iterator = array[Symbol.iterator]();
        console.log(iterator.next());
        console.log(iterator.next());
        console.log(iterator.next());
        console.log(iterator.next());
        console.log(iterator.next());

14.Generator

Generator是一種異步編程的解決方案闷板,語法行為與傳統(tǒng)的函數(shù)不同澎灸。Generator是一種狀態(tài)機函數(shù),封裝了多個內(nèi)部狀態(tài)遮晚。執(zhí)行Generator函數(shù)會返回一個遍歷器對象性昭,可依次遍歷Generator函數(shù)內(nèi)部的每一個狀態(tài)。

Generator 特征

  • function與函數(shù)名之間有一個*县遣。如function* test(){}糜颠。
  • 函數(shù)內(nèi)部可以使用yield表達式汹族,定義不同的內(nèi)部狀態(tài)。
          function* helloGenerator(){
                yield 'hello';
                yield 'Generator';
                return 'end'
            }
            var it = helloGenerator();
            for(var item of it){
                console.log(item);
            }

Generator函數(shù)的調(diào)用跟調(diào)用普通函數(shù)一樣其兴,直接在函數(shù)名后面加一個括號顶瞒,但與普通函數(shù)不一樣的是,Generator函數(shù)執(zhí)行后返回的是一個Iterator對象元旬,可以調(diào)用next方法榴徐,或者使用for...of進行遍歷。

1.yield 表達式

由于Generator函數(shù)返回的是一個Iterator對象匀归,只有在執(zhí)行next方法時才會遍歷下一個狀態(tài)坑资,通過使用yield表達式可以設(shè)置一種暫停標注。當執(zhí)行完next方法后穆端,將yield后面的值作為value返回袱贮,執(zhí)行完一條后并暫停,只有當調(diào)用下一個next方法才會往下執(zhí)行并繼續(xù)查找yield表達式徙赢,當查找不到yield時字柠,將返回并結(jié)束遍歷探越。yield只能在Generator函數(shù)中使用狡赐,在其它地方使用時將報錯。

2.Generator.prototype.throw()

Generator函數(shù)返回的對象钦幔,都有一個throw方法枕屉,可以在函數(shù)體外拋出錯誤,然后在 Generator 函數(shù)體內(nèi)捕獲鲤氢。

3.yield* 表達式

使用yield*表達式搀擂,用來在一個 Generator 函數(shù)里面執(zhí)行另一個 Generator 函數(shù)。

function* foo() {
  yield 'a';
  yield 'b';
}
function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  for (let v of foo()) {
    yield v;
  }
  yield 'y';
}

for (let v of bar()){
  console.log(v);
}
// "x"
// "a"
// "b"
// "y"

4.Generator用途

  • 異步操作的同步化表達
    通過使用Generator可以處理異步函數(shù)卷玉,用來代替回調(diào)函數(shù)哨颂。將異步操作寫在yield表達式上肛捍,等調(diào)用next方法后再往后執(zhí)行铃肯。這實際上等同于不需要寫回調(diào)函數(shù)了,因為異步操作的后續(xù)操作可以放在yield表達式下面疆拘,反正要等到調(diào)用next方法時再執(zhí)行寝并。如下所示:
function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 加載UI
loader.next()

// 卸載UI
loader.next()
  • 控制流程管理
    利用for...of循環(huán)會自動依次執(zhí)行yield命令的特性箫措,提供一種更一般的控制流管理的方法。如下所示:
let steps = [step1Func, step2Func, step3Func];

function* iterateSteps(steps){
  for (var i=0; i< steps.length; i++){
    var step = steps[i];
    yield step();
  }
}
  • 部署 Iterator 接口
    利用 Generator 函數(shù)衬潦,可以在任意對象上部署 Iterator 接口斤蔓。如下所示:
function* iterEntries(obj) {
  let keys = Object.keys(obj);
  for (let i=0; i < keys.length; i++) {
    let key = keys[i];
    yield [key, obj[key]];
  }
}

let myObj = { foo: 3, bar: 7 };

for (let [key, value] of iterEntries(myObj)) {
  console.log(key, value);
}

// foo 3
// bar 7
  • 作為數(shù)據(jù)結(jié)構(gòu)
    Generator可以看作是數(shù)據(jù)結(jié)構(gòu),更確切地說镀岛,可以看作是一個數(shù)組結(jié)構(gòu)弦牡,因為 Generator 函數(shù)可以返回一系列的值友驮,這意味著它可以對任意表達式,提供類似數(shù)組的接口喇伯。如下所示:
function* doStuff() {
  yield fs.readFile.bind(null, 'hello.txt');
  yield fs.readFile.bind(null, 'world.txt');
  yield fs.readFile.bind(null, 'and-such.txt');
}

for (task of doStuff()) {
  // task是一個函數(shù)喊儡,可以像回調(diào)函數(shù)那樣使用它
}

5.Generator 函數(shù)的異步應用

異步是指一個任務不是連續(xù)完成的,可以理解成該任務被分成為兩段稻据,先執(zhí)行第一段艾猜,然后再執(zhí)行其它的任務,等做好準備后再執(zhí)行第二段的任務捻悯。

比如匆赃,有一個任務是讀取文件進行處理,任務的第一段是向操作系統(tǒng)發(fā)出請求今缚,要求讀取文件算柳。然后,程序執(zhí)行其他任務姓言,等到操作系統(tǒng)返回文件瞬项,再接著執(zhí)行任務的第二段(處理文件)。這種不連續(xù)的執(zhí)行何荚,就叫做異步囱淋。

6.async 函數(shù)

async函數(shù)是Generator的方法糖。async就是將Generator函數(shù)的*換成了async餐塘,將yield換成了await妥衣。async函數(shù)返回一個Promise對象,可以使用then方法添加回調(diào)函數(shù)戒傻。當函數(shù)執(zhí)行的時候税手,一旦遇到await就會先返回,等到異步操作完成需纳,再接著執(zhí)行函數(shù)體內(nèi)后面的語句芦倒。

      let readFile = function(){
            return new Promise(function(resolve,reject){
                setTimeout(() => {
                    resolve('文件讀取成功')
                }, 2000);
            })
        }

        let asyncReadFile = async function(){
            let rf1 = await readFile()
            let rf2 = await readFile();
            console.log(rf1.toString());
            console.log(rf2.toString());
        }
        asyncReadFile();

7.await命令

await命令后面是一個Promise對象,如果是其它值不翩,將直接返回對應的值兵扬。如下所示:

async function f() {
  // 等同于
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123

await命令后面的Promise對象如果變?yōu)?code>reject狀態(tài),則reject的參數(shù)會被catch方法的回調(diào)函數(shù)接收到慌盯。如下所示:

async function f() {
  await Promise.reject('出錯了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出錯了

任何一個await語句后面的Promise 對象變?yōu)?code>reject狀態(tài)周霉,那么整個async函數(shù)都會中斷執(zhí)行亚皂。

async function f() {
  await Promise.reject('出錯了');
  await Promise.resolve('hello world'); // 不會執(zhí)行
}

使用try...catch結(jié)構(gòu)俱箱,實現(xiàn)多次重復嘗試。

const superagent = require('superagent');
const NUM_RETRIES = 3;

async function test() {
  let i;
  for (i = 0; i < NUM_RETRIES; ++i) {
    try {
      await superagent.get('http://google.com/this-throws-an-error');
      break;
    } catch(err) {}
  }
  console.log(i); // 3
}

test();

上面代碼中灭必,如果await操作成功狞谱,就會使用break語句退出循環(huán)乃摹;如果失敗,會被catch語句捕捉跟衅,然后進入下一輪循環(huán)孵睬。

使用注意點

  • await命令后面的Promise對象,運行結(jié)果可能是rejected伶跷,最好將 await命令放在try...catch中掰读。
  • 多個 await命令如果不存在先后順序,最好同時時行叭莫。
  • await命令只能在async中使用蹈集,在其它地方使用將會報錯。

15.

  • async函數(shù)可以保留運行堆棧雇初。

8.async函數(shù)實現(xiàn)原理

async的實現(xiàn)原理就是將Genernator函數(shù)與自動執(zhí)行器包裝在一個函數(shù)里拢肆。如下所示:

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

所有的async函數(shù)都可以寫成上面的第二種形式,其中的spawn函數(shù)就是自動執(zhí)行器靖诗。

16.Class

ES6可以使用class關(guān)鍵字創(chuàng)建類郭怪,相比使用構(gòu)造函數(shù)創(chuàng)建類更具有面向?qū)ο蟮乃枷耄缦滤荆?/p>

        class Point{
            constructor(x,y){
                this.x = x;
                this.y = y;
            }
            toString(){
                console.log("x="+this.x + " y="+this.y);
            }
        }
        let point = new Point(12,13);
        point.toString();

類的內(nèi)部所有定義的方法刊橘,都是不可枚舉的(non-enumerable)鄙才。如下所示:

class Point{
            constructor(x,y){
                this.x = x;
                this.y = y;
            }
            toString(){
                console.log("x="+this.x + " y="+this.y);
            }
        }
        let point = new Point(12,13);
        point.toString();
        console.log(Object.keys(Point.prototype));//[]
        console.log(Object.keys(point));// ["x", "y"]

1.取值函數(shù)(getter)和存值函數(shù)(setter)

ES5一樣,在“類”的內(nèi)部可以使用getset關(guān)鍵字伤为,對某個屬性設(shè)置存值函數(shù)和取值函數(shù)咒循,攔截該屬性的存取行為据途。

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

2.Class表達式

函數(shù)一樣绞愚,類也可以使用表達式的形式定義。如下所示:

        const Circle = class C{
            getRaidus(){
                console.log('圓的半徑為10')
            }
        }
        const circle = new Circle();
        circle.getRaidus();

3.靜態(tài)方法

類相當于實例的原型颖医,所有在類中定義的方法位衩,都會被實例繼承。如果在一個方法前熔萧,加上static關(guān)鍵字糖驴,就表示該方法不會被實例繼承,而是直接通過類來調(diào)用佛致,這就稱為“靜態(tài)方法”贮缕。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

4.實例屬性

Class定義的類中,添加屬性可以在構(gòu)造函數(shù)中添加俺榆,也可以在類的頂層定義屬性感昼,如下所示:

      const Circle = class C{
            raidus = 20;
            constructor(){
                this.x = 10;
                this.y = 10;
            }
            getRaidus(){
                console.log(`x=${this.x};x=${this.y};radius=${this.raidus};`)
            }
        }
        const circle = new Circle();
        circle.getRaidus();

5.靜態(tài)屬性

靜態(tài)屬性指的是 Class 本身的屬性,即Class.propName罐脊,而不是定義在實例對象(this)上的屬性定嗓。

// 老寫法
class Foo {
  // ...
}
Foo.prop = 1;

// 新寫法
class Foo {
  static prop = 1;
}

6.私有屬性

class加了私有屬性蜕琴。方法是在屬性名之前,使用#表示宵溅。如下所示:

        class Foo {
            #privateValue = 42;
            getPrivateValue() {
                return this.#privateValue;
            }
        }

        var foo = new Foo();
        console.log(foo.getPrivateValue())

7.繼承

Class可以通過extends關(guān)鍵字實現(xiàn)繼承凌简,這比 ES5的通過修改原型鏈實現(xiàn)繼承,要清晰和方便很多恃逻。如下所示:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調(diào)用父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 調(diào)用父類的toString()
  }
}

子類必須在constructor方法中調(diào)用super方法雏搂,否則新建實例時會報錯。這是因為子類自己的this對象寇损,必須先通過父類的構(gòu)造函數(shù)完成塑造畔派,得到與父類同樣的實例屬性和方法,然后再對其進行加工润绵,加上子類自己的實例屬性和方法线椰。如果不調(diào)用super方法,子類就得不到this對象尘盼。如果子類沒有定義constructor方法憨愉,這個方法會被默認添加,代碼如下卿捎。也就是說配紫,不管有沒有顯式定義,任何一個子類都有constructor方法午阵。

8.super

super既可以當作函數(shù)來使用躺孝,也可以使用對象來使用。

  • super作為函數(shù)調(diào)用時底桂,代表父類的構(gòu)造函數(shù)植袍。ES6 要求,子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù)籽懦。
class A {}

class B extends A {
  constructor() {
    super();
  }
}
  • super作為對象時于个,在普通方法中,指向父類的原型對象暮顺;在靜態(tài)方法中厅篓,指向父類。
class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();

super指向父類的原型對象捶码,所以定義在父類實例上的屬性和方法羽氮,無法通過super來調(diào)用的。

9.Object.getPrototypeOf()

Object.getPrototypeOf可以用來從子類上獲取父類惫恼。

Object.getPrototypeOf(ColorPoint) === Point
// true

可以使用這個方法判斷档押,一個類是否繼承了另一個類。

10.類的 prototype 屬性和proto屬性

Class創(chuàng)建的類,同樣有prototype__proto__屬性汇荐,同樣存在兩條繼承鏈洞就。

  • 子類的__proto__屬性,表示構(gòu)造函數(shù)的繼承掀淘,總是指向父類旬蟋。。
  • 子類prototype屬性的__proto__屬性革娄,表示方法的繼承倾贰,總是指向父類的prototype屬性。
    class A {

        }

        class B extends A {

        }
        console.log(B.__proto__ === A);//true
        console.log(B.prototype.__proto__ === A.prototype);//true

11.實例的 proto 屬性

子類實例的__proto__屬性的__proto__屬性拦惋,指向父類實例的__proto__屬性匆浙,也就是說子類原型的原型是父類的原型。

      class A {

        }

        class B extends A {

        }
        let a = new A();
        let b = new B();
        console.log(b.__proto__.__proto__ === a.__proto__);//true

17.Module

ES6 的模塊自動采用嚴格模式厕妖,不管你有沒有在模塊頭部加上"use strict";首尼。

嚴格模式

  • 變量必須聲明后再使用
  • 函數(shù)的參數(shù)不能有同名屬性,否則報錯
  • 不能使用with語句
  • 不能對只讀屬性賦值言秸,否則報錯
  • 不能使用前綴 0 表示八進制數(shù)软能,否則報錯
  • 不能刪除不可刪除的屬性,否則報錯
  • 不能刪除變量delete prop举畸,會報錯查排,只能刪除屬性delete global[prop]
  • eval不會在它的外層作用域引入變量
  • evalarguments不能被重新賦值
  • arguments不會自動反映函數(shù)參數(shù)的變化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局對象
  • 不能使用fn.callerfn.arguments獲取函數(shù)調(diào)用的堆棧
  • 增加了保留字(比如protectedstaticinterface

1.export 命令

模塊主要由兩個命令組成抄沮,exportimport命令跋核。export用于向外部導出接口。import用于引入外部導出的接口叛买。一個單獨的文件就是一個模塊砂代,模塊類的變量和函數(shù)在外部無法獲取,如果希望外部能夠訪問聪全,需要使用export輸入變量泊藕。export命令可以出現(xiàn)在模塊的任何位置辅辩,只要處于模塊頂層就可以难礼。如果處于塊級作用域內(nèi),就會報錯玫锋。

export const url = 'http://www.gzcopright.cn';
function test (){

};

function util (){

}
export {test,util as Util};

2.import命令

使用export導出的變量蛾茉,在其它JS文件中使用import可以加載模塊。import命令輸入的變量都是只讀的撩鹿,因為它的本質(zhì)是輸入接口谦炬。也就是說,不允許在加載模塊的腳本里面,改寫接口键思。import后面的from指定模塊文件的位置础爬,可以是相對路徑,也可以是絕對路徑吼鳞,.js后綴可以省略看蚜。import命令具有提升效果,會提升到整個模塊的頭部赔桌,首先執(zhí)行,這種行為的本質(zhì)是供炎,import命令是編譯階段執(zhí)行的,在代碼運行之前疾党。

import{url,Util,log} from './module.js'

function test(){
    console.log(url)
}

test();

如果多次執(zhí)行import語句音诫,只會執(zhí)行一次,如下所示:

import 'lodash';
import 'lodash';

3.模塊的整體加載

除了指定加載某個輸出值雪位,可以使用*來加載所有輸出的變量竭钝,如下所示:

import * as obj from './module.js'

function test(){
    console.log(obj.url)
}

test();

4.export default 命令

export default用于指定模塊的默認輸出,一個模塊只能有一個默認輸出雹洗。export default輸出的命令在使用import時蜓氨,不需要使用{},并且名稱可以隨便取。如下所示:

export default function () {
  console.log('foo');
}

// import-default.js
import customName from './export-default';
customName(); // 'foo'

export default命令其實只是輸出一個叫做default的變量,所以它后面不能跟變量聲明語句霞怀。

// 正確
export var a = 1;

// 正確
var a = 1;
export default a;

// 錯誤
export default var a = 1;

5.export 與 import 的復合寫法

如果在同一模塊中扣讼,先輸入后輸出同一個模塊,importexport可以寫在一起腌歉。

export { foo, bar } from 'my_module';

// 可以簡單理解為
import { foo, bar } from 'my_module';
export { foo, bar };

6.模塊的繼承

模塊之間是可以繼承的,如下所示,circleplus模塊顷霹,繼承了circle模塊。

// circleplus.js

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}

上面代碼中的export *击吱,表示再輸出circle模塊的所有屬性和方法淋淀。

7.import()

使用import()可以動態(tài)加載模塊,使用方法跟import一樣覆醇,唯一的區(qū)別就是import需要放在代碼的頂層朵纷,不支持動態(tài)導入。

適用場合

  • 按需加載永脓,import()可以在需要的時候袍辞,再加載某個模塊。
button.addEventListener('click', event => {
  import('./dialogBox.js')
  .then(dialogBox => {
    dialogBox.open();
  })
  .catch(error => {
    /* Error handling */
  })
});
  • 條件加載常摧。import()可以放在if代碼塊搅吁,根據(jù)不同的情況威创,加載不同的模塊。
if (condition) {
  import('moduleA').then(...);
} else {
  import('moduleB').then(...);
}
  • 動態(tài)的模塊路徑谎懦。import()允許模塊路徑動態(tài)生成肚豺。
import(f())
.then(...);

上面代碼中,根據(jù)函數(shù)f的返回結(jié)果界拦,加載不同的模塊详炬。

18.Module 的加載實現(xiàn)

加載js文件,使用的是<script>寞奸,默認是同步加載呛谜,既瀏覽器引擎解析dom時,如果碰到了script標簽時會先加載js腳本枪萄。當js文件太大時隐岛,會阻塞線程出現(xiàn)卡死的情況,所以瀏覽器允許腳本異步加載瓷翻,下面就是兩種異步加載的語法聚凹。

<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>

deferasync都支持異步加載腳本。但兩者有一定的區(qū)別

  • defer加載的腳本需要等面布渲染完成后再運行齐帚,如果有多個script使用defer時妒牙,每個文件是按順序執(zhí)行了。
  • async是加載完后就執(zhí)行对妄,如果有多個script使用async時湘今,順序是不固定的。

瀏覽器加載ES6模塊剪菱,也使用<script>標簽摩瞎,但是要加入type="module"屬性。瀏覽器對于帶有type="module"<script>孝常,都是異步加載旗们,不會造成堵塞瀏覽器,即等到整個頁面渲染完构灸,再執(zhí)行模塊腳本上渴,等同于打開了<script>標簽的defer屬性。

1.ES6 模塊與 CommonJS 模塊的差異

  • ES6 模塊輸出的是值的引用喜颁,而CommonJS 模塊輸出的是值拷貝稠氮。
  • ES6 模塊是編譯時加載接口,而CommonJS 模塊是運行時加載洛巢。

19.編程風格

  • let 取代 var,常量使用const
  • 靜態(tài)字符串一律使用單引號或反引號括袒,不使用雙引號。動態(tài)字符串使用反引號稿茉。
  • 使用數(shù)組成員對變量賦值時,函數(shù)的參數(shù)如果是對象的成員,如果函數(shù)返回多個值漓库,優(yōu)先使用解構(gòu)賦值恃慧。
  • 單行定義的對象,最后一個成員不要以逗號結(jié)尾渺蒿,多行定義的對象痢士,最后一個成員以逗號結(jié)尾。對象盡量靜態(tài)化茂装,一旦定義怠蹂,就不得隨意添加新的屬性。如果添加屬性不可避免少态,要使用Object.assign方法城侧。
  • 使用擴展運算符(...)拷貝數(shù)組。使用 Array.from方法彼妻,將類似數(shù)組的對象轉(zhuǎn)為數(shù)組嫌佑。
  • 立即執(zhí)行函數(shù)可以寫成箭頭函數(shù)的形式。那些使用匿名函數(shù)當作參數(shù)的場合侨歉,盡量用箭頭函數(shù)代替屋摇。因為這樣更簡潔,而且綁定了 this幽邓。
  • 注意區(qū)分 ObjectMap炮温,只有模擬現(xiàn)實世界的實體對象時,才使用 Object牵舵。如果只是需要key: value的數(shù)據(jù)結(jié)構(gòu)茅特,使用 Map結(jié)構(gòu)。因為 Map有內(nèi)建的遍歷機制棋枕。
  • 總是用 Class白修,取代需要 prototype的操作。因為 Class 的寫法更簡潔重斑,更易于理解兵睛。
  • 使用import取代require。使用export取代module.exports窥浪。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祖很,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漾脂,更是在濱河造成了極大的恐慌假颇,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骨稿,死亡現(xiàn)場離奇詭異笨鸡,居然都是意外死亡姜钳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門形耗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哥桥,“玉大人,你說我怎么就攤上這事激涤∧飧猓” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵倦踢,是天一觀的道長送滞。 經(jīng)常有香客問我,道長辱挥,這世上最難降的妖魔是什么犁嗅? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮般贼,結(jié)果婚禮上愧哟,老公的妹妹穿的比我還像新娘。我一直安慰自己哼蛆,他們只是感情好蕊梧,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著腮介,像睡著了一般肥矢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叠洗,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天甘改,我揣著相機與錄音,去河邊找鬼灭抑。 笑死十艾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的腾节。 我是一名探鬼主播忘嫉,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼案腺!你這毒婦竟也來了庆冕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤劈榨,失蹤者是張志新(化名)和其女友劉穎访递,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體同辣,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡拷姿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年惭载,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跌前。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡棕兼,死狀恐怖陡舅,靈堂內(nèi)的尸體忽然破棺而出抵乓,到底是詐尸還是另有隱情,我是刑警寧澤靶衍,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布灾炭,位于F島的核電站,受9級特大地震影響颅眶,放射性物質(zhì)發(fā)生泄漏蜈出。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一涛酗、第九天 我趴在偏房一處隱蔽的房頂上張望铡原。 院中可真熱鬧,春花似錦商叹、人聲如沸燕刻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卵洗。三九已至,卻和暖如春弥咪,著一層夾襖步出監(jiān)牢的瞬間过蹂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工聚至, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酷勺,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓扳躬,卻偏偏與公主長得像脆诉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坦报,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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

  • 一库说、let 和 constlet:變量聲明, const:只讀常量聲明(聲明的時候賦值)。 let 與 var 的...
    dadage456閱讀 762評論 0 0
  • 本文為阮一峰大神的《ECMAScript 6 入門》的個人版提純片择! babel babel負責將JS高級語法轉(zhuǎn)義潜的,...
    Devildi已被占用閱讀 1,983評論 0 4
  • defineProperty() 學習書籍《ECMAScript 6 入門 》 Proxy Proxy 用于修改某...
    Bui_vlee閱讀 654評論 0 1
  • [TOC] 參考阮一峰的ECMAScript 6 入門參考深入淺出ES6 let和const let和const都...
    郭子web閱讀 1,781評論 0 1
  • 本人是android開發(fā)的,由于最近React Native的火熱,再加上自己完全不懂JS的語法,俗話說的好"落后...
    Bui_vlee閱讀 283評論 0 0