ES6各大資料型別的擴充套件

追逐。發表於2019-02-27

一、字串擴充套件

  1. includes()startsWith()endsWith()。傳統JavaScript只有indexOf()方法用來確定一個字串是否包含在另一個字串中,ES6又提供了三個新方法。
  • includes():返回布林值,表示是否找到了引數字串。
  • startsWith():返回布林值,表示引數字串是否在原字串的頭部。
  • endsWith():返回布林值,表示引數字串是否在原字串的尾部。
let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
複製程式碼

這三個方法都支援第二個引數,表示開始搜尋的位置。但是使用第二個引數n時,endsWith的行為與其他兩個方法有所不同。它針對前n個字元,而其他兩個方法針對從第n個位置直到字串結束。

let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
複製程式碼
  1. repeatrepeat方法返回一個新字串,表示將原字串重複n次。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
複製程式碼
  • 引數如果是小數,會被向下取整。
  • 如果repeat的引數是負數或者Infinity,會報錯。
  • 0 到-1 之間的小數,則等同於 0,這是因為會先進行取整運算。0 到-1 之間的小數,取整以後等於-0repeat視同為 0。
  • 引數NaN等同於 0。
  • 如果repeat的引數是字串,則會先轉換成數字。
  1. padStart()padEnd()。ES2017 引入了字串補全長度的功能。如果某個字串不夠指定長度,會在頭部或尾部補全。padStart()用於頭部補全,padEnd()用於尾部補全。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
