ES6入門(mén)摘錄筆記(二)

三,字符串?dāng)U展

3.1 Unicode表示法

ES6 做出了改進(jìn),只要將碼點(diǎn)放入大括號(hào),就能正確解讀該字符。
有了這種表示法之后,JavaScript 共有6種方法可以表示一個(gè)字符。

'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true

3.2 codePointAt()

這篇文章講的特別好?。〗忉屃耸裁词聈nicode,utf-8,utf-16以及javascript的實(shí)現(xiàn)。

對(duì)于四字節(jié)字符,charAt方法無(wú)法讀取整個(gè)字符,charCodeAt方法只能分別返回前兩個(gè)字節(jié)和后兩個(gè)字節(jié)的值。ES6提供了codePointAt方法,能夠正確處理4個(gè)字節(jié)儲(chǔ)存的字符,返回一個(gè)字符的碼點(diǎn)。

let s = '??a';
s.codePointAt(0) // 134071

codePointAt方法的參數(shù),仍然是不正確的。比如,上面代碼中,字符a在字符串s的正確位置序號(hào)應(yīng)該是1,但是必須向codePointAt方法傳入2。解決這個(gè)問(wèn)題的一個(gè)辦法是使用for...of循環(huán),因?yàn)樗鼤?huì)正確識(shí)別32位的UTF-16字符。

codePointAt方法是測(cè)試一個(gè)字符由兩個(gè)字節(jié)還是由四個(gè)字節(jié)組成的最簡(jiǎn)單方法。

function is32Bit(c) {
  return c.codePointAt(0) > 0xFFFF;
}

3.3 String.fromCodePoint()

ES6提供了String.fromCodePoint方法,可以識(shí)別大于0xFFFF的字符,彌補(bǔ)了String.fromCharCode方法的不足。在作用上,正好與codePointAt方法相反。注意,fromCodePoint方法定義在String對(duì)象上,而codePointAt方法定義在字符串的實(shí)例對(duì)象上。

String.fromCodePoint(0x20BB7)
// "??"
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true

上面代碼中,如果String.fromCodePoint方法有多個(gè)參數(shù),則它們會(huì)被合并成一個(gè)字符串返回。

3.4 字符串遍歷接口

字符串可以被for...of循環(huán)遍歷。最大的優(yōu)點(diǎn)是可以識(shí)別大于0xFFFF的碼點(diǎn),傳統(tǒng)的for循環(huán)無(wú)法識(shí)別這樣的碼點(diǎn)。

3.5 at()

目前,有一個(gè)提案,提出字符串實(shí)例的at方法,可以識(shí)別 Unicode 編號(hào)大于0xFFFF的字符,返回正確的字符。ES5的charAt方法有局限。這個(gè)方法可以通過(guò)墊片庫(kù)實(shí)現(xiàn)。

3.6 normalize()

ES6 提供字符串實(shí)例的normalize()方法,用來(lái)將字符的不同表示方法統(tǒng)一為同樣的形式,這稱(chēng)為 Unicode 正規(guī)化。

'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true

3.7 includes(), startsWith(), endsWith()

傳統(tǒng)上,JavaScript只有indexOf方法,可以用來(lái)確定一個(gè)字符串是否包含在另一個(gè)字符串中。ES6又提供了三種新方法。

let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

上面代碼表示,使用第二個(gè)參數(shù)n時(shí),endsWith的行為與其他兩個(gè)方法有所不同。它針對(duì)前n個(gè)字符,而其他兩個(gè)方法針對(duì)從第n個(gè)位置直到字符串結(jié)束。

3.8 repeat()

repeat方法返回一個(gè)新字符串,表示將原字符串重復(fù)n次。

'x'.repeat(3) // "xxx"

3.9 padStart(),padEnd()

'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'

上面代碼中,padStart和padEnd一共接受兩個(gè)參數(shù),第一個(gè)參數(shù)用來(lái)指定字符串的長(zhǎng)度,第二個(gè)參數(shù)是用來(lái)補(bǔ)全的字符串。

3.10 模板字符串(template string)

