1. 程式人生 > >ES6語法筆記(函式、陣列、物件)

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