国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ES6學習筆記2—各擴展

Zoom / 478人閱讀

摘要:字符串的擴展字符的表示法允許采用形式表示一個字符,其中表示字符的碼點。返回布爾值,表示參數字符串是否在源字符串的頭部。使用和這兩個常量,用來表示這個范圍的上下限。對于那些無法用個二進制位精確表示的小數,方法返回最接近這個小數的單精度浮點數。

字符串的擴展 字符的 Unicode 表示法

JavaScript 允許采用uxxxx形式表示一個字符,其中xxxx表示字符的 Unicode 碼點。這種表示法只限于碼點在u0000~uFFFF之間的字符。超出這個范圍的字符,必須用兩個雙字節的形式表示。ES6中只要將碼點放入大括號,就能正確解讀該字符。

"uD842uDFB7"
// "?"

"u{20BB7}"
// "?"

大括號表示法與 UTF-16 編碼是等價的。

"z" === "z"  // true
"172" === "z" // true
"x7A" === "z" // true
"u007A" === "z" // true
"u{7A}" === "z" // true
字符串方法

JavaScript內部,字符以UTF-16的格式儲存,每個字符固定為2個字節。對于那些需要4個字節儲存的字符(Unicode碼點大于0xFFFF的字符),JavaScript會認為它們是兩個字符,字符串長度會誤判為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方法在第一個字符上,正確地識別了“?”
s.codePointAt(1) // 57271   第二個字符是“?”的后兩個字節
String.fromCharCode(0x20BB7) // "?" 最高位2被舍棄了,最后返回碼點U+0BB7對應的字符,而不是碼點U+20BB7對應的字符。
String.fromCodePoint(0x20BB7) // "?"
"?".at(0) // "?"

對于Unicode碼點大于0xFFFF的字符:

charAt:無法讀取整個字符。該方法不能識別碼點大于0xFFFF的字符。

charCodeAt:只能分別返回前兩個字節和后兩個字節的值。

fromCharCode:不能識別大于0xFFFF的碼點。

codePointAt:能夠正確處理4個字節儲存的字符,返回一個字符的碼點。codePointAt方法是測試一個字符由兩個字節還是由四個字節組成的最簡單方法。

fromCodePoint:可以識別0xFFFF的字符。

at: 可以識別Unicode編號大于0xFFFF的字符,返回正確的字符。這個方法可以通過墊片庫實現。

注意,fromCodePoint方法定義在String對象上,而codePointAt方法定義在字符串的實例對象上。

其他方法

normalize():用來將字符的不同表示方法統一為同樣的形式

includes():返回布爾值,表示是否找到了參數字符串。

startsWith():返回布爾值,表示參數字符串是否在源字符串的頭部。

endsWith():返回布爾值,表示參數字符串是否在源字符串的尾部。

padStart():用于頭部補全。常見用途是為數值補全指定位數和提示字符串格式。

"1".padStart(10, "0") // "0000000001"
"12".padStart(10, "YYYY-MM-DD") // "YYYY-MM-12"

padEnd():用于尾部補全。

repeat():返回一個新字符串,表示將原字符串重復n次。參數如果是小數,會被取整。

參數是負數或者Infinity,會報錯。

參數是0到-1之間的小數,則等同于0,這是因為會先進行取整運算。

參數0到-1之間的小數,取整以后等于-0,repeat視同為0。

參數NaN等同于0。

repeat的參數是字符串,則會先轉換成數字。

"na".repeat(2.9) // "nana"
"na".repeat(Infinity)// RangeError
"na".repeat(NaN) // ""
"na".repeat("na") // ""
"na".repeat("3") // "nanana"
字符串的遍歷器接口

ES6為字符串添加了遍歷器接口(詳見《Iterator》一章),使得字符串可以被for...of循環遍歷。該遍歷可以識別大于0xFFFF的碼點,傳統的for循環無法識別這樣的碼點。

var text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "
//for循環會認為它包含兩個字符(都不可打印)
for (let i of text) {
  console.log(i);
}
// "?"
//for...of循環會正確識別出這一個字符
模板字符串