模板字符串是增強(qiáng)版的字符串,用反引號(hào)(`)標(biāo)識(shí)。它可以當(dāng)作普通字符串使用,也可以用來(lái)定義多行字符串,或者在字符串中嵌入變量。如果使用模板字符串表示多行字符串,所有的空格和縮進(jìn)都會(huì)被保留在輸出之中。

// 字符串中嵌入變量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

模板字符串甚至還能嵌套。

3.11 實(shí)例:模板編譯

這里是模板字符串的一個(gè)例子。
通過(guò)模板字符串,生成正式模板的實(shí)例。具體代碼看教程。

3.12 標(biāo)簽?zāi)0澹╰agged template)

模板字符串的功能,不僅僅是上面這些。它可以緊跟在一個(gè)函數(shù)名后面,該函數(shù)將被調(diào)用來(lái)處理這個(gè)模板字符串。這被稱(chēng)為“標(biāo)簽?zāi)0濉?。?biāo)簽?zāi)0迤鋵?shí)不是模板,而是函數(shù)調(diào)用的一種特殊形式?!皹?biāo)簽”指的就是函數(shù),緊跟在后面的模板字符串就是它的參數(shù)。

alert`123`
// 等同于
alert(123)

let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);

“標(biāo)簽?zāi)0濉钡囊粋€(gè)重要應(yīng)用,就是過(guò)濾 HTML 字符串,防止用戶輸入惡意內(nèi)容。
標(biāo)簽?zāi)0宓牧硪粋€(gè)應(yīng)用,就是多語(yǔ)言轉(zhuǎn)換(國(guó)際化處理)。模板字符串本身并不能取代Mustache之類(lèi)的模板庫(kù),因?yàn)闆](méi)有條件判斷和循環(huán)處理功能,但是通過(guò)標(biāo)簽函數(shù),你可以自己添加這些功能。

模板處理函數(shù)的第一個(gè)參數(shù)(模板字符串?dāng)?shù)組),還有一個(gè)raw屬性。

3.13 string.raw

ES6還為原生的String對(duì)象,提供了一個(gè)raw方法。

String.raw方法,往往用來(lái)充當(dāng)模板字符串的處理函數(shù),返回一個(gè)斜杠都被轉(zhuǎn)義(即斜杠前面再加一個(gè)斜杠)的字符串,對(duì)應(yīng)于替換變量后的模板字符串。

String.raw`Hi\n${2+3}!`;
// "Hi\\n5!"

3.14 模板字符串的限制

前面提到標(biāo)簽?zāi)0謇锩?,可以?xún)?nèi)嵌其他語(yǔ)言。但是,模板字符串默認(rèn)會(huì)將字符串轉(zhuǎn)義,導(dǎo)致無(wú)法嵌入其他語(yǔ)言。為了解決這個(gè)問(wèn)題,現(xiàn)在有一個(gè)提案,放松對(duì)標(biāo)簽?zāi)0謇锩娴淖址D(zhuǎn)義的限制。如果遇到不合法的字符串轉(zhuǎn)義,就返回undefined,而不是報(bào)錯(cuò),并且從raw屬性上面可以得到原始字符串。注意,這種對(duì)字符串轉(zhuǎn)義的放松,只在標(biāo)簽?zāi)0褰馕鲎址畷r(shí)生效,不是標(biāo)簽?zāi)0宓膱?chǎng)合,依然會(huì)報(bào)錯(cuò)。

四,正則的擴(kuò)展

4.1 RegExp 構(gòu)造函數(shù)

在 ES5 中,RegExp構(gòu)造函數(shù)的參數(shù)有兩種情況。第一種情況是,參數(shù)是字符串,這時(shí)第二個(gè)參數(shù)表示正則表達(dá)式的修飾符(flag); 第二種情況是,參數(shù)是一個(gè)正則表示式,這時(shí)會(huì)返回一個(gè)原有正則表達(dá)式的拷貝,但不允許此時(shí)使用第二個(gè)參數(shù)添加修飾符,否則會(huì)報(bào)錯(cuò)。

ES6 改變了。如果RegExp第一個(gè)參數(shù)是一個(gè)正則對(duì)象,那么可以使用第二個(gè)參數(shù)指定修飾符。而且,返回的正則表達(dá)式會(huì)忽略原有的正則表達(dá)式的修飾符,只使用新指定的修飾符。

4.2 字符串的正則方法

4.3 u 修飾符

ES6 對(duì)正則表達(dá)式添加了u修飾符,含義為“Unicode模式”,用來(lái)正確處理大于\uFFFF的 Unicode 字符。一旦加上u修飾符號(hào),就會(huì)修改下面這些正則表達(dá)式的行為。

var s = '??';

/^.$/.test(s) // false
/^.$/u.test(s) // true

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true

/??{2}/.test('????') // false
/??{2}/u.test('????') // true

利用這一點(diǎn),可以寫(xiě)出一個(gè)正確返回字符串長(zhǎng)度的函數(shù)。

function codePointLength(text) {
  var result = text.match(/[\s\S]/gu);
  return result ? result.length : 0;
}

還可以用spread判斷:

function length(str) {
  return [...str].length;
}

4.4 y 修飾符

ES6 還為正則表達(dá)式添加了y修飾符,叫做“粘連”(sticky)修飾符。

y修飾符的作用與g修飾符類(lèi)似,也是全局匹配,后一次匹配都從上一次匹配成功的下一個(gè)位置開(kāi)始。不同之處在于,g修飾符只要剩余位置中存在匹配就可,而y修飾符確保匹配必須從剩余的第一個(gè)位置開(kāi)始,這也就是“粘連”的涵義。

y修飾符的設(shè)計(jì)本意,就是讓頭部匹配的標(biāo)志^在全局匹配中都有效。

4.5 sticky 屬性

與y修飾符相匹配,ES6 的正則對(duì)象多了sticky屬性,表示是否設(shè)置了y修飾符。

var r = /hello\d/y;
r.sticky // true

4.6 flags 屬性

ES6 為正則表達(dá)式新增了flags屬性,會(huì)返回正則表達(dá)式的修飾符。

// ES5 的 source 屬性
// 返回正則表達(dá)式的正文
/abc/ig.source
// "abc"

// ES6 的 flags 屬性
// 返回正則表達(dá)式的修飾符
/abc/ig.flags
// 'gi'

4.7 s 修飾符

點(diǎn)(.)是一個(gè)特殊字符,代表任意的單個(gè)字符,但是行終止符(line terminator character)除外。但是,很多時(shí)候我們希望匹配的是任意單個(gè)字符,這時(shí)有一種變通的寫(xiě)法。/foo[^]bar/.test('foo\nbar') // true。
這種解決方案畢竟不太符合直覺(jué),所以現(xiàn)在有一個(gè)提案,引入/s修飾符,使得.可以匹配任意單個(gè)字符。/foo.bar/s.test('foo\nbar') // true。

這被稱(chēng)為dotAll模式,即點(diǎn)(dot)代表一切字符。所以,正則表達(dá)式還引入了一個(gè)dotAll屬性,返回一個(gè)布爾值,表示該正則表達(dá)式是否處在dotAll模式。

4.8 后行斷言

JavaScript 語(yǔ)言的正則表達(dá)式,只支持先行斷言(lookahead)和先行否定斷言(negative lookahead),不支持后行斷言(lookbehind)和后行否定斷言(negative lookbehind)。目前,有一個(gè)提案,引入后行斷言,V8 引擎4.9版已經(jīng)支持。

4.9 Unicode 屬性類(lèi)

目前,有一個(gè)提案,引入了一種新的類(lèi)的寫(xiě)法\p{...}
\P{...},允許正則表達(dá)式匹配符合 Unicode 某種屬性的所有字符。

4.10 具名組匹配

正則表達(dá)式使用圓括號(hào)進(jìn)行組匹配。const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
上面代碼中,正則表達(dá)式里面有三組圓括號(hào)。使用exec方法,就可以將這三組匹配結(jié)果提取出來(lái)。

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

組匹配的一個(gè)問(wèn)題是,每一組的匹配含義不容易看出來(lái),而且只能用數(shù)字序號(hào)引用,要是組的順序變了,引用的時(shí)候就必須修改序號(hào)。
現(xiàn)在有一個(gè)“具名組匹配”(Named Capture Groups)的提案,允許為每一個(gè)組匹配指定一個(gè)名字,既便于閱讀代碼,又便于引用。

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

如果具名組沒(méi)有匹配,那么對(duì)應(yīng)的groups對(duì)象屬性會(huì)是undefined。

解構(gòu)賦值和替換
有了具名組匹配以后,可以使用解構(gòu)賦值直接從匹配結(jié)果上為變量賦值。

引用
如果要在正則表達(dá)式內(nèi)部引用某個(gè)“具名組匹配”,可以使用\k<組名>的寫(xiě)法。數(shù)字引用(\1)依然有效。

五,數(shù)值的擴(kuò)展

5.1 二進(jìn)制和八進(jìn)制表示法

ES6 提供了二進(jìn)制和八進(jìn)制數(shù)值的新的寫(xiě)法,分別用前綴0b(或0B)和0o(或0O)表示。從 ES5 開(kāi)始,在嚴(yán)格模式之中,八進(jìn)制就不再允許使用前綴0表示,ES6 進(jìn)一步明確,要使用前綴0o表示。ES5中沒(méi)有二進(jìn)制的定義。

5.2 Number.isFinite(), Number.isNaN()

ES6 在Number對(duì)象上,新提供了Number.isFinite()和Number.isNaN()兩個(gè)方法。ES5中有g(shù)lobal的傳統(tǒng)的全局方法isFinite()和isNaN()。
區(qū)別在于,傳統(tǒng)方法先調(diào)用Number()將非數(shù)值的值轉(zhuǎn)為數(shù)值,再進(jìn)行判斷,而這兩個(gè)新方法只對(duì)數(shù)值有效,Number.isFinite()對(duì)于非數(shù)值一律返回false, Number.isNaN()只有對(duì)于NaN才返回true,非NaN一律返回false。

5.3 Number.parseInt(), Number.parseFloat()

ES6 將全局方法parseInt()和parseFloat(),移植到Number對(duì)象上面,行為完全保持不變。這樣做的目的,是逐步減少全局性方法,使得語(yǔ)言逐步模塊化。

5.4 Number.isInteger()

Number.isInteger()用來(lái)判斷一個(gè)值是否為整數(shù)。需要注意的是,在 JavaScript 內(nèi)部,整數(shù)和浮點(diǎn)數(shù)是同樣的儲(chǔ)存方法,所以3和3.0被視為同一個(gè)值。

5.5 Number.EPSILON

ES6 在Number對(duì)象上面,新增一個(gè)極小的常量Number.EPSILON。根據(jù)規(guī)格,它表示1與大于1的最小浮點(diǎn)數(shù)之間的差。

Number.EPSILON可以用來(lái)設(shè)置“能夠接受的誤差范圍”。比如,誤差范圍設(shè)為2的-50次方(即Number.EPSILON * Math.pow(2, 2)),即如果兩個(gè)浮點(diǎn)數(shù)的差小于這個(gè)值,我們就認(rèn)為這兩個(gè)浮點(diǎn)數(shù)相等。

5.6 安全整數(shù)和 Number.isSafeInteger()

JavaScript 能夠準(zhǔn)確表示的整數(shù)范圍在-253到253之間(不含兩個(gè)端點(diǎn)),超過(guò)這個(gè)范圍,無(wú)法精確表示這個(gè)值。ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER這兩個(gè)常量,用來(lái)表示這個(gè)范圍的上下限。

Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true
Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// true

Number.isSafeInteger()則是用來(lái)判斷一個(gè)整數(shù)是否落在這個(gè)范圍之內(nèi)。實(shí)際使用這個(gè)函數(shù)時(shí),需要注意。驗(yàn)證運(yùn)算結(jié)果是否落在安全整數(shù)的范圍內(nèi),不要只驗(yàn)證運(yùn)算結(jié)果,而要同時(shí)驗(yàn)證參與運(yùn)算的每個(gè)值。

5.7 Math對(duì)象的擴(kuò)展

Math.trunc() Math.sign() Math.cbrt() Math.clz32() Math.imul() Math.fround() Math.hypot()
對(duì)數(shù)方法(1) Math.expm1()(2)Math.log1p()(3)Math.log10()(4)Math.log2()
ES6新增了6個(gè)雙曲函數(shù)方法。Math.sinh(x)等

5.8 Math.signbit()

目前,有一個(gè)提案,引入了Math.signbit() 方法判斷一個(gè)數(shù)的符號(hào)位是否設(shè)置了。

5.9 指數(shù)運(yùn)算符

ES2016 新增了一個(gè)指數(shù)運(yùn)算符()。指數(shù)運(yùn)算符可以與等號(hào)結(jié)合,形成一個(gè)新的賦值運(yùn)算符(=)。注意,在 V8 引擎中,指數(shù)運(yùn)算符與Math.pow的實(shí)現(xiàn)不相同,對(duì)于特別大的運(yùn)算結(jié)果,兩者會(huì)有細(xì)微的差異。

5.10 Integer 數(shù)據(jù)類(lèi)型

JavaScript 所有數(shù)字都保存成64位浮點(diǎn)數(shù),這決定了整數(shù)的精確程度只能到53個(gè)二進(jìn)制位。大于這個(gè)范圍的整數(shù),JavaScript 是無(wú)法精確表示的。
現(xiàn)在有一個(gè)提案,引入了新的數(shù)據(jù)類(lèi)型 Integer(整數(shù)),來(lái)解決這個(gè)問(wèn)題。整數(shù)類(lèi)型的數(shù)據(jù)只用來(lái)表示整數(shù),沒(méi)有位數(shù)的限制,任何位數(shù)的整數(shù)都可以精確表示。

1n + 2n // 3n
0b1101n // 二進(jìn)制
0o777n // 八進(jìn)制
0xFFn // 十六進(jìn)制

Integer 類(lèi)型不能與 Number 類(lèi)型進(jìn)行混合運(yùn)算。相等運(yùn)算符(==)會(huì)改變數(shù)據(jù)類(lèi)型,也是不允許混合使用。精確相等運(yùn)算符(===)不會(huì)改變數(shù)據(jù)類(lèi)型,因此可以混合使用。

幾乎所有的 Number 運(yùn)算符都可以用在 Integer,但是有兩個(gè)除外:不帶符號(hào)的右移位運(yùn)算符>>>和一元的求正運(yùn)算符+,使用時(shí)會(huì)報(bào)錯(cuò)。

六,函數(shù)的擴(kuò)展

6.1 函數(shù)參數(shù)的默認(rèn)值

ES6 之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值,只能采用變通的方法。為了避免參數(shù)y賦值了但是對(duì)應(yīng)的布爾值為false的問(wèn)題,通常需要先判斷一下參數(shù)y是否被賦值,如果沒(méi)有,再等于默認(rèn)值。

function log(x, y) {
   if (typeof y === 'undefined') {
    y = 'World';
  }
  console.log(x, y);
}

ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫(xiě)在參數(shù)定義的后面。function Point(x = 0, y = 0)。 參數(shù)變量是默認(rèn)聲明的,所以不能用let或const再次聲明。

使用參數(shù)默認(rèn)值時(shí),函數(shù)不能有同名參數(shù)。

// 不報(bào)錯(cuò)
function foo(x, x, y) {
  // ...
}
// 報(bào)錯(cuò)
function foo(x, x, y = 1) {
  // ...
}

與解構(gòu)賦值默認(rèn)值結(jié)合使用

function foo({x, y = 5}) {
  console.log(x, y);
}

只有當(dāng)函數(shù)foo的參數(shù)是一個(gè)對(duì)象時(shí),變量x和y才會(huì)通過(guò)解構(gòu)賦值生成。如果函數(shù)foo調(diào)用時(shí)沒(méi)提供參數(shù),變量x和y就不會(huì)生成,從而報(bào)錯(cuò)。通過(guò)提供函數(shù)參數(shù)的默認(rèn)值,就可以避免這種情況。

參數(shù)默認(rèn)值的位置
通常情況下,定義了默認(rèn)值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因?yàn)檫@樣比較容易看出來(lái),到底省略了哪些參數(shù)。如果非尾部的參數(shù)設(shè)置默認(rèn)值,實(shí)際上這個(gè)參數(shù)是沒(méi)法省略的,除非顯式輸入undefined。如果傳入undefined,將觸發(fā)該參數(shù)等于默認(rèn)值,null則沒(méi)有這個(gè)效果。

函數(shù)的 length 屬性
指定了默認(rèn)值以后,函數(shù)的length屬性,將返回沒(méi)有指定默認(rèn)值的參數(shù)個(gè)數(shù)。也就是說(shuō),指定了默認(rèn)值后,length屬性將失真。

這是因?yàn)閘ength屬性的含義是,該函數(shù)預(yù)期傳入的參數(shù)個(gè)數(shù)。某個(gè)參數(shù)指定默認(rèn)值以后,預(yù)期傳入的參數(shù)個(gè)數(shù)就不包括這個(gè)參數(shù)了。同理,后文的 rest 參數(shù)也不會(huì)計(jì)入length屬性。如果設(shè)置了默認(rèn)值的參數(shù)不是尾參數(shù),那么length屬性也不再計(jì)入后面的參數(shù)了。

作用域

一旦設(shè)置了參數(shù)的默認(rèn)值,函數(shù)進(jìn)行聲明初始化時(shí),參數(shù)會(huì)形成一個(gè)單獨(dú)的作用域(context)。等到初始化結(jié)束,這個(gè)作用域就會(huì)消失。這種語(yǔ)法行為,在不設(shè)置參數(shù)默認(rèn)值時(shí),是不會(huì)出現(xiàn)的。

var x = 1;
function f(x, y = x) {
  console.log(y);
}
f(2) // 2

上面代碼中,參數(shù)y的默認(rèn)值等于變量x。調(diào)用函數(shù)f時(shí),參數(shù)形成一個(gè)單獨(dú)的作用域。在這個(gè)作用域里面,默認(rèn)值變量x指向第一個(gè)參數(shù)x,而不是全局變量x,所以輸出是2。

例子2,

let x = 1;
function f(y = x) {
  let x = 2;
  console.log(y);
}
f() // 1

如果此時(shí),全局變量x不存在,就會(huì)報(bào)錯(cuò)。

應(yīng)用

function throwIfMissing() {
  throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}
foo()
// Error: Missing parameter

利用參數(shù)默認(rèn)值,可以指定某一個(gè)參數(shù)不得省略,如果省略就拋出一個(gè)錯(cuò)誤。注意函數(shù)名throwIfMissing之后有一對(duì)圓括號(hào),這表明參數(shù)的默認(rèn)值不是在定義時(shí)執(zhí)行,而是在運(yùn)行時(shí)執(zhí)行。如果參數(shù)已經(jīng)賦值,默認(rèn)值中的函數(shù)就不會(huì)運(yùn)行。

另外一個(gè)應(yīng)用,可以將參數(shù)默認(rèn)值設(shè)為undefined,表明這個(gè)參數(shù)是可以省略的。

6.2 rest 參數(shù)

ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對(duì)象了。rest 參數(shù)搭配的變量是一個(gè)數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。

arguments對(duì)象不是數(shù)組,而是一個(gè)類(lèi)似數(shù)組的對(duì)象。所以為了使用數(shù)組的方法,必須使用Array.prototype.slice.call先將其轉(zhuǎn)為數(shù)組。rest 參數(shù)就不存在這個(gè)問(wèn)題,它就是一個(gè)真正的數(shù)組,數(shù)組特有的方法都可以使用。

注意,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個(gè)參數(shù)),否則會(huì)報(bào)錯(cuò)。函數(shù)的length屬性,不包括 rest 參數(shù)。

6.3 嚴(yán)格模式

從 ES5 開(kāi)始,函數(shù)內(nèi)部可以設(shè)定為嚴(yán)格模式。
ES2016 做了一點(diǎn)修改,規(guī)定只要函數(shù)參數(shù)使用了默認(rèn)值、解構(gòu)賦值、或者擴(kuò)展運(yùn)算符,那么函數(shù)內(nèi)部就不能顯式設(shè)定為嚴(yán)格模式,否則會(huì)報(bào)錯(cuò)。

這樣規(guī)定的原因是,函數(shù)內(nèi)部的嚴(yán)格模式,同時(shí)適用于函數(shù)體和函數(shù)參數(shù)。但是,函數(shù)執(zhí)行的時(shí)候,先執(zhí)行函數(shù)參數(shù),然后再執(zhí)行函數(shù)體。這樣就有一個(gè)不合理的地方,只有從函數(shù)體之中,才能知道參數(shù)是否應(yīng)該以嚴(yán)格模式執(zhí)行,但是參數(shù)卻應(yīng)該先于函數(shù)體執(zhí)行。

兩種方法可以規(guī)避這種限制。第一種是設(shè)定全局性的嚴(yán)格模式,這是合法的。第二種是把函數(shù)包在一個(gè)無(wú)參數(shù)的立即執(zhí)行函數(shù)里面。

6.4 name 屬性

函數(shù)的name屬性,返回該函數(shù)的函數(shù)名。這個(gè)屬性早就被瀏覽器廣泛支持,但是直到 ES6,才將其寫(xiě)入了標(biāo)準(zhǔn)。

需要注意的是,ES6 對(duì)這個(gè)屬性的行為做出了一些修改。傳入匿名函數(shù)的時(shí)候:

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

如果將一個(gè)具名函數(shù)賦值給一個(gè)變量,則 ES5 和 ES6 的name屬性都返回這個(gè)具名函數(shù)原本的名字。
Function構(gòu)造函數(shù)返回的函數(shù)實(shí)例,name屬性的值為anonymous。bind返回的函數(shù),name屬性值會(huì)加上bound前綴。

6.5 箭頭函數(shù)

如果箭頭函數(shù)的代碼塊部分多于一條語(yǔ)句,就要使用大括號(hào)將它們括起來(lái),并且使用return語(yǔ)句返回。var sum = (num1, num2) => { return num1 + num2; }
由于大括號(hào)被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個(gè)對(duì)象,必須在對(duì)象外面加上括號(hào)(),否則會(huì)報(bào)錯(cuò)。

如果箭頭函數(shù)只有一行語(yǔ)句,且不需要返回值,可以采用下面的寫(xiě)法,就不用寫(xiě)大括號(hào)了。let fn = () => void doesNotReturn();

箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用。

const full = ({ first, last }) => first + ' ' + last;

// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}

箭頭函數(shù)有幾個(gè)使用注意點(diǎn)。

(1)函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。

(2)不可以當(dāng)作構(gòu)造函數(shù),也就是說(shuō),不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤。

(3)不可以使用arguments對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用,可以用 rest 參數(shù)代替。同理super、new.target不能用。

(4)不可以使用yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)。

this指向的固定化,并不是因?yàn)榧^函數(shù)內(nèi)部有綁定this的機(jī)制,實(shí)際原因是箭頭函數(shù)根本沒(méi)有自己的this,導(dǎo)致內(nèi)部的this就是外層代碼塊的this。正是因?yàn)樗鼪](méi)有this,所以也就不能用作構(gòu)造函數(shù)。由于箭頭函數(shù)沒(méi)有自己的this,所以當(dāng)然也就不能用call()、apply()、bind()這些方法去改變this的指向。

(this部分這里參見(jiàn)教程的代碼例子,很豐富。)

6.6 綁定 this

ES7提出了“函數(shù)綁定”(function bind)運(yùn)算符,用來(lái)取代call 、apply、bind
調(diào)用。雖然該語(yǔ)法還是ES7的一個(gè)提案,但是Babel轉(zhuǎn)碼器已經(jīng)支持。

函數(shù)綁定運(yùn)算符是并排的兩個(gè)冒號(hào)(::),雙冒號(hào)左邊是一個(gè)對(duì)象,右邊是一個(gè)函數(shù)。該運(yùn)算符會(huì)自動(dòng)將左邊的對(duì)象,作為上下文環(huán)境(即this對(duì)象),綁定到右邊的函數(shù)上面。

6.7 尾調(diào)用優(yōu)化

尾調(diào)用(Tail Call) 是函數(shù)式編程的一個(gè)重要概念,本身非常簡(jiǎn)單,一句話就能說(shuō)清楚,就是指某個(gè)函數(shù)的最后一步是調(diào)用另一個(gè)函數(shù)。

function f(x){
  return g(x);
}

我們知道,函數(shù)調(diào)用會(huì)在內(nèi)存形成一個(gè)“調(diào)用記錄”,又稱(chēng)“調(diào)用幀”(call frame),保存調(diào)用位置和內(nèi)部變量等信息。如果在函數(shù)A的內(nèi)部調(diào)用函數(shù)B,那么在A的調(diào)用幀上方,還會(huì)形成一個(gè)B的調(diào)用幀。等到B運(yùn)行結(jié)束,將結(jié)果返回到A,B的調(diào)用幀才會(huì)消失。所有的調(diào)用幀,就形成一個(gè)“調(diào)用?!保╟all stack)。

尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用幀,因?yàn)檎{(diào)用位置、內(nèi)部變量等信息都不會(huì)再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用幀,取代外層函數(shù)的調(diào)用幀就可以了。這就叫做“尾調(diào)用優(yōu)化”(Tail call optimization)。注意,只有不再用到外層函數(shù)的內(nèi)部變量,內(nèi)層函數(shù)的調(diào)用幀才會(huì)取代外層函數(shù)的調(diào)用幀,否則就無(wú)法進(jìn)行“尾調(diào)用優(yōu)化”。

函數(shù)調(diào)用自身,稱(chēng)為遞歸。如果尾調(diào)用自身,就稱(chēng)為尾遞歸。遞歸非常耗費(fèi)內(nèi)存,因?yàn)樾枰瑫r(shí)保存成千上百個(gè)調(diào)用幀,很容易發(fā)生“棧溢出”錯(cuò)誤(stack overflow)。但對(duì)于尾遞歸來(lái)說(shuō),由于只存在一個(gè)調(diào)用幀,所以永遠(yuǎn)不會(huì)發(fā)生“棧溢出”錯(cuò)誤。由此可見(jiàn),“尾調(diào)用優(yōu)化”對(duì)遞歸操作意義重大,所以一些函數(shù)式編程語(yǔ)言將其寫(xiě)入了語(yǔ)言規(guī)格。ES6 是如此,第一次明確規(guī)定,所有 ECMAScript 的實(shí)現(xiàn),都必須部署“尾調(diào)用優(yōu)化”。

遞歸函數(shù)的改寫(xiě)。做到這一點(diǎn)的方法,就是把所有用到的內(nèi)部變量改寫(xiě)成函數(shù)的參數(shù)。這樣做的缺點(diǎn)就是不太直觀,第一眼很難看出來(lái)。兩個(gè)方法可以解決這個(gè)問(wèn)題。方法一是在尾遞歸函數(shù)之外,再提供一個(gè)正常形式的函數(shù)。第二種方法就簡(jiǎn)單多了,就是采用 ES6 的函數(shù)默認(rèn)值。
總結(jié)一下,遞歸本質(zhì)上是一種循環(huán)操作。純粹的函數(shù)式編程語(yǔ)言沒(méi)有循環(huán)操作命令,所有的循環(huán)都用遞歸實(shí)現(xiàn),這就是為什么尾遞歸對(duì)這些語(yǔ)言極其重要。

ES6 的尾調(diào)用優(yōu)化只在嚴(yán)格模式下開(kāi)啟,正常模式是無(wú)效的。
關(guān)于尾遞歸優(yōu)化的實(shí)現(xiàn)看教程代碼,有點(diǎn)小復(fù)雜。

6.8 函數(shù)參數(shù)的尾逗號(hào)

ES2017 允許函數(shù)的最后一個(gè)參數(shù)有尾逗號(hào)(trailing comma)。
此前,函數(shù)定義和調(diào)用時(shí),都不允許最后一個(gè)參數(shù)后面出現(xiàn)逗號(hào)。
這樣的規(guī)定也使得,函數(shù)參數(shù)與數(shù)組和對(duì)象的尾逗號(hào)規(guī)則,保持一致了。但是 JSON 不支持尾后逗號(hào)。

6.9 catch 語(yǔ)句的參數(shù)

目前,有一個(gè)提案,允許try...catch結(jié)構(gòu)中的catch語(yǔ)句調(diào)用時(shí)不帶有參數(shù)。這個(gè)提案跟參數(shù)有關(guān),也放在這一章介紹。

七,數(shù)組的擴(kuò)展

7.1 擴(kuò)展運(yùn)算符(spread)

...是擴(kuò)展運(yùn)算符。它好比 rest 參數(shù)的逆運(yùn)算,將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分隔的參數(shù)序列。Rest操作符一般用在函數(shù)參數(shù)的聲明中,而Spread用在函數(shù)的調(diào)用中。

由于擴(kuò)展運(yùn)算符可以展開(kāi)數(shù)組,所以不再需要apply方法,將數(shù)組轉(zhuǎn)為函數(shù)的參數(shù)了。

擴(kuò)展運(yùn)算符的應(yīng)用
(1)復(fù)制數(shù)組
數(shù)組是復(fù)合的數(shù)據(jù)類(lèi)型,直接復(fù)制的話,只是復(fù)制了指向底層數(shù)據(jù)結(jié)構(gòu)的指針,而不是克隆一個(gè)全新的數(shù)組。
ES5 只能用變通方法來(lái)復(fù)制數(shù)組。

const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]

擴(kuò)展運(yùn)算符提供了復(fù)制數(shù)組的簡(jiǎn)便寫(xiě)法。

const a1 = [1, 2];
// 寫(xiě)法一
const a2 = [...a1];
// 寫(xiě)法二
const [...a2] = a1;

(2)合并數(shù)組

擴(kuò)展運(yùn)算符提供了數(shù)組合并的新寫(xiě)法。

// ES5的合并數(shù)組
arr1.concat(arr2, arr3);
// ES6的合并數(shù)組
[...arr1, ...arr2, ...arr3]

(3)與解構(gòu)賦值結(jié)合

// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list

如果將擴(kuò)展運(yùn)算符用于數(shù)組賦值,只能放在參數(shù)的最后一位,否則會(huì)報(bào)錯(cuò)。

(4)字符串
有一個(gè)重要的好處,那就是能夠正確識(shí)別四個(gè)字節(jié)的 Unicode 字符。

(5)實(shí)現(xiàn)了 Iterator 接口的對(duì)象
任何 Iterator 接口的對(duì)象(參閱 Iterator 一章),都可以用擴(kuò)展運(yùn)算符轉(zhuǎn)為真正的數(shù)組。如let array = [...nodeList];。對(duì)于那些沒(méi)有部署 Iterator 接口的類(lèi)似數(shù)組的對(duì)象,擴(kuò)展運(yùn)算符就無(wú)法將其轉(zhuǎn)為真正的數(shù)組。

(6)Map 和 Set 結(jié)構(gòu),Generator 函數(shù)

7.2 Array.from()

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

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

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

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

實(shí)際應(yīng)用中,常見(jiàn)的類(lèi)似數(shù)組的對(duì)象是DOM操作返回的NodeList集合,以及函數(shù)內(nèi)部的arguments對(duì)象。Array.from都可以將它們轉(zhuǎn)為真正的數(shù)組。

值得提醒的是,擴(kuò)展運(yùn)算符(...)也可以將某些數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為數(shù)組。擴(kuò)展運(yùn)算符背后調(diào)用的是遍歷器接口(Symbol.iterator),如果一個(gè)對(duì)象沒(méi)有部署這個(gè)接口,就無(wú)法轉(zhuǎn)換。

Array.from方法還支持類(lèi)似數(shù)組的對(duì)象。所謂類(lèi)似數(shù)組的對(duì)象,本質(zhì)特征只有一點(diǎn),即必須有l(wèi)ength屬性。因此,任何有l(wèi)ength屬性的對(duì)象,都可以通過(guò)Array.from方法轉(zhuǎn)為數(shù)組,而此時(shí)擴(kuò)展運(yùn)算符就無(wú)法轉(zhuǎn)換。

// arguments對(duì)象
function foo() {
  const args = [...arguments];
}

// NodeList對(duì)象
[...document.querySelectorAll('div')]

Array.from還可以接受第二個(gè)參數(shù),作用類(lèi)似于數(shù)組的map方法,用來(lái)對(duì)每個(gè)元素進(jìn)行處理,將處理后的值放入返回的數(shù)組。

Array.from()的另一個(gè)應(yīng)用是,將字符串轉(zhuǎn)為數(shù)組,然后返回字符串的長(zhǎng)度。因?yàn)樗苷_處理各種Unicode字符,可以避免JavaScript將大于\uFFFF的Unicode字符,算作兩個(gè)字符的bug。

7.3 Array.of()

Array.of方法用于將一組值,轉(zhuǎn)換為數(shù)組。Array.of總是返回參數(shù)值組成的數(shù)組。如果沒(méi)有參數(shù),就返回一個(gè)空數(shù)組。Array.of基本上可以用來(lái)替代Array()或new Array(),并且不存在由于參數(shù)不同而導(dǎo)致的重載。它的行為非常統(tǒng)一。
這個(gè)方法的主要目的,是彌補(bǔ)數(shù)組構(gòu)造函數(shù)Array()的不足。因?yàn)閰?shù)個(gè)數(shù)的不同,會(huì)導(dǎo)致Array()的行為有差異。只有當(dāng)參數(shù)個(gè)數(shù)不少于2個(gè)時(shí),Array()才會(huì)返回由參數(shù)組成的新數(shù)組。

Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

7.4 數(shù)組實(shí)例的 copyWithin()

Array.prototype.copyWithin(target, start = 0, end = this.length)

7.5 數(shù)組實(shí)例的 find() 和 findIndex()

[1, 4, -5, 10].find((n) => n < 0)
// -5
[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10

數(shù)組實(shí)例的findIndex方法的用法與find方法非常類(lèi)似,返回第一個(gè)符合條件的數(shù)組成員的位置,如果所有成員都不符合條件,則返回-1。這兩個(gè)方法都可以接受第二個(gè)參數(shù),用來(lái)綁定回調(diào)函數(shù)的this對(duì)象。另外,這兩個(gè)方法都可以發(fā)現(xiàn)NaN,彌補(bǔ)了數(shù)組的IndexOf方法的不足。indexof它內(nèi)部使用嚴(yán)格相等運(yùn)算符(===)進(jìn)行判斷,這會(huì)導(dǎo)致對(duì)NaN的誤判。

[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0

7.6 數(shù)組實(shí)例的fill()

fill方法使用給定值,填充一個(gè)數(shù)組。fill方法還可以接受第二個(gè)和第三個(gè)參數(shù),用于指定填充的起始位置和結(jié)束位置。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

7.7 數(shù)組實(shí)例的 entries(),keys() 和 values()

它們都返回一個(gè)遍歷器對(duì)象(詳見(jiàn)《Iterator》一章),可以用for...of循環(huán)進(jìn)行遍歷。如果不使用for...of循環(huán),可以手動(dòng)調(diào)用遍歷器對(duì)象的next方法,進(jìn)行遍歷。

7.8 數(shù)組實(shí)例的 includes()

與字符串的includes方法類(lèi)似,參數(shù)負(fù)的情況稍有不同。

7.9 數(shù)組的空位

數(shù)組的空位指,數(shù)組的某一個(gè)位置沒(méi)有任何值。比如,Array構(gòu)造函數(shù)返回的數(shù)組都是空位。Array(3) // [, , ,]
注意,空位不是undefined,一個(gè)位置的值等于undefined,依然是有值的??瘴皇菦](méi)有任何值,in運(yùn)算符可以說(shuō)明這一點(diǎn)。

ES5 對(duì)空位的處理,已經(jīng)很不一致了,大多數(shù)情況下會(huì)忽略空位。
forEach(), filter(), every() 和some()都會(huì)跳過(guò)空位。
map()會(huì)跳過(guò)空位,但會(huì)保留這個(gè)值
join()和toString()會(huì)將空位視為undefined,而undefined和null會(huì)被處理成空字符串。
ES6 則是明確將空位轉(zhuǎn)為undefined。
由于空位的處理規(guī)則非常不統(tǒng)一,所以建議避免出現(xiàn)空位。

八,對(duì)象的擴(kuò)展

8.1 屬性的簡(jiǎn)潔表示法

ES6 允許直接寫(xiě)入變量和函數(shù),作為對(duì)象的屬性和方法。這樣的書(shū)寫(xiě)更加簡(jiǎn)潔。

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
function f(x, y) {
  return {x, y};
}

// 等同于

function f(x, y) {
  return {x: x, y: y};
}

除了屬性簡(jiǎn)寫(xiě),方法也可以簡(jiǎn)寫(xiě)。

const o = {
  method() {
    return "Hello!";
  }
};

// 等同于

const o = {
  method: function() {
    return "Hello!";
  }
};

如果某個(gè)方法的值是一個(gè) Generator 函數(shù),前面需要加上星號(hào)。

8.2 屬性名表達(dá)式

但是,如果使用字面量方式定義對(duì)象(使用大括號(hào)),在 ES5 中只能使用方法一(標(biāo)識(shí)符)定義屬性。

var obj = {
  foo: true,
  abc: 123
};

ES6 允許字面量定義對(duì)象時(shí),用方法二(表達(dá)式)作為對(duì)象的屬性名,即把表達(dá)式放在方括號(hào)內(nèi)。

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};

表達(dá)式還可以用于定義方法名。注意,屬性名表達(dá)式與簡(jiǎn)潔表示法,不能同時(shí)使用,會(huì)報(bào)錯(cuò)。

注意,屬性名表達(dá)式如果是一個(gè)對(duì)象,默認(rèn)情況下會(huì)自動(dòng)將對(duì)象轉(zhuǎn)為字符串[object Object],這一點(diǎn)要特別小心。

8.3 方法的 name 屬性

函數(shù)的name屬性,返回函數(shù)名。對(duì)象方法也是函數(shù),因此也有name屬性。如果對(duì)象的方法使用了取值函數(shù)(getter)和存值函數(shù)(setter),則name屬性不是在該方法上面,而是該方法的屬性的描述對(duì)象的get和set屬性上面,返回值是方法名前加上get和set。

const obj = {
  get foo() {},
  set foo(x) {}
};

obj.foo.name
// TypeError: Cannot read property 'name' of undefined

const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

descriptor.get.name // "get foo"
descriptor.set.name // "set foo"

有兩種特殊情況:bind方法創(chuàng)造的函數(shù),name屬性返回bound加上原函數(shù)的名字;Function構(gòu)造函數(shù)創(chuàng)造的函數(shù),name屬性返回anonymous。如果對(duì)象的方法是一個(gè) Symbol 值,那么name屬性返回的是這個(gè) Symbol 值的描述。

8.4 Object.is()

ES5 比較兩個(gè)值是否相等,只有兩個(gè)運(yùn)算符:相等運(yùn)算符(==)和嚴(yán)格相等運(yùn)算符(===)。它們都有缺點(diǎn),前者會(huì)自動(dòng)轉(zhuǎn)換數(shù)據(jù)類(lèi)型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一種運(yùn)算,在所有環(huán)境中,只要兩個(gè)值是一樣的,它們就應(yīng)該相等。
ES6 提出“Same-value equality”(同值相等)算法,用來(lái)解決這個(gè)問(wèn)題。Object.is就是部署這個(gè)算法的新方法。

8.5 Object.assign()

Object.assign方法用于對(duì)象的合并,將源對(duì)象(source)的所有可枚舉屬性,復(fù)制到目標(biāo)對(duì)象(target)。Object.assign方法的第一個(gè)參數(shù)是目標(biāo)對(duì)象,后面的參數(shù)都是源對(duì)象。Object.assign拷貝的屬性是有限制的,只拷貝源對(duì)象的自身屬性(不拷貝繼承屬性),也不拷貝不可枚舉的屬性(enumerable: false)。屬性名為 Symbol 值的屬性,也會(huì)被Object.assign拷貝。

注意,如果目標(biāo)對(duì)象與源對(duì)象有同名屬性,或多個(gè)源對(duì)象有同名屬性,則后面的屬性會(huì)覆蓋前面的屬性。
注意點(diǎn)
(1)淺拷貝(2)同名屬性的替換(3)數(shù)組處理時(shí)視為對(duì)象 (4)取值函數(shù)的處理將求值后再?gòu)?fù)制。

應(yīng)用:(1)為對(duì)象添加屬性(2)為對(duì)象添加方法(3)克隆對(duì)象(4)合并多個(gè)對(duì)象(5)為屬性指定默認(rèn)值

8.6 屬性的可枚舉性和遍歷

可枚舉性
對(duì)象的每個(gè)屬性都有一個(gè)描述對(duì)象(Descriptor),用來(lái)控制該屬性的行為。ES5中 Object.getOwnPropertyDescriptor 方法可以獲取該屬性的描述對(duì)象。描述對(duì)象的enumerable屬性,稱(chēng)為”可枚舉性“,如果該屬性為false,就表示某些操作會(huì)忽略當(dāng)前屬性。

目前,有四個(gè)操作會(huì)忽略enumerable為false的屬性。

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

另外,ES6 規(guī)定,所有 Class 的原型的方法都是不可枚舉的??偟膩?lái)說(shuō),操作中引入繼承的屬性會(huì)讓問(wèn)題復(fù)雜化,大多數(shù)時(shí)候,我們只關(guān)心對(duì)象自身的屬性。所以,盡量不要用for...in循環(huán),而用Object.keys()代替。

屬性的遍歷
ES6 一共有5種方法可以遍歷對(duì)象的屬性。

(1)for...in
(2)Object.keys(obj
(3)Object.getOwnPropertyNames(obj)(4)Object.getOwnPropertySymbols(obj)
(5)Reflect.ownKeys(obj)

以上的5種方法遍歷對(duì)象的鍵名,都遵守同樣的屬性遍歷的次序規(guī)則。

首先遍歷所有數(shù)值鍵,按照數(shù)值升序排列。
其次遍歷所有字符串鍵,按照加入時(shí)間升序排列。
最后遍歷所有 Symbol 鍵,按照加入時(shí)間升序排列。

8.7 Object.getOwnPropertyDescriptors()

ES2017 引入了Object.getOwnPropertyDescriptors方法,返回指定對(duì)象所有自身屬性(非繼承屬性)的描述對(duì)象。

該方法的引入目的,主要是為了解決Object.assign()無(wú)法正確拷貝get屬性和set屬性的問(wèn)題。這是因?yàn)镺bject.assign方法總是拷貝一個(gè)屬性的值,而不會(huì)拷貝它背后的賦值方法或取值方法。這時(shí),Object.getOwnPropertyDescriptors方法配合Object.defineProperties方法,就可以實(shí)現(xiàn)正確拷貝。

Object.getOwnPropertyDescriptors方法的另一個(gè)用處,是配合Object.create方法,將對(duì)象屬性克隆到一個(gè)新對(duì)象。這屬于淺拷貝。

另外,Object.getOwnPropertyDescriptors方法可以實(shí)現(xiàn)一個(gè)對(duì)象繼承另一個(gè)對(duì)象。

8.8 _proto_屬性, Object.setPrototypeOf(), Object.getPrototypeOf()

JavaScript 語(yǔ)言的對(duì)象繼承是通過(guò)原型鏈實(shí)現(xiàn)的。ES6 提供了更多原型對(duì)象的操作方法。

proto屬性 該屬性沒(méi)有寫(xiě)入 ES6 的正文,而是寫(xiě)入了附錄,原因是proto前后的雙下劃線,說(shuō)明它本質(zhì)上是一個(gè)內(nèi)部屬性,而不是一個(gè)正式的對(duì)外的 API,只是由于瀏覽器廣泛支持,才被加入了 ES6。標(biāo)準(zhǔn)明確規(guī)定,只有瀏覽器必須部署這個(gè)屬性,其他運(yùn)行環(huán)境不一定需要部署,而且新的代碼最好認(rèn)為這個(gè)屬性是不存在的。使用下面的Object.setPrototypeOf()(寫(xiě)操作)、Object.getPrototypeOf()(讀操作)、Object.create()(生成操作)代替。

Object.setPrototypeOf() Object.setPrototypeOf方法的作用與proto相同,用來(lái)設(shè)置一個(gè)對(duì)象的prototype對(duì)象,返回參數(shù)對(duì)象本身。它是 ES6 正式推薦的設(shè)置原型對(duì)象的方法。

Object.getPrototypeOf() 該方法與Object.setPrototypeOf方法配套,用于讀取一個(gè)對(duì)象的原型對(duì)象。

8.9 super 關(guān)鍵字

我們知道,this關(guān)鍵字總是指向函數(shù)所在的當(dāng)前對(duì)象,ES6 又新增了另一個(gè)類(lèi)似的關(guān)鍵字super,指向當(dāng)前對(duì)象的原型對(duì)象。注意,super關(guān)鍵字表示原型對(duì)象時(shí),只能用在對(duì)象的方法之中,用在其他地方都會(huì)報(bào)錯(cuò)。目前,只有對(duì)象方法的簡(jiǎn)寫(xiě)法可以讓 JavaScript 引擎確認(rèn),定義的是對(duì)象的方法。

const obj = {
  find() {
    return super.foo;
  }
};

JavaScript 引擎內(nèi)部,super.foo等同于Object.getPrototypeOf(this).foo(屬性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

8.10 Object.keys(),Object.values(),Object.entries()

ES5 引入了Object.keys方法,返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。

ES2017 引入了跟Object.keys 配套的Object.values 和Object.entries ,作為遍歷一個(gè)對(duì)象的補(bǔ)充手段,供for...of 循環(huán)使用。

Object.entries的基本用途是遍歷對(duì)象的屬性。Object.entries方法的另一個(gè)用處是,將對(duì)象轉(zhuǎn)為真正的Map結(jié)構(gòu)。

8.11 對(duì)象的擴(kuò)展運(yùn)算符(spread)

ES2017 將這個(gè)運(yùn)算符引入了對(duì)象。
(1)解構(gòu)賦值

單純的解構(gòu)賦值,所以可以讀取對(duì)象繼承的屬性;擴(kuò)展運(yùn)算符的解構(gòu)賦值,只能讀取對(duì)象o自身的屬性。

(2)擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符(...)用于取出參數(shù)對(duì)象的所有可遍歷屬性,拷貝到當(dāng)前對(duì)象之中。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

這等同于使用Object.assign方法。

let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);

擴(kuò)展運(yùn)算符的參數(shù)對(duì)象之中,如果有取值函數(shù)get,這個(gè)函數(shù)是會(huì)執(zhí)行的。

8.12 Null 傳導(dǎo)運(yùn)算符

編程實(shí)務(wù)中,如果讀取對(duì)象內(nèi)部的某個(gè)屬性,往往需要判斷一下該對(duì)象是否存在。比如,要讀取message.body.user.firstName
,安全的寫(xiě)法是寫(xiě)成下面這樣。const firstName = (message && message.body && message.body.user && message.body.user.firstName) || 'default';

這樣的層層判斷非常麻煩,因此現(xiàn)在有一個(gè)提案,引入了“Null 傳導(dǎo)運(yùn)算符”(null propagation operator)?.
,簡(jiǎn)化上面的寫(xiě)法。const firstName = message?.body?.user?.firstName || 'default';

上面代碼有三個(gè)?.運(yùn)算符,只要其中一個(gè)返回null或undefined,就不再往下運(yùn)算,而是返回undefined。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第5章 引用類(lèi)型(返回首頁(yè)) 本章內(nèi)容 使用對(duì)象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類(lèi)型 使用基本類(lèi)型...
    大學(xué)一百閱讀 3,676評(píng)論 0 4
  • 1.屬性的簡(jiǎn)潔表示法 允許直接寫(xiě)入變量和函數(shù) 上面代碼表明,ES6 允許在對(duì)象之中,直接寫(xiě)變量。這時(shí),屬性名為變量...
    雨飛飛雨閱讀 1,262評(píng)論 0 3
  • 哪位作者說(shuō)過(guò),沒(méi)有方向, 當(dāng)你努力就是白搭。 在過(guò)去的這些年頭里我沒(méi)想過(guò)沒(méi)策劃過(guò)我的人生, 我抱著這樣一個(gè)心態(tài)...
    青檀Darling閱讀 325評(píng)論 0 1
  • 有一個(gè)朋友叫靜靜,說(shuō)心里話,我挺想靜靜。 周末一場(chǎng)小雪,下了相當(dāng)于沒(méi)下,希望通過(guò)以雪蓋霾的人們嘆了口氣。 于是我開(kāi)...
    coco劉閱讀 916評(píng)論 0 0
  • 我為2016做了生涯四度規(guī)劃,選擇了“學(xué)習(xí)、助人、健康、愛(ài)好”四個(gè)關(guān)鍵詞來(lái)發(fā)展自己,可我任性地跑偏了。 2016年...
    Amy蕾閱讀 585評(píng)論 0 2

友情鏈接更多精彩內(nèi)容