複製程式碼
  • 如果原字串的長度,等於或大於指定的最小長度,則返回原字串。
  • 如果用來補全的字串與原字串,兩者的長度之和超過了指定的最小長度,則會截去超出位數的補全字串。
  • 如果省略第二個引數,預設使用空格補全長度。
  1. 模板字串。 模板字串是增強版的字串,用反引號(`)標識。它可以當作普通字串使用,也可以用來定義多行字串,或者在字串中嵌入變數。
  • 如果在模板字串中需要使用反引號,則前面要用反斜槓轉義。
  • 如果使用模板字串表示多行字串,所有的空格和縮排都會被保留在輸出之中。
  • 模板字串中嵌入變數,需要將變數名寫在${}之中。
  • 大括號內部可以放入任意的 JavaScript 表示式,可以進行運算,以及引用物件屬性。
  • 模板字串之中還能呼叫函式。
  • 如果大括號中的值不是字串,將按照一般的規則轉為字串。比如,大括號中是一個物件,將預設呼叫物件的toString方法。
  • 如果模板字串中的變數沒有宣告,將報錯。
var name = "追逐",trait = "帥氣";
//es
var str = "我叫"+name+",人非常"+trait+",說話又好聽";

//es6
var str2 = `我叫 ${name} ,人非常 ${trait} ,說話又好聽`;
複製程式碼
  1. 標籤模板。模板字串可以緊跟在一個函式名後面,該函式將被呼叫來處理這個模板字串。這被稱為“標籤模板”功能。
alert`123`
// 等同於
alert(123)
複製程式碼

標籤模板其實不是模板,而是函式呼叫的一種特殊形式。“標籤”指的就是函式,緊跟在後面的模板字串就是它的引數。如果模板字元裡面有變數,就不是簡單的呼叫了,而是會將模板字串先處理成多個引數,再呼叫函式。

let a = 5;
let b = 10;

tag`Hello ${ a + b } world ${ a * b }`;
// 等同於
tag(['Hello ', ' world ', ''], 15, 50);
複製程式碼

二、數值擴充套件

  1. Number.isFinite()Number.isNaN()。ES6 在Number物件上,新提供了Number.isFinite()Number.isNaN()兩個方法。
  • Number.isFinite()用來檢查一個數值是否為有限的(finite)。
Number.isFinite(15); // true
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。和全域性函式 isNaN() 相比,該方法不會強制將引數轉換成數字,只有在引數是真正的數字型別,且值為 NaN 的時候才會返回 true
Number.isNaN(NaN);        // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0)       // true

// 下面這幾個如果使用全域性的 isNaN() 時,會返回 true。
Number.isNaN("NaN");      // false,字串 "NaN" 不會被隱式轉換成數字 NaN。
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("blabla");   // false

// 下面的都返回 false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");
複製程式碼
  1. Number.parseInt()Number.parseFloat()。ES6 將全域性方法parseInt()parseFloat(),移植到Number物件上面,行為完全保持不變。這樣做的目的,是逐步減少全域性性方法,使得語言逐步模組化。
// ES5的寫法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45

// ES6的寫法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45

Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
複製程式碼
  1. Number.isInteger()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
複製程式碼
  1. Math物件的擴充套件。 ES6 在 Math 物件上新增了 17 個與數學相關的方法。所有這些方法都是靜態方法,只能在 Math 物件上呼叫。
  • Math.trunc方法用於去除一個數的小數部分,返回整數部分。對於非數值,Math.trunc內部使用Number方法將其先轉為數值。對於空值和無法擷取整數的值,返回NaN
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
複製程式碼
  • Math.sign方法用來判斷一個數到底是正數、負數、還是零。對於非數值,會先將其轉換為數值。它返回五種值,引數為正數,返回+1;引數為負數,返回-1;引數為 0,返回0;引數為-0,返回-0;其他值,返回NaN
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN

Math.sign('')  // 0
Math.sign(true)  // +1
Math.sign(false)  // 0
Math.sign(null)  // 0
Math.sign('9')  // +1
Math.sign('foo')  // NaN
Math.sign()  // NaN
Math.sign(undefined)  // NaN
複製程式碼
  • Math.cbrt方法用於計算一個數的立方根。對於非數值,Math.cbrt方法內部也是先使用Number方法將其轉為數值。
Math.cbrt(-1) // -1
Math.cbrt(0)  // 0
Math.cbrt(1)  // 1
Math.cbrt(2)  // 1.2599210498948734
複製程式碼
  • Math.hypot方法返回所有引數的平方和的平方根。
Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755
Math.hypot();            // 0
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5');   // 7.0710678118654755
Math.hypot(-3);          // 3
複製程式碼
  1. 指數運算子。ES2016 新增了一個指數運算子(**)。指數運算子可以與等號結合,形成一個新的賦值運算子(**=)。
2 ** 2 // 4
2 ** 3 // 8

let a = 1.5;
a **= 2;
// 等同於 a = a * a;

let b = 4;
b **= 3;
// 等同於 b = b * b * b;
複製程式碼

三、函式擴充套件

  1. 函式引數的預設值。
  • 基本用法。
function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
複製程式碼
  • 與解構賦值預設值結合使用。
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 f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]

// 例二
function f(x, y = 5, z) {
  return [x, y, z];
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]
複製程式碼
  • 函式的length屬性。指定了預設值以後,函式的length屬性,將返回沒有指定預設值的引數個數。也就是說,指定了預設值後,length屬性將失真。fn.length 返回形參個數,arguments.length 返回實參個數。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
複製程式碼
  • 作用域。一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域。等到初始化結束,這個作用域就會消失。這種語法行為,在不設定引數預設值時,是不會出現的。
var x = 1;

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

f(2) // 2
複製程式碼

上面程式碼中,引數y的預設值等於變數x。呼叫函式f時,引數形成一個單獨的作用域。在這個作用域裡面,預設值變數x指向第一個引數x,而不是全域性變數x,所以輸出是2

let x = 1;

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

f() // 1
複製程式碼

上面程式碼中,函式f呼叫時,引數y = x形成一個單獨的作用域。這個作用域裡面,變數x本身沒有定義,所以指向外層的全域性變數x。函式呼叫時,函式體內部的區域性變數x影響不到預設值變數x

var x = 1;

function foo(x = x) {
  // ...
}

foo() // ReferenceError: x is not defined
複製程式碼

上面程式碼中,引數x = x形成一個單獨作用域。實際執行的是let x = x,由於暫時性死區的原因,這行程式碼會報錯”x 未定義“。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}

foo()//3
console.log(x); //1
複製程式碼

如果將var x = 3var去除,函式foo的內部變數x就指向第一個引數x,與匿名函式內部的x是一致的,所以最後輸出的就是2,而外層的全域性變數x依然不受影響。

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x // 1
複製程式碼
  1. rest引數。 ES6 引入 rest 引數(形式為...變數名),用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。rest 引數搭配的變數是一個陣列,該變數將多餘的引數放入陣列中。
function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10
複製程式碼

arguments物件不是陣列,而是一個類似陣列的物件。所以為了使用陣列的方法,必須使用Array.prototype.slice.call先將其轉為陣列。rest 引數就不存在這個問題,它就是一個真正的陣列,陣列特有的方法都可以使用。下面是一個利用 rest 引數改寫陣列push方法的例子。

function push(array, ...items) {
  items.forEach(function(item) {
    array.push(item);
    console.log(item);
  });
}

var a = [];
push(a, 1, 2, 3)
複製程式碼

注意,rest 引數之後不能再有其他引數(即只能是最後一個引數),否則會報錯。

// 報錯
function f(a, ...b, c) {
  // ...
}
複製程式碼

函式的length屬性,不包括 rest 引數。

(function(a) {}).length  // 1
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1
複製程式碼
  1. 嚴格模式。 ES2016規定只要函式引數使用了預設值、解構賦值、或者擴充套件運算子,那麼函式內部就不能顯式設定為嚴格模式,否則會報錯。
  2. name屬性,返回函式名。
function foo() {}
foo.name // "foo"

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

var fn1 = function fn2(){};
fn1.name // "fn2"
複製程式碼
  1. 箭頭函式
  • 基本用法,ES6 允許使用“箭頭”(=>)定義函式。
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語句返回。

var sum = (num1, num2) => { return num1 + num2; }
複製程式碼

由於大括號被解釋為程式碼塊,所以如果箭頭函式直接返回一個物件,必須在物件外面加上括號,否則會報錯。

// 報錯
let getTempItem = id => { id: id, name: "Temp" };

// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
複製程式碼
  • 函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
  • 不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
  • 不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用 rest 引數代替。
  • 不可以使用yield命令,因此箭頭函式不能用作 Generator 函式。
  • this指向的固定化,並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為它沒有this,所以也就不能用作建構函式。
  • 由於箭頭函式沒有自己的this,所以當然也就不能用call()apply()bind()這些方法去改變this的指向。
// 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。
複製程式碼
  1. 函式引數的尾逗號。ES2017允許函式的最後一個引數有尾逗號。這樣的規定也使得,函式引數與陣列和物件的尾逗號規則,保持一致了。
function clownsEverywhere(
  param1,
  param2,
) { /* ... */ }

clownsEverywhere(
  'foo',
  'bar',
);
複製程式碼

四、陣列的擴充套件

  1. 擴充套件運算子是三個點(...)。它好比rest引數的逆運算,將一個陣列轉為用逗號分隔的引數序列。該運算子將一個陣列變為引數序列。
console.log(...[1, 2, 3])
// 1 2 3

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

[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
複製程式碼

擴充套件運算子後面還可以放置表示式。

const arr = [
  ...(x > 0 ? ['a'] : []),
  'b',
];
複製程式碼

如果擴充套件運算子後面是一個空陣列,則不產生任何效果。

[...[], 1]
// [1]
複製程式碼

由於擴充套件運算子可以展開陣列,所以不再需要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);
複製程式碼

取一個陣列中的最大值。

//es5
Math.max.apply(null,[1,5,2,8]) // 8
//es6
Math.max(...[1,5,2,8])//8
//上面兩種方法等同於
Math.max(1,5,2,8)
複製程式碼

擴充套件運算子可以用於複製陣列。

//es5
const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]
a2 // [2, 2]

//es6
const a1 = [1, 2];
// 寫法一
const a2 = [...a1];
// 寫法二
const [...a2] = a1;
複製程式碼

擴充套件運算子用於合併陣列。

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

var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];

// ES5的合併陣列
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6的合併陣列
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
複製程式碼

擴充套件運算子可以與解構賦值結合起來,用於生成陣列。

// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
複製程式碼
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest  // []

const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []
複製程式碼
const [...butLast, last] = [1, 2, 3, 4, 5];
// 報錯

const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 報錯
複製程式碼

擴充套件運算子還可以將字串轉為真正的陣列。

[...'hello']
// [ "h", "e", "l", "l", "o" ]
複製程式碼

擴充套件運算子實現了 Iterator 介面的物件

let nodeList = document.querySelectorAll('div');
let array = [...nodeList];
複製程式碼
  1. Array.from()方法用於將兩類物件轉為真正的陣列。
  • 第一個引數:一個類陣列物件,用於轉為真正的陣列
  • 第二個引數:類似於陣列的map方法,用來對每個元素進行處理,將處理後的值放入返回的陣列。
  • 第三個引數:如果map函式裡面用到了this關鍵字,還可以傳入Array.from的第三個引數,用來繫結this
// NodeList物件
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
  console.log(p);
});

// arguments物件
function foo() {
  var args = Array.from(arguments);
  // ...
}
複製程式碼
  1. Array.of()方法用於將一組值,轉換為陣列。這個方法的主要目的,是彌補陣列建構函式Array()的不足。因為引數個數的不同,會導致Array()的行為有差異。
//Array
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
複製程式碼
  1. 陣列例項的copyWithin方法,在當前陣列內部,將指定位置的成員複製到其他位置(會覆蓋原有成員),然後返回當前陣列。也就是說,使用這個方法,會修改當前陣列。 它接受三個引數。
  • target(必需):從該位置開始替換資料。
  • start(可選):從該位置開始讀取資料,預設為 0。如果為負值,表示倒數。
  • end(可選):到該位置前停止讀取資料,預設等於陣列長度。如果為負值,表示倒數。 這三個引數都應該是數值,如果不是,會自動轉為數值。
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
複製程式碼
  1. 陣列例項的find()findIndex()。這兩個方法都可以接受第二個引數,用來繫結回撥函式的this物件。
  • 陣列例項的find方法,用於找出第一個符合條件的陣列成員。它的引數是一個回撥函式,所有陣列成員依次執行該回撥函式,直到找出第一個返回值為true的成員,然後返回該成員。如果沒有符合條件的成員,則返回undefined
[1, 4, -5, 10].find((n) => n < 0)
// -5
[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10
//find方法的回撥函式可以接受三個引數,依次為當前的值、當前的位置和原陣列。
複製程式碼
  • findIndex方法的用法與find方法非常類似,返回第一個符合條件的陣列成員的位置,如果所有成員都不符合條件,則返回-1
[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2
複製程式碼
  1. 陣列例項的fill()fill方法使用給定值,填充一個陣列。fill方法用於空陣列的初始化非常方便。陣列中已有的元素,會被全部抹去。fill方法還可以接受第二個和第三個引數,用於指定填充的起始位置和結束位置。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]
複製程式碼
  1. for...of是es6引入的作為遍歷所有資料結構的統一的方法。一個資料結構只要部署了Symbol.iterator屬性,就被視為具有 iterator 介面,就可以用for...of迴圈遍歷它的成員。也就是說,for...of迴圈內部呼叫的是資料結構的Symbol.iterator方法。
  2. 陣列例項的 entries()keys()values()用於遍歷陣列,它們都返回一個遍歷器物件,可以用for...of迴圈進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
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"
複製程式碼
  1. 陣列例項的includes()方法返回一個布林值,表示某個陣列是否包含給定的值,與字串的includes方法類似。
[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true
複製程式碼

該方法的第二個參數列示搜尋的起始位置,預設為0。如果第二個引數為負數,則表示倒數的位置,如果這時它大於陣列長度(比如第二個引數為-4,但陣列長度為3),則會重置為從0開始。 沒有該方法之前,我們通常使用陣列的indexOf方法,檢查是否包含某個值。indexOf方法有兩個缺點,一是不夠語義化,它的含義是找到引數值的第一個出現位置,所以要去比較是否不等於-1,表達起來不夠直觀。二是,它內部使用嚴格相等運算子(===)進行判斷,這會導致對NaN的誤判。 10. 陣列的空位指,陣列的某一個位置沒有任何值。 空位不是undefined,一個位置的值等於undefined,依然是有值的。空位是沒有任何值,in運算子可以說明這一點。

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
複製程式碼

es5對空位的處理很不一致,大多數情況下會忽略空位。

  • forEach(), filter(), reduce(), every()some()都會跳過空位。
  • map()會跳過空位,但會保留這個值
  • join()toString()會將空位視為undefined,而undefinednull會被處理成空字串。 es6明確將空位轉為undefined
  • Array.from方法會將陣列的空位,轉為undefined,也就是說,這個方法不會忽略空位。
Array.from(['a',,'b'])
// [ "a", undefined, "b" ]
複製程式碼
  • 擴充套件運算子(...)也會將空位轉為undefined
[...['a',,'b']]
// [ "a", undefined, "b" ]
複製程式碼
  • copyWithin()會連空位一起拷貝。
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
複製程式碼
  • fill()會將空位視為正常的陣列位置。
new Array(3).fill('a') // ["a","a","a"]
複製程式碼
  • for...of迴圈也會遍歷空位。
let arr = [, ,];
for (let i of arr) {
  console.log(1);
}
// 1
// 1
複製程式碼

上面程式碼中,陣列arr有兩個空位,for...of並沒有忽略它們。如果改成map方法遍歷,空位是會跳過的。 entries()keys()values()find()findIndex()會將空位處理成undefined

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0
複製程式碼

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

五、物件的擴充套件

  1. 屬性的簡潔表示法,ES6 允許直接寫入變數和函式,作為物件的屬性和方法。這樣的書寫更加簡潔。
//es6寫法
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}

// 等同於es5寫法
const baz = {foo: foo};

//es6寫法
const o = {
  method() {
    return "Hello!";
  }
};

// 等同於es5寫法
const o = {
  method: function() {
    return "Hello!";
  }
};
複製程式碼
  1. 屬性名錶達式 JavaScript 定義物件的屬性,有兩種方法。
// 方法一
obj.foo = true;

// 方法二
obj['a' + 'bc'] = 123;
複製程式碼

但是,如果使用字面量方式定義物件(使用大括號),在 ES5 中只能使用方法一(識別符號)定義屬性。

var obj = {
  foo: true,
  abc: 123
};
複製程式碼

ES6 允許字面量定義物件時,用方法二(表示式)作為物件的屬性名,即把表示式放在方括號內。

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};
複製程式碼

表示式還可以用於定義方法名。

let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};

obj.hello() // hi
複製程式碼

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

// 報錯
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// 正確
const foo = 'bar';
const baz = { [foo]: 'abc'};
複製程式碼
  1. Object.is()。ES5 比較兩個值是否相等,只有兩個運算子:相等運算子(==)和嚴格相等運算子(===)。它們都有缺點,前者會自動轉換資料型別,後者的NaN不等於自身,以及+0等於-0。JavaScript 缺乏一種運算,在所有環境中,只要兩個值是一樣的,它們就應該相等。 ES6 提出同值相等演算法,用來解決這個問題。Object.is就是部署這個演算法的新方法。它用來比較兩個值是否嚴格相等,與嚴格比較運算子(===)的行為基本一致。
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
複製程式碼

不同之處只有兩個:一是+0不等於-0,二是NaN等於自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
複製程式碼
  1. Object.assign()Object.assign方法用於物件的合併,將源物件(source)的所有可列舉屬性,複製到目標物件(target)。
const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
console.log(target); // {a:1, b:2, c:3}
複製程式碼

如果只有一個引數,Object.assign會直接返回該引數。

const obj = {a: 1};
Object.assign(obj) === obj // true
複製程式碼

由於undefinednull無法轉成物件,所以如果它們作為引數,就會報錯。

Object.assign(undefined) // 報錯
Object.assign(null) // 報錯
複製程式碼

注意:Object.assign可以用來處理陣列,但是會把陣列視為物件。

Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
//把陣列視為屬性名為 0、1、2 的物件,因此源陣列的 0 號屬性4覆蓋了目標陣列的 0 號屬性1。
複製程式碼
  1. Object.keys()。ES5 引入了Object.keys方法,返回一個陣列,成員是引數物件自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
複製程式碼

ES2017 引入了跟Object.keys配套的Object.valuesObject.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]
}
複製程式碼
  1. Object.values()Object.values方法返回一個陣列,成員是引數物件自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
複製程式碼

返回陣列的成員順序,下面的程式碼中,屬性名為數值的屬性,是按照數值大小,從小到大遍歷的,因此返回的順序是bca

const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"]
複製程式碼
  1. Object.entries()Object.entries方法返回一個陣列,成員是引數物件自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對陣列。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
複製程式碼
  1. 物件的擴充套件運算子。
  • 解構賦值。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
複製程式碼

由於解構賦值要求等號右邊是一個物件,所以如果等號右邊是undefinednull,就會報錯,因為它們無法轉為物件。

let { x, y, ...z } = null; // 執行時錯誤
let { x, y, ...z } = undefined; // 執行時錯誤
複製程式碼

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

let { ...x, y, z } = obj; // 句法錯誤
let { x, ...y, ...z } = obj; // 句法錯誤
複製程式碼

注意,解構賦值的拷貝是淺拷貝,即如果一個鍵的值是複合型別的值(陣列、物件、函式)、那麼解構賦值拷貝的是這個值的引用,而不是這個值的副本。

let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2; 
console.log(x.a.b); // 2
複製程式碼
  • 擴充套件運算子 擴充套件運算子(...)用於取出引數物件的所有可遍歷屬性,拷貝到當前物件之中。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
複製程式碼

這等同於使用Object.assign方法。

let aClone = { ...a };
// 等同於
let aClone = Object.assign({}, a);
複製程式碼

相關文章