模板字符串用反引號(`)標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。在模板字符串中需要使用反引號,則前面要用反斜杠轉義。使用模板字符串表示多行字符串,所有的空格和縮進都會被保留在輸出之中。

// 普通字符串
`In JavaScript "
" is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

// 字符串中嵌入變量
`Hello ${name}, how are you ${time}?`

模板字符串中嵌入變量,需要將變量名寫在${}之中。大括號內部可以放入任意的JavaScript表達式,可以進行運算,以及引用對象屬性和調用函數。如果大括號中的值不是字符串,將按照一般的規則轉為字符串。比如,大括號中是一個對象,將默認調用對象的toString方法。
如果模板字符串中的變量沒有聲明,將報錯。

// 變量place沒有聲明
var msg = `Hello, ${place}`;
// 報錯

由于模板字符串的大括號內部,就是執行JavaScript代碼,因此如果大括號內部是一個字符串,將會原樣輸出。

`Hello ${"World"}`
// "Hello World"
標簽模板

模板字符串緊跟在一個函數名后面,該函數將被調用來處理這個模板字符串。即模板字符串就是該函數的參數。標簽模板是函數調用的一種特殊形式。“標簽”指的就是函數,緊跟在后面的模板字符串就是它的參數。

//模板字符里面有變量,會將模板字符串先處理成多個參數,再調用函數。
var a = 5;
var b = 10;

tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(["Hello ", " world ", ""], 15, 50);

模板處理函數的第一個參數(模板字符串數組),還有一個raw屬性。

console.log(`123`) //123
console.log`123` // ["123", raw: Array[1]]

上面代碼中,第二個console.log接受的參數,實際上是一個數組。該數組有一個raw屬性,保存的是轉義后的原字符串。

String.raw方法,往往用來充當模板字符串的處理函數,返回一個斜杠都被轉義(即斜杠前面再加一個斜杠)的字符串,對應于替換變量后的模板字符串。如果原字符串的斜杠已經轉義,那么String.raw不會做任何處理。

String.raw`Hi
${2+3}!`;
// "Hi
5!"
String.raw`Hi
`
// "Hi
"

String.raw方法也可以作為正常的函數使用。這時,它的第一個參數,應該是一個具有raw屬性的對象,且raw屬性的值應該是一個數組。

String.raw({ raw: "test" }, 0, 1, 2);
// "t0e1s2t"

// 等同于
String.raw({ raw: ["t","e","s","t"] }, 0, 1, 2);
正則的擴展 RegExp構造函數
var regex = new RegExp("xyz", "i");
// 等價于
var regex = /xyz/i;
// 等價于
var regex = new RegExp(/xyz/i);

ES5中以下寫法會報錯。ES6可以使用第二個參數指定修飾符,新指定的修飾符會覆蓋原有的正則表達式的修飾符。

var regex = new RegExp(/xyz/ig, "i");
//原有正則對象的修飾符是ig,它會被第二個參數i覆蓋.
u修飾符

ES6對正則表達式添加了u修飾符,含義為“Unicode模式”,用來正確處理大于uFFFF的Unicode字符。也就是說,會正確處理四個字節的UTF-16編碼。

/^uD83D/u.test("uD83DuDC2A") // false
/^uD83D/.test("uD83DuDC2A") // true

上面的代碼中,uD83DuDC2A是一個四字節的UTF-16編碼,代表一個字符。不加“u”,會按 ES5 將其識別為2個字符,加了“u”之后,會按 ES6 將其正確識別為一個字符。

以下幾種情況就必須加上“u”才能正確識別:

.在正則表達式中表示除行終止符(換行符(n),回車符(r),行分隔符,段分隔符)外的任意單個字符,S表示匹配所有不是空格的字符。他們均正確識別碼點大于0xFFFF的Unicode字符,必須加上u修飾符才能正確識別。

/^S$/.test("?") // false
/^S$/u.test("?") // true

ES6新增了使用大括號表示Unicode字符,這種表示法在正則表達式中必須加上u修飾符,才能識別。否則大括號會被解讀為量詞。

/^u{3}$/.test("uuu") // true 被解讀為量詞
/^u{3}$/u.test("uuu") // false 被解讀為Unicode表達式

有些Unicode字符的編碼不同,但是字型很相近,需要加u才能識別。比如,u004B與u212A都是大寫的K。

/[a-z]/i.test("u212A") // false  該行代碼不加u修飾符,就無法識別非規范的K字符
/[a-z]/iu.test("u212A") // true

y 修飾符

y修飾符的作用與g修飾符類似,也是全局匹配,后一次匹配都從上一次匹配成功的下一個位置開始。不同之處在于,g修飾符只要剩余位置中存在匹配就可,而y修飾符確保匹配必須從剩余的第一個位置開始。y修飾符號就是讓頭部匹配的標志^在全局匹配中都有效。

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  第一次執行后,剩余字符串是_aa_a,y修飾符要求匹配必須從頭部開始,所以返回null

在split方法中使用y修飾符,原字符串必須以分隔符開頭。這也意味著,只要匹配成功,數組的第一個成員肯定是空字符串。

sticky屬性

ES6的正則對象多了sticky屬性,表示是否設置了y修飾符。

var r = /hellod/y;
r.sticky // true
flags屬性

ES6為正則表達式新增了flags屬性,會返回正則表達式的修飾符。

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

// ES6的flags屬性  返回正則表達式的修飾符
/abc/ig.flags
// "gi"
先行斷言

JavaScript 語言的正則表達式,只支持先行斷言(lookahead)和先行否定斷言(negative lookahead)。
”先行斷言“指的是,x只有在y前面才匹配,必須寫成/x(?=y)/。比如,只匹配百分號之前的數字,要寫成/d+(?=%)/。”先行否定斷言“指的是,x只有不在y前面才匹配,必須寫成/x(?!y)/。比如,只匹配不在百分號之前的數字,要寫成/d+(?!%)/。

數值的擴展

ES6 提供了二進制和八進制數值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示。可使用Number方法將0b和0o前綴的字符串數值轉為十進制。

Number("0b111")  // 7
新增方法

Number.isFinite():用來檢查一個數值是否為有限的(finite),對于非數值一律返回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():用來檢查一個值是否為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對象上面,行為完全保持不變。

Number.parseInt === parseInt // true

Number.isInteger():用來判斷一個值是否為整數。需要注意的是,在JavaScript內部,整數和浮點數是同樣的儲存方法,所以3和3.0被視為同一個值

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false

Number.isSafeInteger():用來判斷一個整數是否落在Number.MAX_SAFE_INTEGER與Number.MIN_SAFE_INTEGER范圍之內。使用該函數時,需注意不僅要驗證運算結果是否落在安全整數的范圍內,還要同時驗證參與運算的每個值,否則很可能得到錯誤結果。

Number.isSafeInteger(3) // true
Number.isSafeInteger(1.2) // false

新增常量

Number.EPSILON:極小常量。用于為浮點數計算,設置一個誤差范圍。

JavaScript能夠準確表示的整數范圍在-2^53到2^53之間(不含兩個端點),超過這個范圍,無法精確表示這個值。ES6使用Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER這兩個常量,用來表示這個范圍的上下限。

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 // true
Math對象的擴展

ES6在Math對象上新增了17個與數學相關的方法。所有這些方法都是靜態方法,只能在Math對象上調用。

Math.trunc:用于去除一個數的小數部分,返回整數部分。對于非數值,Math.trunc內部使用Number方法將其先轉為數值。對于空值和無法截取整數的值,返回NaN。

Math.trunc("123.456")  // 123
Math.trunc(NaN);      // NaN
Math.trunc("foo");    // NaN
Math.trunc();         // NaN

Math.cbrt():用于計算一個數的立方根。對于非數值,Math.cbrt方法內部先使用Number方法將其轉為數值。

Math.sign():用來判斷一個數到底是正數、負數、還是零。正數返回+1,負數返回-1,0返回0,-0返回-0,其他值,返回NaN。

Math.clz32():JavaScript的整數使用32位二進制形式。Math.clz32()返回一個數的32位無符號整數形式有多少個前導0。對于小數,只考慮其整數部分。對于空值或其他類型的值,會將它們先轉為數值,然后再計算。

//1000的二進制形式是0b1111101000,一共有10位,所以32位之中有22個前導0。
Math.clz32(1000) // 22

Math.imul:返回兩個數以32位帶符號整數形式相乘的結果,返回的也是一個32位的帶符號整數。

Math.fround:返回一個數的單精度浮點數形式。對于整數來說,Math.fround方法返回結果一樣。對于那些無法用64個二進制位精確表示的小數,Math.fround方法返回最接近這個小數的單精度浮點數。

Math.fround(1)     // 1
Math.fround(1.337) // 1.3370000123977661
Math.fround(1.5)   // 1.5

Math.hypot:返回所有參數的平方和的平方根。如果參數不是數值,Math.hypot方法會將其轉為數值。只要有一個參數無法轉為數值,就會返回NaN。

Math.sign():用來判斷一個值的正負,但是如果參數是-0,它會返回-0。

新增對數方法

Math.expm1():

Math.log1p(x):返回1 + x的自然對數,即Math.log(1 + x)。如果x小于-1,返回NaN。

Math.log10(x):返回以10為底的x的對數。如果x小于0,則返回NaN。

Math.log2(x):返回以2為底的x的對數。如果x小于0,則返回NaN。

新增三角函數方法

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)

