ES6語法筆記(函式、陣列、物件)
**以下內容均摘自ECMAScript 6 入門——阮一峰
一、函式
1.函式的預設值與解構賦值
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined 5 foo({x: 1}) // 1 5 foo({x: 1, y: 2}) // 1 2 foo() // TypeError: Cannot read property 'x' of undefined function foo({x, y = 5} = {}) { console.log(x, y); } foo() // undefined 5
//寫法一 function m1({x = 0, y = 0} = {}) { return [x, y]; } // 寫法二 function m2({x, y} = { x: 0, y: 0 }) { return [x, y]; } // 函式沒有引數的情況 m1() // [0, 0] m2() // [0, 0] // x 和 y 都有值的情況 m1({x: 3, y: 8}) // [3, 8] m2({x: 3, y: 8}) // [3, 8] // x 有值,y 無值的情況 m1({x: 3}) // [3, 0] m2({x: 3}) // [3, undefined] // x 和 y 都無值的情況 m1({}) //[0, 0]; m2({}) // [undefined, undefined] m1({z: 3}) // [0, 0] m2({z: 3}) // [undefined, undefined]
寫法一有預設物件再對預設物件進行解構賦值,寫法二隻有預設物件。
2.定義了預設值的引數,應該是函式的尾引數,因為可以省略。
3.函式的length返回沒有指定預設值的引數的個數。
(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2 //如果設定了預設值的引數不是尾引數,那麼length屬性也不再計入後面的引數了。(function (a, b = 1, c) {}).length // 1
4.一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域(context)。
var x = 1; function f(x, y = x) { console.log(y); } f(2) // 2
5.rest 引數搭配的變數是一個數組,該變數將多餘的引數放入陣列中
function push(array, ...items) { items.forEach(function(item) { array.push(item); console.log(item); }); } var a = []; push(a, 1, 2, 3)
rest 引數之後不能再有其他引數(即只能是最後一個引數),否則會報錯。
6.ES6中指定嚴格模式第一種是設定全域性性的嚴格模式,這是合法的。第二種是把函式包在一個無引數的立即執行函式裡面。
函式引數使用了預設值、解構賦值、或者擴充套件運算子,那麼函式內部就不能顯式設定為嚴格模式,否則會報錯。
7.函式的name
屬性,返回該函式的函式名。
8.箭頭函式
var f = v => v; // 等同於 var f = function (v) { return v; }; var f = () => 5; // 等同於 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同於 var sum = function(num1, num2) { return num1 + num2; };
//如果箭頭函式的程式碼塊部分多於一條語句,就要使用大括號將它們括起來,並且使用return語句返回。 //由於大括號被解釋為程式碼塊,所以如果箭頭函式直接返回一個物件,必須在物件外面加上括號,否則會報錯。 // 報錯 let getTempItem = id => { id: id, name: "Temp" }; // 不報錯 let getTempItem = id => ({ id: id, name: "Temp" });
*箭頭函式使用注意事項
(1)函式體內的this
物件,就是定義時所在的物件,而不是使用時所在的物件。(箭頭函式本身沒有this,是引用的外層物件的this,是固定的)
(2)不可以當作建構函式,也就是說,不可以使用new
命令,否則會丟擲一個錯誤。
(3)不可以使用arguments
物件,該物件在函式體內不存在。如果要用,可以用 rest 引數代替。
(4)不可以使用yield
命令,因此箭頭函式不能用作 Generator 函式。
9.尾呼叫指某個函式的最後一步是呼叫另一個函式。只保留內層函式的呼叫幀,大大節省記憶體,這就叫做“尾呼叫優化”。
二、陣列的拓展
1.拓展運算子(spread)是三個點(...
)。它好比 rest 引數的逆運算,將一個數組轉為用逗號分隔的引數序列。
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5
//由於擴充套件運算子可以展開陣列,所以不再需要apply方法,將陣列轉為函式的引數了。 // ES5 的寫法 function f(x, y, z) { // ... } var args = [0, 1, 2]; f.apply(null, args); // ES6的寫法 function f(x, y, z) { // ... } let args = [0, 1, 2]; f(...args);
2.擴充套件運算子的應用
擴充套件運算子提供了陣列深度複製的簡便寫法。
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
與結構賦值結合(只能位於最後一位)
const [first, ...rest] = [1, 2, 3, 4, 5]; first // 1 rest // [2, 3, 4, 5]
將字串轉化為真正的陣列
[...'hello'] // [ "h", "e", "l", "l", "o" ]
任何 Iterator 介面的物件,都可以用擴充套件運算子轉為真正的陣列。
3.Array.from()將類似陣列的物件(array-like object)和可遍歷(iterable)的物件(包括 ES6 新增的資料結構 Set 和 Map)轉為真正的陣列。
所謂類似陣列的物件,本質特徵只有一點,即必須有length
屬性。
Array.from({ length: 3 }); // [ undefined, undefined, undefined ]
Array.from
還可以接受第二個引數,作用類似於陣列的map
方法,用來對每個元素進行處理,將處理後的值放入返回的陣列。
let spans = document.querySelectorAll('span.name'); // map() let names1 = Array.prototype.map.call(spans, s => s.textContent); // Array.from() let names2 = Array.from(spans, s => s.textContent)
4.Array.of()方法用於將一組值,轉換為陣列。
Array.of() // [] Array.of(undefined) // [undefined] Array.of(1) // [1] Array.of(1, 2) // [1, 2]
5.copyWithin()
方法在當前陣列內部,將指定位置的成員複製到其他位置(會覆蓋原有成員),然後返回當前陣列。
[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
6. find() 方法找出符合條件的值(找不到返回undefined
) findIndex()方法返回符合條件值的下標(找不到返回-1)。
接受三個引數,依次為當前的值、當前的位置和原陣列。
[1, 5, 10, 15].find(function(value, index, arr) { return value > 9; }) // 10 [1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) // 2
7.fill()方法以給定值填充一個數組。
//可以接受第二個和第三個引數,用於指定填充的起始位置和結束位置。 ['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
8.entries(),keys() 和 values()與for..of配合遍歷陣列
for (let index of ['a', 'b'].keys()) { console.log(index); } // 0 // 1 for (let elem of ['a', 'b'].values()) { console.log(elem); } // 'a' // 'b' for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); } // 0 "a" // 1 "b"
9.includes()方法表示某個陣列是否包含給定的值。
[1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false [1, 2, NaN].includes(NaN) // true
10.flat()方法用於拉平多維陣列(預設拉平1層) flatMap()
方法對原陣列的每個成員執行一個函式,然後對返回值組成的陣列執行flat()
方法。
[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]] [1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
// 相當於 [[2, 4], [3, 6], [4, 8]].flat() [2, 3, 4].flatMap((x) => [x, x * 2]) // [2, 4, 3, 6, 4, 8] //只能展開一層 // 相當於 [[[2]], [[4]], [[6]], [[8]]].flat() [1, 2, 3, 4].flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]
11.陣列中處理空位[,,]的規則非常不統一,應避免出現空位。
三、物件
1.屬性與函式的簡寫形式
function f(x, y) { return {x, y}; } // 等同於 function f(x, y) { return {x: x, y: y}; } f(1, 2) // Object {x: 1, y: 2} const o = { method() { return "Hello!"; } }; // 等同於 const o = { method: function() { return "Hello!"; } };
2.屬性名錶達式。ES6 允許字面量定義物件時,用表示式作為物件的屬性名,即把表示式放在方括號內。
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123 }; let obj = { ['h' + 'ello']() { return 'hi'; } }; obj.hello() // hi
屬性名錶達式與簡潔表示法,不能同時使用,會報錯。
3.函式的name
屬性,返回函式名。物件方法也是函式,因此也有name
屬性。
const person = { sayName() { console.log('hello!'); }, }; person.sayName.name // "sayName"
4.屬性的遍歷方法
(1)for...in
迴圈遍歷物件自身的和繼承的可列舉屬性(不含 Symbol 屬性)。
(2)Object.keys(obj)
返回一個數組,包括物件自身的(不含繼承的)所有可列舉屬性(不含 Symbol 屬性)的鍵名。
(3)Object.getOwnPropertyNames(obj)
返回一個數組,包含物件自身的所有屬性(不含 Symbol 屬性,但是包括不可列舉屬性)的鍵名。
(4)Object.getOwnPropertySymbols(obj)
返回一個數組,包含物件自身的所有 Symbol 屬性的鍵名。
(5)Reflect.ownKeys(obj)
返回一個數組,包含物件自身的所有鍵名,不管鍵名是 Symbol 或字串,也不管是否可列舉。
5.super關鍵字,指向當前物件的原型物件。
const proto = { foo: 'hello' }; const obj = { foo: 'world', find() { return super.foo; } }; Object.setPrototypeOf(obj, proto); obj.find() // "hello"
目前,只有物件方法的簡寫法可以讓 JavaScript 引擎確認,定義的是物件的方法。其他方式使用super均會報錯。
6.物件的解構賦值,將所有未分配,可遍歷的屬性分配到指定的物件上。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 } //本質上是淺拷貝 let obj = { a: { b: 1 } }; let { ...x } = obj; obj.a.b = 2; x.a.b // 2 //不能複製繼承自原型物件的屬性 let o1 = { a: 1 }; let o2 = { b: 2 }; o2.__proto__ = o1; let { ...o3 } = o2; o3 // { b: 2 } o3.a // undefined
7.物件的擴充套件運算子(...
)用於取出引數物件的所有可遍歷屬性,拷貝到當前物件之中。
let z = { a: 3, b: 4 }; let n = { ...z }; n // { a: 3, b: 4 } //擴充套件運算子可以用於合併兩個物件。 let ab = { ...a, ...b }; // 等同於 let ab = Object.assign({}, a, b);
如果使用者自定義的屬性,放在擴充套件運算子後面,則擴充套件運算子內部的同名屬性會被覆蓋掉。
如果把自定義屬性放在擴充套件運算子前面,就變成了設定新物件的預設屬性值。
四、物件的拓展方法
1.Object.is()比較兩個值是否嚴格相等
Object.is('foo', 'foo') // true Object.is({}, {}) // false +0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
2.Object.assign()方法用於物件的合併
const target = { a: 1 }; const source1 = { b: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
後面物件的屬性會覆蓋前面物件的屬性 當首引數為非物件時,會轉化為物件
Object.assign
方法實行的是淺拷貝
const obj1 = {a: {b: 1}}; const obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2
//Object.assign可以用來處理陣列,但是會把陣列視為物件。 Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
3.Object.getOwnPropertyDescriptor()
方法會返回某個物件屬性的描述物件(descriptor)
const obj = { foo: 123, get bar() { return 'abc' } }; Object.getOwnPropertyDescriptors(obj) // { foo: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: get bar], // set: undefined, // enumerable: true, // configurable: true } }
4.__proto__屬性(前後各兩個下劃線),用來讀取或設定當前物件的prototype
物件(瀏覽器必須部署這個屬性,其他執行環境不一定需要部署)
Object.setPrototypeOf
方法的作用與__proto__
相同,用來設定一個物件的prototype
物件,返回引數物件本身。它是 ES6 正式推薦的設定原型物件的方法。
Object.getPrototypeOf用於讀取一個物件的原型物件。
let proto = {}; let obj = { x: 10 }; Object.setPrototypeOf(obj, proto); proto.y = 20; proto.z = 40; obj.x // 10 obj.y // 20 obj.z // 40
5.Object.keys(),Object.values(),Object.entries()
Object.keys
方法,返回一個數組,成員是引數物件自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。
Object.values
方法返回一個數組,成員是引數物件自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。
//如果Object.values方法的引數是一個字串,會返回各個字元組成的一個數組。 Object.values('foo') // ['f', 'o', 'o'] //由於數值和布林值的包裝物件,都不會為例項新增非繼承的屬性。所以,Object.values會返回空陣列。 Object.values(42) // [] Object.values(true) // [
Object.entries()
方法返回一個數組,成員是引數物件自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對陣列。
Object.fromEntries()
方法是Object.entries()
的逆操作,用於將一個鍵值對陣列轉為物件。
const obj = { foo: 'bar', baz: 42 }; Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ] Object.fromEntries([ ['foo', 'bar'], ['baz', 42] ]) // { foo: "bar", baz: 42 }
Object.values Object.entries會忽略Symbol 屬性
Object.entries({ [Symbol()]: 123, foo: 'abc' }); // [ [ 'foo', 'abc' ] ]