[ ES6 ](二)函式陣列物件擴充套件
函式的擴充套件
函式引數的預設值
ES5: function log(x, y) { y = y || 'World'; console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello World ES6: function Point(x = 0, y = 0) { this.x = x; this.y = y; } const p = new Point(); p // { x: 0, y: 0 } function foo(x = 5, y = 6) { console.log(x, y); } foo(undefined, null) // 5 null
作用域
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo() // 2
x // 1
箭頭函式 ***
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
使用注意點
箭頭函式有幾個使用注意點。 (1)函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
(2)不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。。 (3)不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用 rest 引數代替。。 (4)不可以使用yield命令,因此箭頭函式不能用作 Generator 函式。
箭頭函式可以讓setTimeout裡面的this,繫結定義時所在的作用域,而不是指向執行時所在的作用域。下面是另一個例子。
function Timer() { this.s1 = 0; this.s2 = 0; // 箭頭函式 setInterval(() => this.s1++, 1000); // 普通函式 setInterval(function () { this.s2++; }, 1000); } var timer = new Timer(); setTimeout(() => console.log('s1: ', timer.s1), 3100); setTimeout(() => console.log('s2: ', timer.s2), 3100); // s1: 3 // s2: 0
上面程式碼中,Timer函式內部設定了兩個定時器,分別使用了箭頭函式和普通函式。前者的this繫結定義時所在的作用域(即Timer函式),後者的this指向執行時所在的作用域(即全域性物件)。所以,3100 毫秒之後,timer.s1被更新了 3 次,而timer.s2一次都沒更新。
不適用場合
由於箭頭函式使得this從“動態”變成“靜態”,下面兩個場合不應該使用箭頭函式。 第一個場合是定義函式的方法,且該方法內部包括this
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
上面程式碼中,cat.jumps()方法是一個箭頭函式,這是錯誤的。呼叫cat.jumps()時,如果是普通函式,該方法內部的this指向cat;如果寫成上面那樣的箭頭函式,使得this指向全域性物件,因此不會得到預期結果。
第二個場合是需要動態this的時候,也不應使用箭頭函式。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
上面程式碼執行時,點選按鈕會報錯,因為button的監聽函式是一個箭頭函式,導致裡面的this就是全域性物件。如果改成普通函式,this就會動態指向被點選的按鈕物件。
另外,如果函式體很複雜,有許多行,或者函式內部有大量的讀寫操作,不單純是為了計算值,這時也不應該使用箭頭函式,而是要使用普通函式,這樣可以提高程式碼可讀性。
陣列的擴充套件
複製陣列
const a1 = [1, 2];
// 寫法一
const a2 = [...a1];
// 寫法二
const [...a2] = a1;
合併陣列
const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];
const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];
a3[0] === a1[0] // true
a4[0] === a1[0] // true
上面程式碼中,a3和a4是用兩種不同方法合併而成的新陣列,但是它們的成員都是對原陣列成員的引用,這就是淺拷貝。如果修改了原陣列的成員,會同步反映到新陣列。
Array.from() 用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iterable)的物件
Array.of() 用於將一組值,轉換為陣列
陣列例項的 copyWithin() 在當前陣列內部,將指定位置的成員複製到其他位置(會覆蓋原有成員),然後返回當前陣列
陣列例項的 find() 和 findIndex() 找出第一個符合條件的陣列成員。它的引數是一個回撥函式,所有陣列成員依次執行該回調函式,直到找出第一個返回值為true的成員,然後返回該成員。
陣列例項的 fill() 使用給定值,填充一個數組
陣列例項的 entries(),keys() 和 values() 用於遍歷陣列。它們都返回一個遍歷器物件
陣列例項的 includes() 返回一個布林值,表示某個陣列是否包含給定的值
陣列例項的 flat(),flatMap() 將巢狀的陣列“拉平”,變成一維的陣列。該方法返回一個新陣列,對原資料沒有影響。
物件的擴充套件
Object.is() 用來比較兩個值是否嚴格相等
Object.assign() 用於物件的合併,將源物件(source)的所有可列舉屬性,複製到目標物件(target)/實行的是淺拷貝,而不是深拷貝
屬性的遍歷 ES6 一共有 5 種方法可以遍歷物件的屬性。
(1)for…in
for…in迴圈遍歷物件自身的和繼承的可列舉屬性(不含 Symbol 屬性)。
(2)Object.keys(obj)
Object.keys返回一個數組,包括物件自身的(不含繼承的)所有可列舉屬性(不含 Symbol 屬性)的鍵名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一個數組,包含物件自身的所有屬性(不含 Symbol 屬性,但是包括不可列舉屬性)的鍵名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一個數組,包含物件自身的所有 Symbol 屬性的鍵名。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一個數組,包含物件自身的所有鍵名,不管鍵名是 Symbol 或字串,也不管是否可列舉。
以上的 5 種方法遍歷物件的鍵名,都遵守同樣的屬性遍歷的次序規則。
首先遍歷所有數值鍵,按照數值升序排列。 其次遍歷所有字串鍵,按照加入時間升序排列。 最後遍歷所有 Symbol 鍵,按照加入時間升序排列。
Object.getOwnPropertyDescriptors() 返回指定物件所有自身屬性(非繼承屬性)的描述物件。
__proto__屬性 用來設定一個物件的prototype物件,返回引數物件本身
Object.setPrototypeOf() 用來設定一個物件的prototype物件,返回引數物件本身
Object.getPrototypeOf() 用於讀取一個物件的原型物件
super 關鍵字 this關鍵字總是指向函式所在的當前物件,關鍵字super,指向當前物件的原型物件。
Object.keys() ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作為遍歷一個物件的補充手段,供for...of迴圈使用。
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}