指數運算符

ES2016 新增了一個指數運算符(**)。

2 ** 3 // 8
b **= 3; // 等同于 b = b * b * b;

在 V8 引擎中,指數運算符與Math.pow的實現不相同,對于特別大的運算結果,兩者會有細微的差異。

Math.pow(99, 99)
// 3.697296376497263e+197

99 ** 99
// 3.697296376497268e+197
數組的擴展 Array.from()

Array.from方法用于將兩類對象轉為真正的數組:類似數組的對象(即有length屬性的對象)和可遍歷(iterable)的對象(包括ES6新增的數據結構Set和Map)。如果參數是一個真正的數組,Array.from會返回一個一模一樣的新數組。

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"]

擴展運算符(...)也可以將某些數據結構轉為數組。擴展運算符背后調用的是遍歷器接口(Symbol.iterator)。

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

// NodeList對象
[...document.querySelectorAll("div")]

Array.from還可以接受第二個參數,作用類似于數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組。

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字符,因此可以將將字符串轉為數組,然后正確返回字符串的長度。

function countSymbols(string) {
  return Array.from(string).length;
}
Array.of()

Array.of總是返回參數值組成的數組。如果沒有參數,就返回一個空數組。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]
數組的新增實例方法

copyWithin() :在當前數組內部,將指定位置的成員復制到其他位置(會覆蓋原有成員),然后返回當前數組。Array.prototype.copyWithin(target, start = 0, end = this.length)。三個參數都應該是數值,如果不是,會自動轉為數值。會修改當前數組。

find() :用于找出第一個符合條件的數組成員。它的參數是一個回調函數,所有數組成員依次執行該回調函數,直到找出第一個返回值為true的成員,然后返回該成員。如果沒有符合條件的成員,則返回undefined。可以發現NaN。

findIndex() :與find方法非常類似,返回第一個符合條件的數組成員的位置,如果所有成員都不符合條件,則返回-1。可以發現NaN。

[NaN].indexOf(NaN)    // -1

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

//indexOf方法無法識別數組的NaN成員,但是findIndex方法可以借助Object.is方法做到。

fill() :使用給定值,填充一個數組。fill方法用于空數組的初始化非常方便。數組中已有的元素,會被全部抹去。

