摘要:字符串的擴(kuò)展字符的表示法允許采用形式表示一個(gè)字符,其中表示字符的碼點(diǎn)。返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。使用和這兩個(gè)常量,用來表示這個(gè)范圍的上下限。對(duì)于那些無法用個(gè)二進(jìn)制位精確表示的小數(shù),方法返回最接近這個(gè)小數(shù)的單精度浮點(diǎn)數(shù)。
字符串的擴(kuò)展 字符的 Unicode 表示法
JavaScript 允許采用uxxxx形式表示一個(gè)字符,其中xxxx表示字符的 Unicode 碼點(diǎn)。這種表示法只限于碼點(diǎn)在u0000~uFFFF之間的字符。超出這個(gè)范圍的字符,必須用兩個(gè)雙字節(jié)的形式表示。ES6中只要將碼點(diǎn)放入大括號(hào),就能正確解讀該字符。
"uD842uDFB7" // "?" "u{20BB7}" // "?"
大括號(hào)表示法與 UTF-16 編碼是等價(jià)的。
"z" === "z" // true "172" === "z" // true "x7A" === "z" // true "u007A" === "z" // true "u{7A}" === "z" // true字符串方法
JavaScript內(nèi)部,字符以UTF-16的格式儲(chǔ)存,每個(gè)字符固定為2個(gè)字節(jié)。對(duì)于那些需要4個(gè)字節(jié)儲(chǔ)存的字符(Unicode碼點(diǎn)大于0xFFFF的字符),JavaScript會(huì)認(rèn)為它們是兩個(gè)字符,字符串長度會(huì)誤判為2。
var s = "?"; s.length // 2 s.charAt(0) // "" s.charAt(1) // "" s.charCodeAt(0) // 55362 s.charCodeAt(1) // 57271 s.codePointAt(0) // 134071 codePointAt方法在第一個(gè)字符上,正確地識(shí)別了“?” s.codePointAt(1) // 57271 第二個(gè)字符是“?”的后兩個(gè)字節(jié) String.fromCharCode(0x20BB7) // "?" 最高位2被舍棄了,最后返回碼點(diǎn)U+0BB7對(duì)應(yīng)的字符,而不是碼點(diǎn)U+20BB7對(duì)應(yīng)的字符。 String.fromCodePoint(0x20BB7) // "?" "?".at(0) // "?"
對(duì)于Unicode碼點(diǎn)大于0xFFFF的字符:
charAt:無法讀取整個(gè)字符。該方法不能識(shí)別碼點(diǎn)大于0xFFFF的字符。
charCodeAt:只能分別返回前兩個(gè)字節(jié)和后兩個(gè)字節(jié)的值。
fromCharCode:不能識(shí)別大于0xFFFF的碼點(diǎn)。
codePointAt:能夠正確處理4個(gè)字節(jié)儲(chǔ)存的字符,返回一個(gè)字符的碼點(diǎn)。codePointAt方法是測(cè)試一個(gè)字符由兩個(gè)字節(jié)還是由四個(gè)字節(jié)組成的最簡(jiǎn)單方法。
fromCodePoint:可以識(shí)別0xFFFF的字符。
at: 可以識(shí)別Unicode編號(hào)大于0xFFFF的字符,返回正確的字符。這個(gè)方法可以通過墊片庫實(shí)現(xiàn)。
注意,fromCodePoint方法定義在String對(duì)象上,而codePointAt方法定義在字符串的實(shí)例對(duì)象上。
其他方法normalize():用來將字符的不同表示方法統(tǒng)一為同樣的形式
includes():返回布爾值,表示是否找到了參數(shù)字符串。
startsWith():返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。
endsWith():返回布爾值,表示參數(shù)字符串是否在源字符串的尾部。
padStart():用于頭部補(bǔ)全。常見用途是為數(shù)值補(bǔ)全指定位數(shù)和提示字符串格式。
"1".padStart(10, "0") // "0000000001" "12".padStart(10, "YYYY-MM-DD") // "YYYY-MM-12"
padEnd():用于尾部補(bǔ)全。
repeat():返回一個(gè)新字符串,表示將原字符串重復(fù)n次。參數(shù)如果是小數(shù),會(huì)被取整。
參數(shù)是負(fù)數(shù)或者Infinity,會(huì)報(bào)錯(cuò)。
參數(shù)是0到-1之間的小數(shù),則等同于0,這是因?yàn)闀?huì)先進(jìn)行取整運(yùn)算。
參數(shù)0到-1之間的小數(shù),取整以后等于-0,repeat視同為0。
參數(shù)NaN等同于0。
repeat的參數(shù)是字符串,則會(huì)先轉(zhuǎn)換成數(shù)字。
"na".repeat(2.9) // "nana" "na".repeat(Infinity)// RangeError "na".repeat(NaN) // "" "na".repeat("na") // "" "na".repeat("3") // "nanana"字符串的遍歷器接口
ES6為字符串添加了遍歷器接口(詳見《Iterator》一章),使得字符串可以被for...of循環(huán)遍歷。該遍歷可以識(shí)別大于0xFFFF的碼點(diǎn),傳統(tǒng)的for循環(huán)無法識(shí)別這樣的碼點(diǎn)。
var text = String.fromCodePoint(0x20BB7); for (let i = 0; i < text.length; i++) { console.log(text[i]); } // " " // " " //for循環(huán)會(huì)認(rèn)為它包含兩個(gè)字符(都不可打?。?for (let i of text) { console.log(i); } // "?" //for...of循環(huán)會(huì)正確識(shí)別出這一個(gè)字符模板字符串
模板字符串用反引號(hào)(`)標(biāo)識(shí)。它可以當(dāng)作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。在模板字符串中需要使用反引號(hào),則前面要用反斜杠轉(zhuǎn)義。使用模板字符串表示多行字符串,所有的空格和縮進(jìn)都會(huì)被保留在輸出之中。
// 普通字符串 `In JavaScript " " is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` // 字符串中嵌入變量 `Hello ${name}, how are you ${time}?`
模板字符串中嵌入變量,需要將變量名寫在${}之中。大括號(hào)內(nèi)部可以放入任意的JavaScript表達(dá)式,可以進(jìn)行運(yùn)算,以及引用對(duì)象屬性和調(diào)用函數(shù)。如果大括號(hào)中的值不是字符串,將按照一般的規(guī)則轉(zhuǎn)為字符串。比如,大括號(hào)中是一個(gè)對(duì)象,將默認(rèn)調(diào)用對(duì)象的toString方法。
如果模板字符串中的變量沒有聲明,將報(bào)錯(cuò)。
// 變量place沒有聲明 var msg = `Hello, ${place}`; // 報(bào)錯(cuò)
由于模板字符串的大括號(hào)內(nèi)部,就是執(zhí)行JavaScript代碼,因此如果大括號(hào)內(nèi)部是一個(gè)字符串,將會(huì)原樣輸出。
`Hello ${"World"}` // "Hello World"標(biāo)簽?zāi)0?/b>
模板字符串緊跟在一個(gè)函數(shù)名后面,該函數(shù)將被調(diào)用來處理這個(gè)模板字符串。即模板字符串就是該函數(shù)的參數(shù)。標(biāo)簽?zāi)0迨呛瘮?shù)調(diào)用的一種特殊形式?!皹?biāo)簽”指的就是函數(shù),緊跟在后面的模板字符串就是它的參數(shù)。
//模板字符里面有變量,會(huì)將模板字符串先處理成多個(gè)參數(shù),再調(diào)用函數(shù)。 var a = 5; var b = 10; tag`Hello ${ a + b } world ${ a * b }`; // 等同于 tag(["Hello ", " world ", ""], 15, 50);
模板處理函數(shù)的第一個(gè)參數(shù)(模板字符串?dāng)?shù)組),還有一個(gè)raw屬性。
console.log(`123`) //123 console.log`123` // ["123", raw: Array[1]]
上面代碼中,第二個(gè)console.log接受的參數(shù),實(shí)際上是一個(gè)數(shù)組。該數(shù)組有一個(gè)raw屬性,保存的是轉(zhuǎn)義后的原字符串。
String.raw方法,往往用來充當(dāng)模板字符串的處理函數(shù),返回一個(gè)斜杠都被轉(zhuǎn)義(即斜杠前面再加一個(gè)斜杠)的字符串,對(duì)應(yīng)于替換變量后的模板字符串。如果原字符串的斜杠已經(jīng)轉(zhuǎn)義,那么String.raw不會(huì)做任何處理。
String.raw`Hi ${2+3}!`; // "Hi 5!" String.raw`Hi ` // "Hi "
String.raw方法也可以作為正常的函數(shù)使用。這時(shí),它的第一個(gè)參數(shù),應(yīng)該是一個(gè)具有raw屬性的對(duì)象,且raw屬性的值應(yīng)該是一個(gè)數(shù)組。
String.raw({ raw: "test" }, 0, 1, 2); // "t0e1s2t" // 等同于 String.raw({ raw: ["t","e","s","t"] }, 0, 1, 2);正則的擴(kuò)展 RegExp構(gòu)造函數(shù)
var regex = new RegExp("xyz", "i"); // 等價(jià)于 var regex = /xyz/i; // 等價(jià)于 var regex = new RegExp(/xyz/i);
ES5中以下寫法會(huì)報(bào)錯(cuò)。ES6可以使用第二個(gè)參數(shù)指定修飾符,新指定的修飾符會(huì)覆蓋原有的正則表達(dá)式的修飾符。
var regex = new RegExp(/xyz/ig, "i"); //原有正則對(duì)象的修飾符是ig,它會(huì)被第二個(gè)參數(shù)i覆蓋.u修飾符
ES6對(duì)正則表達(dá)式添加了u修飾符,含義為“Unicode模式”,用來正確處理大于uFFFF的Unicode字符。也就是說,會(huì)正確處理四個(gè)字節(jié)的UTF-16編碼。
/^uD83D/u.test("uD83DuDC2A") // false /^uD83D/.test("uD83DuDC2A") // true
上面的代碼中,uD83DuDC2A是一個(gè)四字節(jié)的UTF-16編碼,代表一個(gè)字符。不加“u”,會(huì)按 ES5 將其識(shí)別為2個(gè)字符,加了“u”之后,會(huì)按 ES6 將其正確識(shí)別為一個(gè)字符。
以下幾種情況就必須加上“u”才能正確識(shí)別:
.在正則表達(dá)式中表示除行終止符(換行符(n),回車符(r),行分隔符,段分隔符)外的任意單個(gè)字符,S表示匹配所有不是空格的字符。他們均正確識(shí)別碼點(diǎn)大于0xFFFF的Unicode字符,必須加上u修飾符才能正確識(shí)別。
/^S$/.test("?") // false /^S$/u.test("?") // true
ES6新增了使用大括號(hào)表示Unicode字符,這種表示法在正則表達(dá)式中必須加上u修飾符,才能識(shí)別。否則大括號(hào)會(huì)被解讀為量詞。
/^u{3}$/.test("uuu") // true 被解讀為量詞 /^u{3}$/u.test("uuu") // false 被解讀為Unicode表達(dá)式
有些Unicode字符的編碼不同,但是字型很相近,需要加u才能識(shí)別。比如,u004B與u212A都是大寫的K。
/[a-z]/i.test("u212A") // false 該行代碼不加u修飾符,就無法識(shí)別非規(guī)范的K字符 /[a-z]/iu.test("u212A") // truey 修飾符
y修飾符的作用與g修飾符類似,也是全局匹配,后一次匹配都從上一次匹配成功的下一個(gè)位置開始。不同之處在于,g修飾符只要剩余位置中存在匹配就可,而y修飾符確保匹配必須從剩余的第一個(gè)位置開始。y修飾符號(hào)就是讓頭部匹配的標(biāo)志^在全局匹配中都有效。
var s = "aaa_aa_a"; var r1 = /a+/g; var r2 = /a+/y; r1.exec(s) // ["aaa"] r1.exec(s) // ["aa"] r2.exec(s) // ["aaa"] r2.exec(s) // null 第一次執(zhí)行后,剩余字符串是_aa_a,y修飾符要求匹配必須從頭部開始,所以返回null
在split方法中使用y修飾符,原字符串必須以分隔符開頭。這也意味著,只要匹配成功,數(shù)組的第一個(gè)成員肯定是空字符串。
sticky屬性ES6的正則對(duì)象多了sticky屬性,表示是否設(shè)置了y修飾符。
var r = /hellod/y; r.sticky // trueflags屬性
ES6為正則表達(dá)式新增了flags屬性,會(huì)返回正則表達(dá)式的修飾符。
// ES5的source屬性 返回正則表達(dá)式的正文 /abc/ig.source // "abc" // ES6的flags屬性 返回正則表達(dá)式的修飾符 /abc/ig.flags // "gi"先行斷言
JavaScript 語言的正則表達(dá)式,只支持先行斷言(lookahead)和先行否定斷言(negative lookahead)。
”先行斷言“指的是,x只有在y前面才匹配,必須寫成/x(?=y)/。比如,只匹配百分號(hào)之前的數(shù)字,要寫成/d+(?=%)/?!毕刃蟹穸〝嘌浴爸傅氖牵?strong>x只有不在y前面才匹配,必須寫成/x(?!y)/。比如,只匹配不在百分號(hào)之前的數(shù)字,要寫成/d+(?!%)/。
ES6 提供了二進(jìn)制和八進(jìn)制數(shù)值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示??墒褂肗umber方法將0b和0o前綴的字符串?dāng)?shù)值轉(zhuǎn)為十進(jìn)制。
Number("0b111") // 7新增方法
Number.isFinite():用來檢查一個(gè)數(shù)值是否為有限的(finite),對(duì)于非數(shù)值一律返回false。
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():用來檢查一個(gè)值是否為NaN。
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
ES6將全局方法parseInt()和parseFloat(),移植到Number對(duì)象上面,行為完全保持不變。
Number.parseInt === parseInt // true
Number.isInteger():用來判斷一個(gè)值是否為整數(shù)。需要注意的是,在JavaScript內(nèi)部,整數(shù)和浮點(diǎn)數(shù)是同樣的儲(chǔ)存方法,所以3和3.0被視為同一個(gè)值。
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isInteger("15") // false Number.isInteger(true) // false
Number.isSafeInteger():用來判斷一個(gè)整數(shù)是否落在Number.MAX_SAFE_INTEGER與Number.MIN_SAFE_INTEGER范圍之內(nèi)。使用該函數(shù)時(shí),需注意不僅要驗(yàn)證運(yùn)算結(jié)果是否落在安全整數(shù)的范圍內(nèi),還要同時(shí)驗(yàn)證參與運(yùn)算的每個(gè)值,否則很可能得到錯(cuò)誤結(jié)果。
Number.isSafeInteger(3) // true Number.isSafeInteger(1.2) // false新增常量
Number.EPSILON:極小常量。用于為浮點(diǎn)數(shù)計(jì)算,設(shè)置一個(gè)誤差范圍。
JavaScript能夠準(zhǔn)確表示的整數(shù)范圍在-2^53到2^53之間(不含兩個(gè)端點(diǎn)),超過這個(gè)范圍,無法精確表示這個(gè)值。ES6使用Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER這兩個(gè)常量,用來表示這個(gè)范圍的上下限。
Math.pow(2, 53) // 9007199254740992 Number.MAX_SAFE_INTEGER === 9007199254740991 // true Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER // true Number.MIN_SAFE_INTEGER === -9007199254740991 // trueMath對(duì)象的擴(kuò)展
ES6在Math對(duì)象上新增了17個(gè)與數(shù)學(xué)相關(guān)的方法。所有這些方法都是靜態(tài)方法,只能在Math對(duì)象上調(diào)用。
Math.trunc:用于去除一個(gè)數(shù)的小數(shù)部分,返回整數(shù)部分。對(duì)于非數(shù)值,Math.trunc內(nèi)部使用Number方法將其先轉(zhuǎn)為數(shù)值。對(duì)于空值和無法截取整數(shù)的值,返回NaN。
Math.trunc("123.456") // 123 Math.trunc(NaN); // NaN Math.trunc("foo"); // NaN Math.trunc(); // NaN
Math.cbrt():用于計(jì)算一個(gè)數(shù)的立方根。對(duì)于非數(shù)值,Math.cbrt方法內(nèi)部先使用Number方法將其轉(zhuǎn)為數(shù)值。
Math.sign():用來判斷一個(gè)數(shù)到底是正數(shù)、負(fù)數(shù)、還是零。正數(shù)返回+1,負(fù)數(shù)返回-1,0返回0,-0返回-0,其他值,返回NaN。
Math.clz32():JavaScript的整數(shù)使用32位二進(jìn)制形式。Math.clz32()返回一個(gè)數(shù)的32位無符號(hào)整數(shù)形式有多少個(gè)前導(dǎo)0。對(duì)于小數(shù),只考慮其整數(shù)部分。對(duì)于空值或其他類型的值,會(huì)將它們先轉(zhuǎn)為數(shù)值,然后再計(jì)算。
//1000的二進(jìn)制形式是0b1111101000,一共有10位,所以32位之中有22個(gè)前導(dǎo)0。 Math.clz32(1000) // 22
Math.imul:返回兩個(gè)數(shù)以32位帶符號(hào)整數(shù)形式相乘的結(jié)果,返回的也是一個(gè)32位的帶符號(hào)整數(shù)。
Math.fround:返回一個(gè)數(shù)的單精度浮點(diǎn)數(shù)形式。對(duì)于整數(shù)來說,Math.fround方法返回結(jié)果一樣。對(duì)于那些無法用64個(gè)二進(jìn)制位精確表示的小數(shù),Math.fround方法返回最接近這個(gè)小數(shù)的單精度浮點(diǎn)數(shù)。
Math.fround(1) // 1 Math.fround(1.337) // 1.3370000123977661 Math.fround(1.5) // 1.5
Math.hypot:返回所有參數(shù)的平方和的平方根。如果參數(shù)不是數(shù)值,Math.hypot方法會(huì)將其轉(zhuǎn)為數(shù)值。只要有一個(gè)參數(shù)無法轉(zhuǎn)為數(shù)值,就會(huì)返回NaN。
Math.sign():用來判斷一個(gè)值的正負(fù),但是如果參數(shù)是-0,它會(huì)返回-0。
新增對(duì)數(shù)方法Math.expm1():
Math.log1p(x):返回1 + x的自然對(duì)數(shù),即Math.log(1 + x)。如果x小于-1,返回NaN。
Math.log10(x):返回以10為底的x的對(duì)數(shù)。如果x小于0,則返回NaN。
Math.log2(x):返回以2為底的x的對(duì)數(shù)。如果x小于0,則返回NaN。
新增三角函數(shù)方法Math.sinh(x):返回x的雙曲正弦(hyperbolic sine)
Math.cosh(x):返回x的雙曲余弦(hyperbolic cosine)
Math.tanh(x): 返回x的雙曲正切(hyperbolic tangent)
Math.asinh(x): 返回x的反雙曲正弦(inverse hyperbolic sine)
Math.acosh(x): 返回x的反雙曲余弦(inverse hyperbolic cosine)
Math.atanh(x): 返回x的反雙曲正切(inverse hyperbolic tangent)
指數(shù)運(yùn)算符ES2016 新增了一個(gè)指數(shù)運(yùn)算符(**)。
2 ** 3 // 8 b **= 3; // 等同于 b = b * b * b;
在 V8 引擎中,指數(shù)運(yùn)算符與Math.pow的實(shí)現(xiàn)不相同,對(duì)于特別大的運(yùn)算結(jié)果,兩者會(huì)有細(xì)微的差異。
Math.pow(99, 99) // 3.697296376497263e+197 99 ** 99 // 3.697296376497268e+197數(shù)組的擴(kuò)展 Array.from()
Array.from方法用于將兩類對(duì)象轉(zhuǎn)為真正的數(shù)組:類似數(shù)組的對(duì)象(即有l(wèi)ength屬性的對(duì)象)和可遍歷(iterable)的對(duì)象(包括ES6新增的數(shù)據(jù)結(jié)構(gòu)Set和Map)。如果參數(shù)是一個(gè)真正的數(shù)組,Array.from會(huì)返回一個(gè)一模一樣的新數(shù)組。
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"]
擴(kuò)展運(yùn)算符(...)也可以將某些數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為數(shù)組。擴(kuò)展運(yùn)算符背后調(diào)用的是遍歷器接口(Symbol.iterator)。
// arguments對(duì)象 function foo() { var args = [...arguments]; } // NodeList對(duì)象 [...document.querySelectorAll("div")]
Array.from還可以接受第二個(gè)參數(shù),作用類似于數(shù)組的map方法,用來對(duì)每個(gè)元素進(jìn)行處理,將處理后的值放入返回的數(shù)組。
Array.from(arrayLike, x => x * x); // 等同于 Array.from(arrayLike).map(x => x * x); Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9]
Array.from()能正確處理各種Unicode字符,因此可以將將字符串轉(zhuǎn)為數(shù)組,然后正確返回字符串的長度。
function countSymbols(string) { return Array.from(string).length; }Array.of()
Array.of總是返回參數(shù)值組成的數(shù)組。如果沒有參數(shù),就返回一個(gè)空數(shù)組。Array.of基本上可以用來替代Array()或new Array()。
Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]數(shù)組的新增實(shí)例方法
copyWithin() :在當(dāng)前數(shù)組內(nèi)部,將指定位置的成員復(fù)制到其他位置(會(huì)覆蓋原有成員),然后返回當(dāng)前數(shù)組。Array.prototype.copyWithin(target, start = 0, end = this.length)。三個(gè)參數(shù)都應(yīng)該是數(shù)值,如果不是,會(huì)自動(dòng)轉(zhuǎn)為數(shù)值。會(huì)修改當(dāng)前數(shù)組。
find() :用于找出第一個(gè)符合條件的數(shù)組成員。它的參數(shù)是一個(gè)回調(diào)函數(shù),所有數(shù)組成員依次執(zhí)行該回調(diào)函數(shù),直到找出第一個(gè)返回值為true的成員,然后返回該成員。如果沒有符合條件的成員,則返回undefined??梢园l(fā)現(xiàn)NaN。
findIndex() :與find方法非常類似,返回第一個(gè)符合條件的數(shù)組成員的位置,如果所有成員都不符合條件,則返回-1??梢园l(fā)現(xiàn)NaN。
[NaN].indexOf(NaN) // -1 [NaN].findIndex(y => Object.is(NaN, y)) // 0 //indexOf方法無法識(shí)別數(shù)組的NaN成員,但是findIndex方法可以借助Object.is方法做到。
fill() :使用給定值,填充一個(gè)數(shù)組。fill方法用于空數(shù)組的初始化非常方便。數(shù)組中已有的元素,會(huì)被全部抹去。
["a", "b", "c"].fill(7) // [7, 7, 7] new Array(3).fill(7) // [7, 7, 7] ["a", "b", "c"].fill(7, 1, 2) // ["a", 7, "c"] fill方法從1號(hào)位開始,向原數(shù)組填充7,到2號(hào)位之前結(jié)束。
includes():返回一個(gè)布爾值,表示某個(gè)數(shù)組是否包含給定的值,與字符串的includes方法類似。該方法屬于ES7,但Babel轉(zhuǎn)碼器已經(jīng)支持。
[1, 2, NaN].includes(NaN); // true [NaN].indexOf(NaN) // -1 [NaN].includes(NaN) // true //indexof會(huì)導(dǎo)致對(duì)NaN的誤判,但是includes可以正確判斷NaN。遍歷數(shù)組
entries(),keys()和values()均用于遍歷數(shù)組。它們都返回一個(gè)遍歷器對(duì)象(Iterator),可以用for...of循環(huán)進(jìn)行遍歷,唯一的區(qū)別是keys()是對(duì)鍵名的遍歷、values()是對(duì)鍵值的遍歷,entries()是對(duì)鍵值對(duì)的遍歷。
for (let [index, elem] of ["a", "b"].entries()) { console.log(index, elem); } // 0 "a" // 1 "b"
不使用for...of循環(huán),可以手動(dòng)調(diào)用遍歷器對(duì)象的next方法,進(jìn)行遍歷。
let letter = ["a", "b", "c"]; let entries = letter.entries(); console.log(entries.next().value); // [0, "a"] console.log(entries.next().value); // [1, "b"]數(shù)組的空位
數(shù)組的空位指,數(shù)組的某一個(gè)位置沒有任何值。空位不是undefined,一個(gè)位置的值等于undefined,依然是有值的??瘴皇菦]有任何值。
Array(3) // [, , ,] Array(3)返回一個(gè)具有3個(gè)空位的數(shù)組。對(duì)空位的處理
ES5大多數(shù)情況下會(huì)忽略空位。
forEach(), filter(), every() 和some()都會(huì)跳過空位。
map()會(huì)跳過空位,但會(huì)保留這個(gè)值。
join()和toString()會(huì)將空位視為undefined,而undefined和null會(huì)被處理成空字符串。
// filter方法 ["a",,"b"].filter(x => true) // ["a","b"] // map方法 [,"a"].map(x => 1) // [,1] // join方法 [,"a",undefined,null].join("#") // "#a##"
ES6明確將空位轉(zhuǎn)為undefined。
Array.from方法會(huì)將數(shù)組的空位,轉(zhuǎn)為undefined。
擴(kuò)展運(yùn)算符(...)將空位轉(zhuǎn)為undefined。
copyWithin()會(huì)連空位一起拷貝。
fill()會(huì)將空位視為正常的數(shù)組位置。
for...of循環(huán)也會(huì)遍歷空位。
entries()、keys()、values()、find()和findIndex()會(huì)將空位處理成undefined。
Array.from(["a",,"b"]) // [ "a", undefined, "b" ] [...["a",,"b"]] // [ "a", undefined, "b" ] new Array(3).fill("a") // ["a","a","a"]
由于空位的處理規(guī)則非常不統(tǒng)一,所以建議避免出現(xiàn)空位。
函數(shù)的擴(kuò)展 函數(shù)參數(shù)的默認(rèn)值ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義的后面。參數(shù)變量是默認(rèn)聲明的,所以不能用let或const再次聲明。使用參數(shù)默認(rèn)值時(shí),函數(shù)不能有同名參數(shù)。
//參數(shù)變量x是默認(rèn)聲明的,在函數(shù)體中,不能用let或const再次聲明,否則會(huì)報(bào)錯(cuò)。 function foo(x = 5) { let x = 1; // error const x = 2; // error }
如果參數(shù)默認(rèn)值是變量,那么參數(shù)就不是傳值的,而是每次都重新計(jì)算默認(rèn)值表達(dá)式的值。也就是說,參數(shù)默認(rèn)值是惰性求值的。
let x = 99; function foo(p = x + 1) { console.log(p); } foo() // 100 x = 100; foo() // 101 //代碼中,參數(shù)p的默認(rèn)值是x + 1。這時(shí),每次調(diào)用函數(shù)foo,都會(huì)重新計(jì)算x + 1,而不是默認(rèn)p等于 100。與解構(gòu)賦值默認(rèn)值結(jié)合使用
參數(shù)默認(rèn)值可以與解構(gòu)賦值的默認(rèn)值結(jié)合起來使用。
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined, 5 foo() // TypeError: Cannot read property "x" of undefined
只有當(dāng)函數(shù)foo的參數(shù)是一個(gè)對(duì)象時(shí),變量x和y才會(huì)通過解構(gòu)賦值而生成。如果函數(shù)foo調(diào)用時(shí)參數(shù)不是對(duì)象,變量x和y就不會(huì)生成,從而報(bào)錯(cuò)。如果參數(shù)對(duì)象沒有y屬性,y的默認(rèn)值5才會(huì)生效。
參數(shù)默認(rèn)值的位置通常情況下,定義了默認(rèn)值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。如果非尾部的參數(shù)設(shè)置默認(rèn)值,則調(diào)用時(shí)無法只省略該參數(shù),而不省略它后面的參數(shù),除非顯式輸入undefined。
function f(x = 1, y) { return [x, y]; } f(2) // [2, undefined]) f(, 1) // 報(bào)錯(cuò) f(undefined, 1) // [1, 1]函數(shù)的 length 屬性
函數(shù)的length屬性,將返回沒有指定默認(rèn)值的參數(shù)個(gè)數(shù)。設(shè)置了默認(rèn)值的參數(shù)不是尾參數(shù),那么length屬性也不再計(jì)入后面的參數(shù)。rest參數(shù)也不會(huì)計(jì)入length屬性。
(function (a, b, c = 5) {}).length // 2 (function(...args) {}).length // 0 (function (a, b = 1, c) {}).length // 1作用域
一旦設(shè)置了參數(shù)的默認(rèn)值,調(diào)用函數(shù)時(shí),參數(shù)會(huì)形成一個(gè)多帶帶的作用域(context)。這種語法行為,在不設(shè)置參數(shù)默認(rèn)值時(shí),是不會(huì)出現(xiàn)的。當(dāng)該多帶帶作用域里面默認(rèn)值是變量,且變量未定義,則指向外層的全局變量,若此時(shí)該全局變量不存在,就會(huì)報(bào)錯(cuò)。
let x = 1; function f(y = x) { let x = 2; console.log(y); } f() // 1
函數(shù)f調(diào)用時(shí),參數(shù)y = x形成一個(gè)多帶帶的作用域。這個(gè)作用域里面,變量x本身沒有定義,所以指向外層的全局變量x。函數(shù)調(diào)用時(shí),函數(shù)體內(nèi)部的新聲明局部變量x影響不到默認(rèn)值變量x。
rest參數(shù)rest 運(yùn)算符:將一個(gè)不定數(shù)量的參數(shù)表示為一個(gè)數(shù)組。
ES6 引入 rest 參數(shù)(形式為“...變量名”),用于獲取函數(shù)的多余參數(shù),rest 參數(shù)中的變量代表一個(gè)數(shù)組。注意,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個(gè)參數(shù)),否則會(huì)報(bào)錯(cuò)。函數(shù)的length屬性,不包括 rest 參數(shù)。
function f(a, ...b) { console.log(b); } f(2,3,4,5) //[3, 4, 5] // 報(bào)錯(cuò) function f(a, ...b, c) { // ... }擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符(spread)是三個(gè)點(diǎn)(...)。它好比 rest 參數(shù)的逆運(yùn)算,將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分隔的參數(shù)序列。
在某種程度上,rest運(yùn)算符和Spread運(yùn)算符(即擴(kuò)展運(yùn)算符)相反,Spread運(yùn)算符會(huì)“展開”元素使其變成多個(gè)元素,rest運(yùn)算符會(huì)收集多個(gè)元素和“壓縮”成一個(gè)單一的元素。
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5 // ES6的寫法 Math.max(...[14, 3, 77]) // 等同于 Math.max(14, 3, 77);擴(kuò)展運(yùn)算符的應(yīng)用
合并數(shù)組
// ES5 [1, 2].concat(more) // ES6 [1, 2, ...more]
將字符串轉(zhuǎn)為真正的數(shù)組。
[..."hello"] // [ "h", "e", "l", "l", "o" ]
該寫法能夠正確識(shí)別32位的Unicode字符。
"xuD83DuDE80y".length // 4 [..."xuD83DuDE80y"].length // 3 //JavaScript會(huì)將32位Unicode字符,識(shí)別為2個(gè)字符,采用擴(kuò)展運(yùn)算符就沒有這個(gè)問題。
擴(kuò)展運(yùn)算符內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的Iterator接口。所以任何Iterator接口的對(duì)象,都可以用擴(kuò)展運(yùn)算符轉(zhuǎn)為真正的數(shù)組。對(duì)于那些沒有部署Iterator接口的類似數(shù)組的對(duì)象,擴(kuò)展運(yùn)算符就無法將其轉(zhuǎn)為真正的數(shù)組。
嚴(yán)格模式從ES5開始,函數(shù)內(nèi)部可以設(shè)定為嚴(yán)格模式。
function doSomething(a, b) { "use strict"; // code }
《ECMAScript 2016標(biāo)準(zhǔ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í)行。因此,標(biāo)準(zhǔn)如此定義。
name 屬性函數(shù)的name屬性,返回該函數(shù)的函數(shù)名。將一個(gè)匿名函數(shù)賦值給一個(gè)變量,ES5 的name屬性,會(huì)返回空字符串,而 ES6 的name屬性會(huì)返回實(shí)際的函數(shù)名。如果將一個(gè)具名函數(shù)賦值給一個(gè)變量,則 ES5 和 ES6 的name屬性都返回這個(gè)具名函數(shù)原本的名字。
var f = function () {}; // ES5 f.name // "" // ES6 f.name // "f" const bar = function baz() {}; // ES5 bar.name // "baz" // ES6 bar.name // "baz"
Function構(gòu)造函數(shù)返回的函數(shù)實(shí)例,name屬性的值為anonymous。bind返回的函數(shù),name屬性值會(huì)加上bound前綴。
(new Function).name // "anonymous" function foo() {}; foo.bind({}).name // "bound foo"箭頭函數(shù)
ES6允許使用“箭頭”(=>)定義函數(shù)。如果箭頭函數(shù)不需要參數(shù)或需要多個(gè)參數(shù),就使用一個(gè)圓括號(hào)代表參數(shù)部分。如果箭頭函數(shù)的代碼塊部分多于一條語句,就要使用大括號(hào)將它們括起來。由于大括號(hào)被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個(gè)對(duì)象,必須在對(duì)象外面加上括號(hào)。箭頭函數(shù)可以嵌套。
var f = v => v; //等同于 var f = function(v) { return v; }; var f = () => 5; // 等同于 var f = function () { return 5 }; var getTempItem = id => ({ id: id, name: "Temp" });
箭頭函數(shù)使用注意點(diǎn):
函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。this指向的固定化,是因?yàn)榧^函數(shù)根本沒有自己的this,導(dǎo)致內(nèi)部的this就是外層代碼塊的this。正是因?yàn)樗鼪]有this,所以也就不能用作構(gòu)造函數(shù)。
不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤。
不可以使用arguments對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用,可以用rest參數(shù)代替。
不可以使用yield命令,因此箭頭函數(shù)不能用作Generator函數(shù)。
// ES6 function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } // ES5 function foo() { var _this = this; setTimeout(function () { console.log("id:", _this.id); }, 100); }
上面代碼中,轉(zhuǎn)換后的ES5版本清楚地說明了,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this。
除了this,以下三個(gè)變量在箭頭函數(shù)之中也是不存在的,指向外層函數(shù)的對(duì)應(yīng)變量:arguments、super、new.target。由于箭頭函數(shù)沒有自己的this,所以當(dāng)然也就不能用call()、apply()、bind()這些方法去改變this的指向。
(function() { return [ (() => this.x).bind({ x: "inner" })() ]; }).call({ x: "outer" }); // ["outer"] //上面代碼中,箭頭函數(shù)沒有自己的this,所以bind方法無效,內(nèi)部的this指向外部的this。“函數(shù)綁定”(function bind)運(yùn)算符
ES7提出了“函數(shù)綁定”(function bind)運(yùn)算符,用來取代call、apply、bind調(diào)用。雖然該語法還是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ù)上面。如果雙冒號(hào)左邊為空,右邊是一個(gè)對(duì)象的方法,則等于將該方法綁定在該對(duì)象上面。雙冒號(hào)運(yùn)算符返回的還是原對(duì)象,因此可以采用鏈?zhǔn)綄懛ā?/b>
foo::bar(...arguments); // 等同于 bar.apply(foo, arguments); var method = obj::obj.foo; // 等同于 var method = ::obj.foo;尾調(diào)用優(yōu)化
尾調(diào)用就是指某個(gè)函數(shù)運(yùn)行的最后一步是調(diào)用另一個(gè)函數(shù)。尾調(diào)用不一定出現(xiàn)在函數(shù)尾部,只要是最后一步操作即可。
function f(x){ return g(x); } //函數(shù)f的最后一步是調(diào)用函數(shù)g,這就叫尾調(diào)用。 function f(x){ return g(x) + 1; } //函數(shù)調(diào)用之后還有操作,不是尾調(diào)用。 function f(x){ g(x); } //上面的函數(shù)等同于下面的代碼,因此也不是尾調(diào)用。 //function f(x){ // g(x); // return undefined; //}尾調(diào)用優(yōu)化
函數(shù)調(diào)用會(huì)在內(nèi)存形成一個(gè)“調(diào)用記錄”,又稱“調(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ì)消失。如果函數(shù)B內(nèi)部還調(diào)用函數(shù)C,那就還有一個(gè)C的調(diào)用幀,以此類推。所有的調(diào)用幀,就形成一個(gè)“調(diào)用?!保╟all stack)。
function f() { let m = 1; let n = 2; return g(m + n); } f(); // 等同于 function f() { return g(3); } f(); // 等同于 g(3);
上面代碼中,如果函數(shù)g不是尾調(diào)用,函數(shù)f就需要保存內(nèi)部變量m和n的值、g的調(diào)用位置等信息。但由于調(diào)用g之后,函數(shù)f就結(jié)束了,所以執(zhí)行到最后一步,完全可以刪除 f(x) 的調(diào)用幀,只保留 g(3) 的調(diào)用幀。
這就叫做“尾調(diào)用優(yōu)化”(Tail call optimization),即只保留內(nèi)層函數(shù)的調(diào)用幀。如果所有函數(shù)都是尾調(diào)用,那么完全可以做到每次執(zhí)行時(shí),調(diào)用幀只有一項(xiàng),這將大大節(jié)省內(nèi)存。這就是“尾調(diào)用優(yōu)化”的意義。注意,只有不再用到外層函數(shù)的內(nèi)部變量,內(nèi)層函數(shù)的調(diào)用幀才會(huì)取代外層函數(shù)的調(diào)用幀,否則就無法進(jìn)行“尾調(diào)用優(yōu)化”。
尾遞歸函數(shù)調(diào)用自身,稱為遞歸。如果尾調(diào)用自身,就稱為尾遞歸。遞歸非常耗費(fèi)內(nèi)存,因?yàn)樾枰瑫r(shí)保存成千上百個(gè)調(diào)用幀,很容易發(fā)生“棧溢出”錯(cuò)誤(stack overflow)。但對(duì)于尾遞歸來說,由于只存在一個(gè)調(diào)用幀,所以永遠(yuǎn)不會(huì)發(fā)生“棧溢出”錯(cuò)誤。
function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120 計(jì)算n的階乘,最多需要保存n個(gè)調(diào)用記錄,復(fù)雜度 O(n) //改寫為尾遞歸 function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120 改寫成了尾遞歸,只保留一個(gè)調(diào)用記錄,復(fù)雜度 O(1) 。
由此可見,“尾調(diào)用優(yōu)化”對(duì)遞歸操作意義重大。ES6明確規(guī)定,所有ECMAScript的實(shí)現(xiàn),都必須部署“尾調(diào)用優(yōu)化”。這就是說,在ES6中,只要使用尾遞歸,就不會(huì)發(fā)生棧溢出,相對(duì)節(jié)省內(nèi)存。
遞歸函數(shù)的改寫尾遞歸的實(shí)現(xiàn),往往需要改寫遞歸函數(shù),確保最后一步只調(diào)用自身。做到這一點(diǎn)的方法,就是把所有用到的內(nèi)部變量改寫成函數(shù)的參數(shù)。
嚴(yán)格模式ES6的尾調(diào)用優(yōu)化只在嚴(yán)格模式下開啟,正常模式是無效的。
這是因?yàn)樵谡DJ较?,函?shù)內(nèi)部有兩個(gè)變量,可以跟蹤函數(shù)的調(diào)用棧。
func.arguments:返回調(diào)用時(shí)函數(shù)的參數(shù)。
func.caller:返回調(diào)用當(dāng)前函數(shù)的那個(gè)函數(shù)。
尾調(diào)用優(yōu)化發(fā)生時(shí),函數(shù)的調(diào)用棧會(huì)改寫,因此上面兩個(gè)變量就會(huì)失真。嚴(yán)格模式禁用這兩個(gè)變量,所以尾調(diào)用模式僅在嚴(yán)格模式下生效。
function restricted() { "use strict"; restricted.caller; // 報(bào)錯(cuò) restricted.arguments; // 報(bào)錯(cuò) } restricted();尾遞歸優(yōu)化的實(shí)現(xiàn)
正常模式下,或者那些不支持該功能的環(huán)境中,采用“循環(huán)”換掉“遞歸”,以減少調(diào)用棧。
//正常遞歸函數(shù) function sum(x, y) { if (y > 0) { return sum(x + 1, y - 1); } else { return x; } } sum(1, 100000) //
可以使用蹦床函數(shù)(trampoline)將遞歸執(zhí)行轉(zhuǎn)為循環(huán)執(zhí)行。
function trampoline(f) { while (f && f instanceof Function) { f = f(); } return f; }
上面就是蹦床函數(shù)的一個(gè)實(shí)現(xiàn),它接受一個(gè)函數(shù)f作為參數(shù)。只要f執(zhí)行后返回一個(gè)函數(shù),就繼續(xù)執(zhí)行。這里是返回一個(gè)函數(shù),然后執(zhí)行該函數(shù),而不是函數(shù)里面調(diào)用函數(shù),這樣就避免了遞歸執(zhí)行,從而就消除了調(diào)用棧過大的問題。
然后,要做的就是將原來的遞歸函數(shù),改寫為每一步返回另一個(gè)函數(shù)。
function sum(x, y) { if (y > 0) { return sum.bind(null, x + 1, y - 1); } else { return x; } } //sum函數(shù)的每次執(zhí)行,都會(huì)返回自身的另一個(gè)版本 trampoline(sum(1, 100000));//然后,用蹦床函數(shù)執(zhí)行sum,就不會(huì)發(fā)生調(diào)用棧溢出。函數(shù)參數(shù)的尾逗號(hào)
ES2017 允許函數(shù)的最后一個(gè)參數(shù)有尾逗號(hào)(trailing comma)。此前,函數(shù)定義和調(diào)用時(shí),都不允許最后一個(gè)參數(shù)后面出現(xiàn)逗號(hào)。
對(duì)象的擴(kuò)展 屬性的簡(jiǎn)潔表示法ES6 允許直接寫入變量和函數(shù),作為對(duì)象的屬性和方法。簡(jiǎn)潔寫法的屬性名總是字符串。
var foo = "bar"; var baz = {foo}; baz // {foo: "bar"} // 等同于 var baz = {foo: foo}; //ES6 允許在對(duì)象之中,直接寫變量。這時(shí),屬性名為變量名, 屬性值為變量的值。 var o = { method() { return "Hello!"; } }; // 等同于 var o = { method: function() { return "Hello!"; } };屬性名表達(dá)式
JavaScript語言定義對(duì)象的屬性,有兩種方法。方法一是直接用標(biāo)識(shí)符作為屬性名,方法二是用表達(dá)式作為屬性名,這時(shí)要將表達(dá)式放在方括號(hào)之內(nèi)。
// 方法一 obj.foo = true; // 方法二 obj["a" + "bc"] = 123;
如果使用字面量方式定義對(duì)象(使用大括號(hào)),在 ES5 中只能使用方法一(標(biāo)識(shí)符)定義屬性。但ES6 允許字面量定義對(duì)象時(shí),用方法二(表達(dá)式)作為對(duì)象的屬性名,即把表達(dá)式放在方括號(hào)內(nèi)。表達(dá)式還可以用于定義方法名。
let propKey = "foo"; let obj = { [propKey]: true, ["a" + "bc"]: 123 };
注意,屬性名表達(dá)式與簡(jiǎn)潔表示法,不能同時(shí)使用,會(huì)報(bào)錯(cuò)。
// 報(bào)錯(cuò) var foo = "bar"; var bar = "abc"; var baz = { [foo] }; // 正確 var foo = "bar"; var baz = { [foo]: "abc"};
注意,屬性名表達(dá)式如果是一個(gè)對(duì)象,默認(rèn)情況下會(huì)自動(dòng)將對(duì)象轉(zhuǎn)為字符串[object Object],這一點(diǎn)要特別小心。
const keyA = {a: 1}; const keyB = {b: 2}; const myObject = { [keyA]: "valueA", [keyB]: "valueB" }; myObject // Object {[object Object]: "valueB"} //[keyA]和[keyB]得到的都是[object Object],所以[keyB]會(huì)把[keyA]覆蓋掉,而myObject最后只有一個(gè)[object Object]屬性。方法的 name 屬性
對(duì)象方法的name屬性返回函數(shù)名(即方法名)。如果對(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 值的描述。
Object.is()ES5使用相等運(yùn)算符(==)和嚴(yán)格相等運(yùn)算符(===)比較兩個(gè)值是否相等。前者會(huì)自動(dòng)轉(zhuǎn)換數(shù)據(jù)類型,后者的NaN不等于自身,以及+0等于-0。ES6中可用Object.is來比較兩個(gè)值是否嚴(yán)格相等,與嚴(yán)格比較運(yùn)算符(===)的行為基本一致。但是用Object.is比較時(shí),+0不等于-0,NaN等于自身。
Object.is("foo", "foo") // true Object.is({}, {}) // false +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // trueObject.assign()
Object.assign方法用于對(duì)象的合并,將源對(duì)象(source)的所有可枚舉屬性,復(fù)制到目標(biāo)對(duì)象(target)。Object.assign方法的第一個(gè)參數(shù)是目標(biāo)對(duì)象,后面的參數(shù)都是源對(duì)象。注意,如果目標(biāo)對(duì)象與源對(duì)象有同名屬性,或多個(gè)源對(duì)象有同名屬性,則后面的屬性會(huì)覆蓋前面的屬性。
var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
如果只有一個(gè)參數(shù),Object.assign會(huì)直接返回該參數(shù)。如果該參數(shù)不是對(duì)象,則會(huì)先轉(zhuǎn)成對(duì)象,然后返回。由于undefined和null無法轉(zhuǎn)成對(duì)象,所以如果它們作為參數(shù),就會(huì)報(bào)錯(cuò)。若非對(duì)象參數(shù)出現(xiàn)在源對(duì)象的位置(即非首參數(shù)),則這些參數(shù)都會(huì)轉(zhuǎn)成對(duì)象,如果無法轉(zhuǎn)成對(duì)象,就會(huì)跳過。這意味著,如果undefined和null不在首參數(shù),就不會(huì)報(bào)錯(cuò)。
Object.assign(null) // 報(bào)錯(cuò) let obj = {a: 1}; Object.assign(obj, null) === obj // true
Object.assign不會(huì)拷貝對(duì)象的內(nèi)部屬性[[PrimitiveValue]]。布爾值、數(shù)值、字符串分別轉(zhuǎn)成對(duì)應(yīng)的包裝對(duì)象時(shí),它們的原始值都在包裝對(duì)象的內(nèi)部屬性[[PrimitiveValue]]上面。只有字符串的包裝對(duì)象,會(huì)產(chǎn)生可枚舉屬性,這些屬性會(huì)被拷貝。因此其他類型的值(即數(shù)值、字符串和布爾值)不在首參數(shù),除了字符串會(huì)以數(shù)組形式,拷貝入目標(biāo)對(duì)象,其他值都不會(huì)產(chǎn)生效果。
var v1 = "abc"; var v2 = true; var v3 = 10; var obj = Object.assign({}, v1, v2, v3); console.log(obj); // { "0": "a", "1": "b", "2": "c" } Object(true) // {[[PrimitiveValue]]: true} Object(10) // {[[PrimitiveValue]]: 10} Object("abc") // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
Object.assign拷貝的屬性是有限制的。
只拷貝源對(duì)象的自身屬性和屬性名為Symbol值的屬性。
不拷貝繼承屬性。
不拷貝不可枚舉的屬性(enumerable: false)。
注意點(diǎn)
1.Object.assign方法實(shí)行的是淺拷貝,而不是深拷貝。 也就是說,如果源對(duì)象某個(gè)屬性的值是對(duì)象,那么目標(biāo)對(duì)象拷貝得到的是這個(gè)對(duì)象的引用。
var obj1 = {a: {b: 1}}; var obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2 //Object.assign拷貝得到的是這個(gè)對(duì)象的引用。這個(gè)對(duì)象的任何變化,都會(huì)反映到目標(biāo)對(duì)象上面。
2.對(duì)于嵌套的對(duì)象,一旦遇到同名屬性,Object.assign的處理方法是替換,而不是添加。
var target = { a: { b: "c", d: "e" } } var source = { a: { b: "hello" } } Object.assign(target, source) // { a: { b: "hello" } } //target對(duì)象的a屬性被source對(duì)象的a屬性整個(gè)替換掉
3.Object.assign可以用來處理數(shù)組,但是會(huì)把數(shù)組視為對(duì)象。
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3] //Object.assign把數(shù)組視為屬性名為0、1、2的對(duì)象,因此源數(shù)組的0號(hào)屬性4覆蓋了目標(biāo)數(shù)組的0號(hào)屬性1。各類型值轉(zhuǎn)換為對(duì)象
布爾值、數(shù)值、字符串分別轉(zhuǎn)成對(duì)應(yīng)的包裝對(duì)象時(shí),它們的原始值都在包裝對(duì)象的內(nèi)部屬性[[PrimitiveValue]]上面。只有字符串的包裝對(duì)象,會(huì)產(chǎn)生可枚舉屬性。 null或undefined轉(zhuǎn)換為對(duì)象時(shí)將創(chuàng)建并返回一個(gè)空對(duì)象。
Object(1) // Number {[[PrimitiveValue]]: 1} Object("foo") // String {0: "f", 1: "o", 2: "o", length: 3, [[PrimitiveValue]]: "foo"} Object(null) // Object {}屬性的可枚舉性
對(duì)象的每個(gè)屬性都有一個(gè)描述對(duì)象(Descriptor),用來控制該屬性的行為。Object.getOwnPropertyDescriptor方法可以獲取該屬性的描述對(duì)象。
let obj = { foo: 123 }; Object.getOwnPropertyDescriptor(obj, "foo") // { // value: 123, // writable: true, // enumerable: true, // configurable: true // }
描述對(duì)象的enumerable屬性,稱為”可枚舉性“,如果該屬性為false,就表示某些操作會(huì)忽略當(dāng)前屬性。
ES5有三個(gè)操作會(huì)忽略enumerable為false的屬性。
for...in循環(huán):只遍歷對(duì)象自身的和繼承的可枚舉的屬性
Object.keys():返回對(duì)象自身的所有可枚舉的屬性的鍵名
JSON.stringify():只串行化對(duì)象自身的可枚舉的屬性
Object.assign():會(huì)忽略enumerable為false的屬性,只拷貝對(duì)象自身的可枚舉的屬性。
以上四個(gè)操作之中,最后一個(gè)是ES6新增的。只有for...in會(huì)返回繼承的屬性。
ES6規(guī)定,所有Class的原型的方法都是不可枚舉的。
屬性的遍歷for...in:循環(huán)遍歷對(duì)象自身的和繼承的可枚舉屬性(不含Symbol屬性)。
Object.keys(obj):返回一個(gè)數(shù)組,包括對(duì)象自身的(不含繼承的)所有可枚舉屬性(不含Symbol屬性)。
Object.getOwnPropertyNames(obj):返回一個(gè)數(shù)組,包含對(duì)象自身的所有屬性(不含Symbol屬性,但是包括不可枚舉屬性)。
Object.getOwnPropertySymbols(obj):返回一個(gè)數(shù)組,包含對(duì)象自身的所有Symbol屬性。
Reflect.ownKeys(obj):返回一個(gè)數(shù)組,包含對(duì)象自身的所有屬性,不管屬性名是Symbol或字符串,也不管是否可枚舉。
以上的5種方法遍歷對(duì)象的屬性,都遵守同樣的屬性遍歷的次序規(guī)則。
首先遍歷所有屬性名為數(shù)值的屬性,按照數(shù)字排序。
其次遍歷所有屬性名為字符串的屬性,按照生成時(shí)間排序。
最后遍歷所有屬性名為Symbol值的屬性,按照生成時(shí)間排序。
__proto__屬性,Object.setPrototypeOf(),Object.getPrototypeOf() __proto__屬性__proto__屬性(前后各兩個(gè)下劃線),用來讀取或設(shè)置當(dāng)前對(duì)象的prototype對(duì)象。目前,所有瀏覽器(包括 IE11)都部署了這個(gè)屬性。該屬性最好不要用。在實(shí)現(xiàn)上,__proto__調(diào)用的是Object.prototype.__proto__。
Object.setPrototypeOf()Object.setPrototypeOf方法的作用與__proto__相同,用來設(shè)置一個(gè)對(duì)象的prototype對(duì)象,返回參數(shù)對(duì)象本身。它是 ES6 正式推薦的設(shè)置原型對(duì)象的方法。
// 格式 Object.setPrototypeOf(object, prototype) // 用法 var o = Object.setPrototypeOf({}, null);
如果第一個(gè)參數(shù)不是對(duì)象,會(huì)自動(dòng)轉(zhuǎn)為對(duì)象。但是由于返回的還是第一個(gè)參數(shù),所以這個(gè)操作不會(huì)產(chǎn)生任何效果。由于undefined和null無法轉(zhuǎn)為對(duì)象,所以如果第一個(gè)參數(shù)是undefined或null,就會(huì)報(bào)錯(cuò)。
Object.setPrototypeOf(1, {}) === 1 // true Object.setPrototypeOf(undefined, {}) // TypeError: Object.setPrototypeOf called on null or undefinedObject.getPrototypeOf()
該方法與Object.setPrototypeOf方法配套,用于讀取一個(gè)對(duì)象的原型對(duì)象。
function Rectangle() { // ... } var rec = new Rectangle(); Object.getPrototypeOf(rec) === Rectangle.prototype // true
如果參數(shù)不是對(duì)象,會(huì)被自動(dòng)轉(zhuǎn)為對(duì)象。如果參數(shù)是undefined或null,它們無法轉(zhuǎn)為對(duì)象,所以會(huì)報(bào)錯(cuò)。
// 等同于 Object.getPrototypeOf(Number(1)) Object.getPrototypeOf(1) // Number {[[PrimitiveValue]]: 0} Object.getPrototypeOf(1) === Number.prototype // true
Object.getPrototypeOf(null) // TypeError: Cannot convert undefined or null to objectObject.keys(),Object.values(),Object.entries()
Object.keys:返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。ES5 引入。
Object.values():返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。ES2017 引入。返回?cái)?shù)組的成員順序,與《屬性的遍歷》部分介紹的排列規(guī)則一致。Object.values會(huì)過濾屬性名為 Symbol 值的屬性。如果參數(shù)不是對(duì)象,Object.values會(huì)先將其轉(zhuǎn)為對(duì)象。
Object.values({ [Symbol()]: 123, foo: "abc" }); // ["abc"] Object.values("foo") // ["f", "o", "o"] Object.values(42) // [] Object.values(true) // [] Object.values(null) //Uncaught TypeError: Cannot convert undefined or null to object
Object.entries():返回一個(gè)數(shù)組,成員是參數(shù)對(duì)象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對(duì)數(shù)組。該方法的行為與Object.values基本一致。原對(duì)象的屬性名是一個(gè) Symbol 值,該屬性會(huì)被忽略。可將對(duì)象轉(zhuǎn)為真正的Map結(jié)構(gòu)。
Object.entries({ [Symbol()]: 123, foo: "abc" }); // [ [ "foo", "abc" ] ] var obj = { foo: "bar", baz: 42 }; var map = new Map(Object.entries(obj)); map // Map { foo: "bar", baz: 42 }對(duì)象的擴(kuò)展運(yùn)算符
ES2017 將擴(kuò)展運(yùn)算符(...)引入了對(duì)象。
(1)解構(gòu)賦值
對(duì)象的解構(gòu)賦值用于從一個(gè)對(duì)象取值,相當(dāng)于將所有可遍歷的、但尚未被讀取的屬性,分配到指定的對(duì)象上面。所有的鍵和它們的值,都會(huì)拷貝到新對(duì)象上面。
解構(gòu)賦值要求等號(hào)右邊是一個(gè)對(duì)象,所以如果等號(hào)右邊是undefined或null,就會(huì)報(bào)錯(cuò),因?yàn)樗鼈儫o法轉(zhuǎn)為對(duì)象。
解構(gòu)賦值必須是最后一個(gè)參數(shù),否則會(huì)報(bào)錯(cuò)。
解構(gòu)賦值的拷貝是淺拷貝,即如果一個(gè)鍵的值是復(fù)合類型的值(數(shù)組、對(duì)象、函數(shù))、那么解構(gòu)賦值拷貝的是這個(gè)值的引用,而不是這個(gè)值的副本。
解構(gòu)賦值不會(huì)拷貝繼承自原型對(duì)象的屬性。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 } //變量z是解構(gòu)賦值所在的對(duì)象。它獲取等號(hào)右邊的所有尚未讀取的鍵(a和b),將它們連同值一起拷貝過來。 let { x, y, ...z } = null; // 運(yùn)行時(shí)錯(cuò)誤 let { ...x, y, z } = { x: 1, y: 2, a: 3, b: 4 }; // 句法錯(cuò)誤 var o = Object.create({ x: 1, y: 2 }); o.z = 3; let { x, ...{ y, z } } = o; x // 1 y // undefined z // 3 //變量x是單純的解構(gòu)賦值,所以可以讀取對(duì)象o繼承的屬性;變量y和z是雙重解構(gòu)賦值,只能讀取對(duì)象o自身的屬性,所以只有變量z可以賦值成功。
(2)擴(kuò)展運(yùn)算符
擴(kuò)展運(yùn)算符(...)用于取出參數(shù)對(duì)象的所有可遍歷屬性,拷貝到當(dāng)前對(duì)象之中。這等同于使用Object.assign方法。擴(kuò)展運(yùn)算符可以用于合并兩個(gè)對(duì)象。如果擴(kuò)展運(yùn)算符的參數(shù)是null或undefined,這個(gè)兩個(gè)值會(huì)被忽略,不會(huì)報(bào)錯(cuò)。
let z = { a: 3, b: 4 }; let n = { ...z }; n // { a: 3, b: 4 } let ab = { ...a, ...b }; // 等同于 let ab = Object.assign({}, a, b); let emptyObject = { ...null, ...undefined }; // 不報(bào)錯(cuò)
如果用戶自定義的屬性,放在擴(kuò)展運(yùn)算符后面,則擴(kuò)展運(yùn)算符內(nèi)部的同名屬性會(huì)被覆蓋掉。
let aWithOverrides = { ...a, x: 1, y: 2 }; // 等同于 let aWithOverrides = { ...a, ...{ x: 1, y: 2 } }; // 等同于 let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 }); //a對(duì)象的x屬性和y屬性,拷貝到新對(duì)象后會(huì)被覆蓋掉。
作用:可用來修改現(xiàn)有對(duì)象的部分屬性。
let newVersion = { ...previousVersion, name: "New Name" // Override the name property }; //newVersion對(duì)象自定義了name屬性,其他屬性全部復(fù)制自previousVersion對(duì)象。Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptor:返回某個(gè)對(duì)象屬性的描述對(duì)象(descriptor)。ES5引入。
var obj = { p: "a" }; Object.getOwnPropertyDescriptor(obj, "p") // Object { value: "a", // writable: true, // enumerable: true, // configurable: true // }
Object.getOwnPropertyDescriptors:返回指定對(duì)象所有自身屬性(非繼承屬性)的描述對(duì)象。ES2017 引入。
const obj = { foo: 123, get bar() { return "abc" } }; Object.getOwnPropertyDescriptors(obj) // { foo: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: bar], // set: undefined, // enumerable: true, // configurable: true } } //Object.getOwnPropertyDescriptors方法返回一個(gè)對(duì)象,所有原對(duì)象的屬性名都是該對(duì)象的屬性名,對(duì)應(yīng)的屬性值就是該屬性的描述對(duì)象。
主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題,因?yàn)镺bject.assign方法總是拷貝一個(gè)屬性的值,而不會(huì)拷貝它背后的賦值方法或取值方法。Object.getOwnPropertyDescriptors配合Object.create方法,將對(duì)象屬性克隆到一個(gè)新對(duì)象時(shí),屬于淺拷貝。
const source = { set foo(value) { console.log(value); } }; const target1 = {}; Object.assign(target1, source); Object.getOwnPropertyDescriptor(target1, "foo") // { value: undefined, // writable: true, // enumerable: true, // configurable: true } const clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));Null 傳導(dǎo)運(yùn)算符
?.稱為Null 傳導(dǎo)運(yùn)算符,該運(yùn)算符若返回null或undefined,就不再往下運(yùn)算,而是返回undefined。
Null 傳導(dǎo)運(yùn)算符有四種用法。
obj?.prop // 讀取對(duì)象屬性
obj?.[expr] // 同上
func?.(...args) // 函數(shù)或?qū)ο蠓椒ǖ恼{(diào)用
new C?.(...args) // 構(gòu)造函數(shù)的調(diào)用
const firstName = (message && message.body && message.body.user && message.body.user.firstName) || "default"; //等同于 const firstName = message?.body?.user?.firstName || "default";
參考自:ECMAScript 6 入門
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/82663.html
摘要:字符串的擴(kuò)展字符串的遍歷器接口字符串可以被循環(huán)遍歷。即能識(shí)別編號(hào)大于查詢字符串是否包含某個(gè)字符返回布爾值,表示是否找到了參數(shù)字符串。返回布爾值,表示參數(shù)字符串是否在原字符串的頭部。 字符串的擴(kuò)展 1.字符串的遍歷器接口 字符串可以被for...of循環(huán)遍歷。 與es5的比較for循環(huán)雖可以遍歷字符串,但不能識(shí)別大于oxFFFF的編碼; 2.位置 --> 字符/碼點(diǎn) 根據(jù)指定位置返回對(duì)應(yīng)...
摘要:學(xué)習(xí)筆記數(shù)值的擴(kuò)展有一些不常用或者還不支持的就沒有記錄了總體來說本篇只是一個(gè)備忘而已用來檢查一個(gè)數(shù)值是否為有限的。兩個(gè)新方法只對(duì)數(shù)值有效,非數(shù)值一律返回。參考引用數(shù)值擴(kuò)展 es6學(xué)習(xí)筆記-數(shù)值的擴(kuò)展 有一些不常用或者還不支持的就沒有記錄了,總體來說本篇只是一個(gè)備忘而已 Number.isFinite(), Number.isNaN() Number.isFinite()用來檢查一個(gè)數(shù)值...
摘要:學(xué)習(xí)筆記函數(shù)擴(kuò)展函數(shù)參數(shù)的默認(rèn)值如果參數(shù)默認(rèn)值是變量,那么參數(shù)就不是傳值的,而是每次都重新計(jì)算默認(rèn)值表達(dá)式的值。屬性函數(shù)的屬性,返回該函數(shù)的函數(shù)名。箭頭函數(shù)詳細(xì)鏈接參考引用函數(shù)擴(kuò)展 es6學(xué)習(xí)筆記-函數(shù)擴(kuò)展_v1.0 函數(shù)參數(shù)的默認(rèn)值 function Point(x = 0, y = 0) { this.x = x; this.y = y; } var p = ne...
摘要:學(xué)習(xí)筆記字符串的擴(kuò)展字符的表示法允許使用的形式表示一個(gè)字符,但在之前,單個(gè)碼點(diǎn)僅支持到,超出該范圍的必須用雙字節(jié)形式表示,否則會(huì)解析錯(cuò)誤。返回布爾值,表示參數(shù)字符串是否在源字符串的頭部。,是引入了字符串補(bǔ)全長度的功能。 es6學(xué)習(xí)筆記-字符串的擴(kuò)展_v1.0 字符的Unicode表示法 JavaScript 允許使用uxxxx的形式表示一個(gè)字符,但在 ES6 之前,單個(gè)碼點(diǎn)僅支持u00...
摘要:經(jīng)過一番折騰,總算是把自己項(xiàng)目里的配置調(diào)整好了,所有文件從原來的縮小到。折騰了不少時(shí)間,改動(dòng)其實(shí)就一個(gè)地方,就是配置文件,記錄一下自己折騰的過程。本以為那這兩種方式取其一就行了。這感覺和想象中的不一樣啊,說好的一個(gè)搞定一切的呢。。。 先是看到前端早讀課【第1065期】再見,babel-preset-2015,聽說現(xiàn)在有了babel-preset-env,別的什么preset都不需要了,...
閱讀 2627·2021-11-12 10:36
閱讀 2267·2021-08-23 09:47
閱讀 1689·2019-08-30 15:44
閱讀 1411·2019-08-30 14:10
閱讀 2249·2019-08-29 16:52
閱讀 2347·2019-08-29 16:40
閱讀 1594·2019-08-29 16:17
閱讀 2415·2019-08-26 13:21