由于受新型冠狀病毒
的影響墙歪,假期又延長了,又不能出門贝奇,只好在家認真學習了虹菲,就借此機會閱讀完阮一峰老師的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ù)m
和n
都屬于尾調(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è)的值為null
或undefined
時,才會返回右側(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
WeakSet
和Set
類似蜕窿,成員的值是唯一的,沒有重復的值桐经。但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.foo
和proxy['foo']
麦向。set(target, propKey, value, receiver)
:攔截對象屬性的設(shè)置,比如proxy.foo = v
或proxy['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
Reflect
跟Proxy
一樣都可以操作對象撰洗。使用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ù)包括resolve
和reject
兩個函數(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
厦坛、p2
、p3
決定,分成兩種情況稼钩。
- 只有
p1
坝撑、p2
、p3
的狀態(tài)全為fulfilled
時膨蛮,p
的狀態(tài)才為fulfilled
持偏。此時p1
、p2
怎囚、p3
的返回值組成一個數(shù)組卿叽,傳遞給p
的回調(diào)函數(shù)。- 只要
p1
桩了、p2
、p3
中有一個狀態(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)部可以使用get
和set
關(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
不會在它的外層作用域引入變量eval
和arguments
不能被重新賦值arguments
不會自動反映函數(shù)參數(shù)的變化- 不能使用
arguments.callee
- 不能使用
arguments.caller
- 禁止
this
指向全局對象- 不能使用
fn.caller
和fn.arguments
獲取函數(shù)調(diào)用的堆棧- 增加了保留字(比如
protected
、static
和interface
)
1.export 命令
模塊主要由兩個命令組成抄沮,export
和import
命令跋核。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 的復合寫法
如果在同一模塊中扣讼,先輸入后輸出同一個模塊,import
和export
可以寫在一起腌歉。
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>
defer
和async
都支持異步加載腳本。但兩者有一定的區(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ū)分
Object
和Map
炮温,只有模擬現(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
窥浪。