["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號位開始,向原數組填充7,到2號位之前結束。

includes():返回一個布爾值,表示某個數組是否包含給定的值,與字符串的includes方法類似。該方法屬于ES7,但Babel轉碼器已經支持。

[1, 2, NaN].includes(NaN); // true

[NaN].indexOf(NaN)  // -1
[NaN].includes(NaN) // true
//indexof會導致對NaN的誤判,但是includes可以正確判斷NaN。

遍歷數組

entries(),keys()和values()均用于遍歷數組。它們都返回一個遍歷器對象(Iterator),可以用for...of循環進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。

for (let [index, elem] of ["a", "b"].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

不使用for...of循環,可以手動調用遍歷器對象的next方法,進行遍歷。

let letter = ["a", "b", "c"];
let entries = letter.entries();
console.log(entries.next().value); // [0, "a"]
console.log(entries.next().value); // [1, "b"]
數組的空位

數組的空位指,數組的某一個位置沒有任何值。空位不是undefined,一個位置的值等于undefined,依然是有值的。空位是沒有任何值。

Array(3) // [, , ,]     Array(3)返回一個具有3個空位的數組。
對空位的處理

ES5大多數情況下會忽略空位。

forEach(), filter(), every() 和some()都會跳過空位。

map()會跳過空位,但會保留這個值。

join()和toString()會將空位視為undefined,而undefined和null會被處理成空字符串。

// filter方法
["a",,"b"].filter(x => true) // ["a","b"]

// map方法
[,"a"].map(x => 1)  // [,1]

// join方法
[,"a",undefined,null].join("#") // "#a##"

ES6明確將空位轉為undefined。

Array.from方法會將數組的空位,轉為undefined。

擴展運算符(...)將空位轉為undefined。

copyWithin()會連空位一起拷貝。

fill()會將空位視為正常的數組位置。

for...of循環也會遍歷空位。

entries()、keys()、values()、find()和findIndex()會將空位處理成undefined。

Array.from(["a",,"b"])   // [ "a", undefined, "b" ]
[...["a",,"b"]]   // [ "a", undefined, "b" ]
new Array(3).fill("a") // ["a","a","a"]

由于空位的處理規則非常不統一,所以建議避免出現空位。

函數的擴展 函數參數的默認值

ES6 允許為函數的參數設置默認值,即直接寫在參數定義的后面。參數變量是默認聲明的,所以不能用let或const再次聲明。使用參數默認值時,函數不能有同名參數。

//參數變量x是默認聲明的,在函數體中,不能用let或const再次聲明,否則會報錯。
function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

如果參數默認值是變量,那么參數就不是傳值的,而是每次都重新計算默認值表達式的值。也就是說,參數默認值是惰性求值的。

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101
//代碼中,參數p的默認值是x + 1。這時,每次調用函數foo,都會重新計算x + 1,而不是默認p等于 100。
與解構賦值默認值結合使用

參數默認值可以與解構賦值的默認值結合起來使用。

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

foo({}) // undefined, 5
foo() // TypeError: Cannot read property "x" of undefined

只有當函數foo的參數是一個對象時,變量x和y才會通過解構賦值而生成。如果函數foo調用時參數不是對象,變量x和y就不會生成,從而報錯。如果參數對象沒有y屬性,y的默認值5才會生效。

參數默認值的位置

通常情況下,定義了默認值的參數,應該是函數的尾參數。如果非尾部的參數設置默認值,則調用時無法只省略該參數,而不省略它后面的參數,除非顯式輸入undefined。

function f(x = 1, y) {
  return [x, y];
}
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]
函數的 length 屬性

函數的length屬性,將返回沒有指定默認值的參數個數。設置了默認值的參數不是尾參數,那么length屬性也不再計入后面的參數。rest參數也不會計入length屬性。

(function (a, b, c = 5) {}).length // 2
(function(...args) {}).length // 0
(function (a, b = 1, c) {}).length // 1
作用域

一旦設置了參數的默認值,調用函數時,參數會形成一個多帶帶的作用域(context)。這種語法行為,在不設置參數默認值時,是不會出現的。當該多帶帶作用域里面默認值是變量,且變量未定義,則指向外層的全局變量,若此時該全局變量不存在,就會報錯。

let x = 1;

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

f() // 1

函數f調用時,參數y = x形成一個多帶帶的作用域。這個作用域里面,變量x本身沒有定義,所以指向外層的全局變量x。函數調用時,函數體內部的新聲明局部變量x影響不到默認值變量x。

rest參數

rest 運算符:將一個不定數量的參數表示為一個數組。

ES6 引入 rest 參數(形式為“...變量名”),用于獲取函數的多余參數,rest 參數中的變量代表一個數組。注意,rest 參數之后不能再有其他參數(即只能是最后一個參數),否則會報錯。函數的length屬性,不包括 rest 參數。

function f(a, ...b) {
  console.log(b);
}
f(2,3,4,5)  //[3, 4, 5]

// 報錯
function f(a, ...b, c) {
  // ...
}
擴展運算符

擴展運算符(spread)是三個點(...)。它好比 rest 參數的逆運算,將一個數組轉為用逗號分隔的參數序列。
在某種程度上,rest運算符和Spread運算符(即擴展運算符)相反,Spread運算符會“展開”元素使其變成多個元素,rest運算符會收集多個元素和“壓縮”成一個單一的元素。

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

// ES6的寫法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);
擴展運算符的應用

合并數組

// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]

將字符串轉為真正的數組。

[..."hello"]
// [ "h", "e", "l", "l", "o" ]

該寫法能夠正確識別32位的Unicode字符。

"xuD83DuDE80y".length // 4
[..."xuD83DuDE80y"].length // 3
//JavaScript會將32位Unicode字符,識別為2個字符,采用擴展運算符就沒有這個問題。

擴展運算符內部調用的是數據結構的Iterator接口。所以任何Iterator接口的對象,都可以用擴展運算符轉為真正的數組。對于那些沒有部署Iterator接口的類似數組的對象,擴展運算符就無法將其轉為真正的數組。

嚴格模式

從ES5開始,函數內部可以設定為嚴格模式。

function doSomething(a, b) {
  "use strict";
  // code
}

《ECMAScript 2016標準》規定只要函數參數使用了默認值、解構賦值、或者擴展運算符,那么函數內部就不能顯式設定為嚴格模式,否則會報錯。

規定的原因:函數內部的嚴格模式,同時適用于函數體代碼和函數參數代碼。但是,函數執行的時候,先執行函數參數代碼,然后再執行函數體代碼。這樣就有一個不合理的地方,只有從函數體代碼之中,才能知道參數代碼是否應該以嚴格模式執行,但是參數代碼卻應該先于函數體代碼執行。因此,標準如此定義。

name 屬性

函數的name屬性,返回該函數的函數名。將一個匿名函數賦值給一個變量,ES5 的name屬性,會返回空字符串,而 ES6 的name屬性會返回實際的函數名。如果將一個具名函數賦值給一個變量,則 ES5 和 ES6 的name屬性都返回這個具名函數原本的名字。

var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"


const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"

Function構造函數返回的函數實例,name屬性的值為anonymous。bind返回的函數,name屬性值會加上bound前綴。

(new Function).name // "anonymous"
function foo() {};
foo.bind({}).name // "bound foo"
箭頭函數

ES6允許使用“箭頭”(=>)定義函數。如果箭頭函數不需要參數或需要多個參數,就使用一個圓括號代表參數部分。如果箭頭函數的代碼塊部分多于一條語句,就要使用大括號將它們括起來。由于大括號被解釋為代碼塊,所以如果箭頭函數直接返回一個對象,必須在對象外面加上括號。箭頭函數可以嵌套。

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" });

箭頭函數使用注意點:

函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。this指向的固定化,是因為箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能用作構造函數。

不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。

不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用rest參數代替。

不可以使用yield命令,因此箭頭函數不能用作Generator函數。

// ES6
function foo() {
  setTimeout(() => {
    console.log("id:", this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log("id:", _this.id);
  }, 100);
}

上面代碼中,轉換后的ES5版本清楚地說明了,箭頭函數里面根本沒有自己的this,而是引用外層的this。

除了this,以下三個變量在箭頭函數之中也是不存在的,指向外層函數的對應變量:arguments、super、new.target。由于箭頭函數沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。

(function() {
  return [
    (() => this.x).bind({ x: "inner" })()
  ];
}).call({ x: "outer" });
// ["outer"]
//上面代碼中,箭頭函數沒有自己的this,所以bind方法無效,內部的this指向外部的this。
“函數綁定”(function bind)運算符

ES7提出了“函數綁定”(function bind)運算符,用來取代call、apply、bind調用。雖然該語法還是ES7的一個提案,但是Babel轉碼器已經支持。

函數綁定運算符是并排的兩個雙冒號(::),雙冒號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象,作為上下文環境(即this對象),綁定到右邊的函數上面。如果雙冒號左邊為空,右邊是一個對象的方法,則等于將該方法綁定在該對象上面。雙冒號運算符返回的還是原對象,因此可以采用鏈式寫法。

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
尾調用優化

尾調用就是指某個函數運行的最后一步是調用另一個函數。尾調用不一定出現在函數尾部,只要是最后一步操作即可。

function f(x){
  return g(x);
}
//函數f的最后一步是調用函數g,這就叫尾調用。


function f(x){
  return g(x) + 1;
}
//函數調用之后還有操作,不是尾調用。
function f(x){
  g(x);
}
//上面的函數等同于下面的代碼,因此也不是尾調用。
//function f(x){
//  g(x);
//  return undefined;
//}
尾調用優化

函數調用會在內存形成一個“調用記錄”,又稱“調用幀”(call frame),保存調用位置和內部變量等信息。如果在函數A的內部調用函數B,那么在A的調用幀上方,還會形成一個B的調用幀。等到B運行結束,將結果返回到A,B的調用幀才會消失。如果函數B內部還調用函數C,那就還有一個C的調用幀,以此類推。所有的調用幀,就形成一個“調用棧”(call stack)。

function f() {
  let m = 1;
  let n = 2;
  return g(m + n);
}
f();

// 等同于
function f() {
  return g(3);
}
f();

// 等同于
g(3);

上面代碼中,如果函數g不是尾調用,函數f就需要保存內部變量m和n的值、g的調用位置等信息。但由于調用g之后,函數f就結束了,所以執行到最后一步,完全可以刪除 f(x) 的調用幀,只保留 g(3) 的調用幀。

這就叫做“尾調用優化”(Tail call optimization),即只保留內層函數的調用幀。如果所有函數都是尾調用,那么完全可以做到每次執行時,調用幀只有一項,這將大大節省內存。這就是“尾調用優化”的意義。注意,只有不再用到外層函數的內部變量,內層函數的調用幀才會取代外層函數的調用幀,否則就無法進行“尾調用優化”。

尾遞歸

函數調用自身,稱為遞歸。如果尾調用自身,就稱為尾遞歸。遞歸非常耗費內存,因為需要同時保存成千上百個調用幀,很容易發生“棧溢出”錯誤(stack overflow)。但對于尾遞歸來說,由于只存在一個調用幀,所以永遠不會發生“棧溢出”錯誤。

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120  計算n的階乘,最多需要保存n個調用記錄,復雜度 O(n) 
//改寫為尾遞歸
function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120 改寫成了尾遞歸,只保留一個調用記錄,復雜度 O(1) 。

由此可見,“尾調用優化”對遞歸操作意義重大。ES6明確規定,所有ECMAScript的實現,都必須部署“尾調用優化”。這就是說,在ES6中,只要使用尾遞歸,就不會發生棧溢出,相對節省內存。

遞歸函數的改寫

尾遞歸的實現,往往需要改寫遞歸函數,確保最后一步只調用自身。做到這一點的方法,就是把所有用到的內部變量改寫成函數的參數。

嚴格模式

ES6的尾調用優化只在嚴格模式下開啟,正常模式是無效的。

這是因為在正常模式下,函數內部有兩個變量,可以跟蹤函數的調用棧。

func.arguments:返回調用時函數的參數。

func.caller:返回調用當前函數的那個函數。

尾調用優化發生時,函數的調用棧會改寫,因此上面兩個變量就會失真。嚴格模式禁用這兩個變量,所以尾調用模式僅在嚴格模式下生效。

function restricted() {
  "use strict";
  restricted.caller;    // 報錯
  restricted.arguments; // 報錯
}
restricted();
尾遞歸優化的實現

正常模式下,或者那些不支持該功能的環境中,采用“循環”換掉“遞歸”,以減少調用棧。

//正常遞歸函數
function sum(x, y) {
  if (y > 0) {
    return sum(x + 1, y - 1);
  } else {
    return x;
  }
}

sum(1, 100000)
//

可以使用蹦床函數(trampoline)將遞歸執行轉為循環執行。

function trampoline(f) {
  while (f && f instanceof Function) {
    f = f();
  }
  return f;
}

上面就是蹦床函數的一個實現,它接受一個函數f作為參數。只要f執行后返回一個函數,就繼續執行。這里是返回一個函數,然后執行該函數,而不是函數里面調用函數,這樣就避免了遞歸執行,從而就消除了調用棧過大的問題。

然后,要做的就是將原來的遞歸函數,改寫為每一步返回另一個函數。

function sum(x, y) {
  if (y > 0) {
    return sum.bind(null, x + 1, y - 1);
  } else {
    return x;
  }
}
//sum函數的每次執行,都會返回自身的另一個版本
trampoline(sum(1, 100000));//然后,用蹦床函數執行sum,就不會發生調用棧溢出。
函數參數的尾逗號

ES2017 允許函數的最后一個參數有尾逗號(trailing comma)。此前,函數定義和調用時,都不允許最后一個參數后面出現逗號。

對象的擴展 屬性的簡潔表示法

ES6 允許直接寫入變量和函數,作為對象的屬性和方法。簡潔寫法的屬性名總是字符串。

var foo = "bar";
var baz = {foo};
baz // {foo: "bar"}
// 等同于
var baz = {foo: foo};
//ES6 允許在對象之中,直接寫變量。這時,屬性名為變量名, 屬性值為變量的值。

var o = {
  method() {
    return "Hello!";
  }
};
// 等同于
var o = {
  method: function() {
    return "Hello!";
  }
};
屬性名表達式

JavaScript語言定義對象的屬性,有兩種方法。方法一是直接用標識符作為屬性名,方法二是用表達式作為屬性名,這時要將表達式放在方括號之內

// 方法一
obj.foo = true;

// 方法二
obj["a" + "bc"] = 123;

如果使用字面量方式定義對象(使用大括號),在 ES5 中只能使用方法一(標識符)定義屬性。但ES6 允許字面量定義對象時,用方法二(表達式)作為對象的屬性名,即把表達式放在方括號內。表達式還可以用于定義方法名。

let propKey = "foo";

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

注意,屬性名表達式與簡潔表示法,不能同時使用,會報錯。

// 報錯
var foo = "bar";
var bar = "abc";
var baz = { [foo] };

// 正確
var foo = "bar";
var baz = { [foo]: "abc"};

注意,屬性名表達式如果是一個對象,默認情況下會自動將對象轉為字符串[object Object],這一點要特別小心。

const keyA = {a: 1};
const keyB = {b: 2};

const myObject = {
  [keyA]: "valueA",
  [keyB]: "valueB"
};

myObject // Object {[object Object]: "valueB"}
//[keyA]和[keyB]得到的都是[object Object],所以[keyB]會把[keyA]覆蓋掉,而myObject最后只有一個[object Object]屬性。
方法的 name 屬性

對象方法的name屬性返回函數名(即方法名)。如果對象的方法使用了取值函數(getter)和存值函數(setter),則name屬性不是在該方法上面,而是該方法的屬性的描述對象的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方法創造的函數,name屬性返回bound加上原函數的名字。

Function構造函數創造的函數,name屬性返回anonymous。

如果對象的方法是一個 Symbol 值,那么name屬性返回的是這個 Symbol 值的描述。

Object.is()

ES5使用相等運算符(==)和嚴格相等運算符(===)比較兩個值是否相等。前者會自動轉換數據類型,后者的NaN不等于自身,以及+0等于-0。ES6中可用Object.is來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行為基本一致。但是用Object.is比較時,+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) // true
Object.assign()

Object.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性,復制到目標對象(target)。Object.assign方法的第一個參數是目標對象,后面的參數都是源對象。注意,如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。

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}

如果只有一個參數,Object.assign會直接返回該參數。如果該參數不是對象,則會先轉成對象,然后返回。由于undefined和null無法轉成對象,所以如果它們作為參數,就會報錯。若非對象參數出現在源對象的位置(即非首參數),則這些參數都會轉成對象,如果無法轉成對象,就會跳過。這意味著,如果undefined和null不在首參數,就不會報錯。

Object.assign(null) // 報錯

let obj = {a: 1};
Object.assign(obj, null) === obj // true

Object.assign不會拷貝對象的內部屬性[[PrimitiveValue]]。布爾值、數值、字符串分別轉成對應的包裝對象時,它們的原始值都在包裝對象的內部屬性[[PrimitiveValue]]上面。只有字符串的包裝對象,會產生可枚舉屬性,這些屬性會被拷貝。因此其他類型的值(即數值、字符串和布爾值)不在首參數,除了字符串會以數組形式,拷貝入目標對象,其他值都不會產生效果。

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拷貝的屬性是有限制的。

只拷貝源對象的自身屬性和屬性名為Symbol值的屬性。

不拷貝繼承屬性。

不拷貝不可枚舉的屬性(enumerable: false)。

注意點

1.Object.assign方法實行的是淺拷貝,而不是深拷貝。 也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用。

var obj1 = {a: {b: 1}};
var obj2 = Object.assign({}, obj1);

obj1.a.b = 2;
obj2.a.b // 2
//Object.assign拷貝得到的是這個對象的引用。這個對象的任何變化,都會反映到目標對象上面。

2.對于嵌套的對象,一旦遇到同名屬性,Object.assign的處理方法是替換,而不是添加。

var target = { a: { b: "c", d: "e" } }
var source = { a: { b: "hello" } }
Object.assign(target, source)
// { a: { b: "hello" } }
//target對象的a屬性被source對象的a屬性整個替換掉

3.Object.assign可以用來處理數組,但是會把數組視為對象。

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
//Object.assign把數組視為屬性名為0、1、2的對象,因此源數組的0號屬性4覆蓋了目標數組的0號屬性1。
各類型值轉換為對象

布爾值、數值、字符串分別轉成對應的包裝對象時,它們的原始值都在包裝對象的內部屬性[[PrimitiveValue]]上面。只有字符串的包裝對象,會產生可枚舉屬性。 null或undefined轉換為對象時將創建并返回一個空對象。

Object(1)
// Number {[[PrimitiveValue]]: 1}
Object("foo")
// String {0: "f", 1: "o", 2: "o", length: 3, [[PrimitiveValue]]: "foo"}
Object(null)
// Object {}
屬性的可枚舉性

對象的每個屬性都有一個描述對象(Descriptor),用來控制該屬性的行為。Object.getOwnPropertyDescriptor方法可以獲取該屬性的描述對象。

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, "foo")
//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

描述對象的enumerable屬性,稱為”可枚舉性“,如果該屬性為false,就表示某些操作會忽略當前屬性。
ES5有三個操作會忽略enumerable為false的屬性。

for...in循環:只遍歷對象自身的和繼承的可枚舉的屬性

Object.keys():返回對象自身的所有可枚舉的屬性的鍵名

JSON.stringify():只串行化對象自身的可枚舉的屬性

Object.assign():會忽略enumerable為false的屬性,只拷貝對象自身的可枚舉的屬性。
以上四個操作之中,最后一個是ES6新增的。只有for...in會返回繼承的屬性。

ES6規定,所有Class的原型的方法都是不可枚舉的。

屬性的遍歷

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

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

Object.getOwnPropertyNames(obj):返回一個數組,包含對象自身的所有屬性(不含Symbol屬性,但是包括不可枚舉屬性)。

Object.getOwnPropertySymbols(obj):返回一個數組,包含對象自身的所有Symbol屬性。

Reflect.ownKeys(obj):返回一個數組,包含對象自身的所有屬性,不管屬性名是Symbol或字符串,也不管是否可枚舉。

以上的5種方法遍歷對象的屬性,都遵守同樣的屬性遍歷的次序規則。

首先遍歷所有屬性名為數值的屬性,按照數字排序。

其次遍歷所有屬性名為字符串的屬性,按照生成時間排序。

最后遍歷所有屬性名為Symbol值的屬性,按照生成時間排序。

__proto__屬性,Object.setPrototypeOf(),Object.getPrototypeOf() __proto__屬性

__proto__屬性(前后各兩個下劃線),用來讀取或設置當前對象的prototype對象。目前,所有瀏覽器(包括 IE11)都部署了這個屬性。該屬性最好不要用。在實現上,__proto__調用的是Object.prototype.__proto__。

Object.setPrototypeOf()

Object.setPrototypeOf方法的作用與__proto__相同,用來設置一個對象的prototype對象,返回參數對象本身。它是 ES6 正式推薦的設置原型對象的方法。

// 格式
Object.setPrototypeOf(object, prototype)

// 用法
var o = Object.setPrototypeOf({}, null);

如果第一個參數不是對象,會自動轉為對象。但是由于返回的還是第一個參數,所以這個操作不會產生任何效果。由于undefined和null無法轉為對象,所以如果第一個參數是undefined或null,就會報錯。

Object.setPrototypeOf(1, {}) === 1 // true

Object.setPrototypeOf(undefined, {})
// TypeError: Object.setPrototypeOf called on null or undefined
Object.getPrototypeOf()

該方法與Object.setPrototypeOf方法配套,用于讀取一個對象的原型對象。

function Rectangle() {
  // ...
}

var rec = new Rectangle();
Object.getPrototypeOf(rec) === Rectangle.prototype
// true

如果參數不是對象,會被自動轉為對象。如果參數是undefined或null,它們無法轉為對象,所以會報錯。

// 等同于 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 object
Object.keys(),Object.values(),Object.entries()

Object.keys:返回一個數組,成員是參數對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。ES5 引入。

Object.values():返回一個數組,成員是參數對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。ES2017 引入。返回數組的成員順序,與《屬性的遍歷》部分介紹的排列規則一致。Object.values會過濾屬性名為 Symbol 值的屬性。如果參數不是對象,Object.values會先將其轉為對象。

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():返回一個數組,成員是參數對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對數組。該方法的行為與Object.values基本一致。原對象的屬性名是一個 Symbol 值,該屬性會被忽略。可將對象轉為真正的Map結構。

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 }

對象的擴展運算符

ES2017 將擴展運算符(...)引入了對象。

(1)解構賦值
對象的解構賦值用于從一個對象取值,相當于將所有可遍歷的、但尚未被讀取的屬性,分配到指定的對象上面。所有的鍵和它們的值,都會拷貝到新對象上面。

解構賦值要求等號右邊是一個對象,所以如果等號右邊是undefined或null,就會報錯,因為它們無法轉為對象。

解構賦值必須是最后一個參數,否則會報錯。

解構賦值的拷貝是淺拷貝,即如果一個鍵的值是復合類型的值(數組、對象、函數)、那么解構賦值拷貝的是這個值的引用,而不是這個值的副本。

解構賦值不會拷貝繼承自原型對象的屬性。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
//變量z是解構賦值所在的對象。它獲取等號右邊的所有尚未讀取的鍵(a和b),將它們連同值一起拷貝過來。



let { x, y, ...z } = null; // 運行時錯誤
let { ...x, y, z } = { x: 1, y: 2, a: 3, b: 4 }; // 句法錯誤
var o = Object.create({ x: 1, y: 2 });
o.z = 3;
let { x, ...{ y, z } } = o;
x // 1
y // undefined
z // 3
//變量x是單純的解構賦值,所以可以讀取對象o繼承的屬性;變量y和z是雙重解構賦值,只能讀取對象o自身的屬性,所以只有變量z可以賦值成功。

(2)擴展運算符
擴展運算符(...)用于取出參數對象的所有可遍歷屬性,拷貝到當前對象之中。這等同于使用Object.assign方法。擴展運算符可以用于合并兩個對象。如果擴展運算符的參數是null或undefined,這個兩個值會被忽略,不會報錯。

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 }; // 不報錯

如果用戶自定義的屬性,放在擴展運算符后面,則擴展運算符內部的同名屬性會被覆蓋掉。

let aWithOverrides = { ...a, x: 1, y: 2 };
// 等同于
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// 等同于
let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });
//a對象的x屬性和y屬性,拷貝到新對象后會被覆蓋掉。

作用:可用來修改現有對象的部分屬性。

let newVersion = {
  ...previousVersion,
  name: "New Name" // Override the name property
};
//newVersion對象自定義了name屬性,其他屬性全部復制自previousVersion對象。
Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptor:返回某個對象屬性的描述對象(descriptor)。ES5引入。

 var obj = { p: "a" };

Object.getOwnPropertyDescriptor(obj, "p")
// Object { value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Object.getOwnPropertyDescriptors:返回指定對象所有自身屬性(非繼承屬性)的描述對象。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方法返回一個對象,所有原對象的屬性名都是該對象的屬性名,對應的屬性值就是該屬性的描述對象。

主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題,因為Object.assign方法總是拷貝一個屬性的值,而不會拷貝它背后的賦值方法或取值方法。Object.getOwnPropertyDescriptors配合Object.create方法,將對象屬性克隆到一個新對象時,屬于淺拷貝。

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 傳導運算符

?.稱為Null 傳導運算符,該運算符若返回null或undefined,就不再往下運算,而是返回undefined。
Null 傳導運算符有四種用法。

obj?.prop // 讀取對象屬性

obj?.[expr] // 同上

func?.(...args) // 函數或對象方法的調用

new C?.(...args) // 構造函數的調用

const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || "default";
//等同于
const firstName = message?.body?.user?.firstName || "default";

參考自:ECMAScript 6 入門

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82663.html

相關文章

  • es6學習筆記--字符串的擴展、數組的擴展、對象的擴展

    摘要:字符串的擴展字符串的遍歷器接口字符串可以被循環遍歷。即能識別編號大于查詢字符串是否包含某個字符返回布爾值,表示是否找到了參數字符串。返回布爾值,表示參數字符串是否在原字符串的頭部。 字符串的擴展 1.字符串的遍歷器接口 字符串可以被for...of循環遍歷。 與es5的比較for循環雖可以遍歷字符串,但不能識別大于oxFFFF的編碼; 2.位置 --> 字符/碼點 根據指定位置返回對應...

    不知名網友 評論0 收藏0
  • es6學習筆記-數值的擴展_V1.0_byKL

    摘要:學習筆記數值的擴展有一些不常用或者還不支持的就沒有記錄了總體來說本篇只是一個備忘而已用來檢查一個數值是否為有限的。兩個新方法只對數值有效,非數值一律返回。參考引用數值擴展 es6學習筆記-數值的擴展 有一些不常用或者還不支持的就沒有記錄了,總體來說本篇只是一個備忘而已 Number.isFinite(), Number.isNaN() Number.isFinite()用來檢查一個數值...

    宋華 評論0 收藏0
  • es6學習筆記-函數擴展_v1.0_byKL

    摘要:學習筆記函數擴展函數參數的默認值如果參數默認值是變量,那么參數就不是傳值的,而是每次都重新計算默認值表達式的值。屬性函數的屬性,返回該函數的函數名。箭頭函數詳細鏈接參考引用函數擴展 es6學習筆記-函數擴展_v1.0 函數參數的默認值 function Point(x = 0, y = 0) { this.x = x; this.y = y; } var p = ne...

    yuanzhanghu 評論0 收藏0
  • es6學習筆記-字符串的擴展_v1.0_byKL

    摘要:學習筆記字符串的擴展字符的表示法允許使用的形式表示一個字符,但在之前,單個碼點僅支持到,超出該范圍的必須用雙字節形式表示,否則會解析錯誤。返回布爾值,表示參數字符串是否在源字符串的頭部。,是引入了字符串補全長度的功能。 es6學習筆記-字符串的擴展_v1.0 字符的Unicode表示法 JavaScript 允許使用uxxxx的形式表示一個字符,但在 ES6 之前,單個碼點僅支持u00...

    JaysonWang 評論0 收藏0
  • babel學習筆記

    摘要:經過一番折騰,總算是把自己項目里的配置調整好了,所有文件從原來的縮小到。折騰了不少時間,改動其實就一個地方,就是配置文件,記錄一下自己折騰的過程。本以為那這兩種方式取其一就行了。這感覺和想象中的不一樣啊,說好的一個搞定一切的呢。。。 先是看到前端早讀課【第1065期】再見,babel-preset-2015,聽說現在有了babel-preset-env,別的什么preset都不需要了,...

    Aomine 評論0 收藏0

發表評論

0條評論

Zoom

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<