ES6, 全稱 ECMAScript 6.0 ,2015.06 發版。es6
let 命令,用來聲明變量。它的用法相似於var,區別在於var聲明的變量全局有效,let 聲明的變量只在它所在的代碼塊內有效。數組
使用var聲明:瀏覽器
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
使用閉包解決:安全
var a = []; for (var i = 0; i < 10; i++) { (function(i){ a[i] = function () { console.log(i); }; })(i); } a[6](); // 6
使用let:數據結構
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
const 聲明一個只讀的常量。一旦聲明,常量的值就不能改變。不能只聲明不賦值。閉包
const a = 10; a = 20; // 報錯 const b; // 報錯
const 的做用域與 let 相同。ide
if(true) { const num = 5; } console.log(num); // 報錯
const 聲明對象,常量對象內存地址,所以對象自己可改,可是給常量從新賦值就會報錯。函數
const obj = {}; obj.a = 'a'; obj = {}; // 報錯
ES5 只有全局做用域和函數做用域,沒有塊級做用域,這帶來不少不合理的場景。this
第一種場景,內層變量可能會覆蓋外層變量。spa
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined
第二種場景,用來計數的循環變量泄露爲全局變量。
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
ES6的塊級做用域
let實際上爲 JavaScript 新增了塊級做用域。
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
塊級做用域的出現,實際上使得得到普遍應用的匿名當即執行函數表達式(匿名 IIFE)再也不必要了。
// IIFE 寫法 (function () { var tmp = ...; ... }()); // 塊級做用域寫法 { let tmp = ...; ... }
ES5 規定,函數只能在頂層做用域和函數做用域之中聲明,不能在塊級做用域聲明。
// 狀況一 if (true) { function f() {} } // 狀況二 try { function f() {} } catch(e) { // ... }
上面兩種函數聲明,根據 ES5 的規定都是非法的。
ES6 引入了塊級做用域,明確容許在塊級做用域之中聲明函數。ES6 規定,塊級做用域之中,函數聲明語句的行爲相似於let,在塊級做用域以外不可引用。
function f() { console.log('I am outside!'); } (function () { if (false) { // 重複聲明一次函數f function f() { console.log('I am inside!'); } } f(); }());
上面代碼在 ES5 中運行,會獲得「I am inside!」,由於在if內聲明的函數f會被提高到函數頭部,實際運行的代碼以下。
// ES5 環境 function f() { console.log('I am outside!'); } (function () { function f() { console.log('I am inside!'); } if (false) { } f(); }());
ES6 就徹底不同了,理論上會獲得「I am outside!」。由於塊級做用域內聲明的函數相似於let,對做用域以外沒有影響。可是,若是你真的在 ES6 瀏覽器中運行一下上面的代碼,是會報錯的,這是爲何呢?
// 瀏覽器的 ES6 環境 function f() { console.log('I am outside!'); } (function () { if (false) { // 重複聲明一次函數f function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function
上面的代碼在 ES6 瀏覽器中,都會報錯。
原來,若是改變了塊級做用域內聲明的函數的處理規則,顯然會對老代碼產生很大影響。爲了減輕所以產生的不兼容問題,瀏覽器的實現能夠不遵照上面的規定,有本身的行爲方式。
注意,上面三條規則只對 ES6 的瀏覽器實現有效,其餘環境的實現不用遵照,仍是將塊級做用域的函數聲明看成let處理。
根據這三條規則,瀏覽器的 ES6 環境中,塊級做用域內聲明的函數,行爲相似於var聲明的變量。上面的例子實際運行的代碼以下。
// 瀏覽器的 ES6 環境 function f() { console.log('I am outside!'); } (function () { var f = undefined; if (false) { function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function
考慮到環境致使的行爲差別太大,應該避免在塊級做用域內聲明函數。若是確實須要,也應該寫成函數表達式,而不是函數聲明語句。
另外,還有一個須要注意的地方。ES6的塊級做用域必須有大括號,若是沒有大括號,JavaScript 引擎就認爲不存在塊級做用域。
// 第一種寫法,報錯 if (true) let x = 1; // 第二種寫法,不報錯 if (true) { let x = 1; }
上面代碼中,第一種寫法沒有大括號,因此不存在塊級做用域,而let只能出如今當前做用域的頂層,因此報錯。第二種寫法有大括號,因此塊級做用域成立。
函數聲明也是如此,嚴格模式下,函數只能聲明在當前做用域的頂層。
// 不報錯 if (true) { function f() {} } // 報錯 'use strict'; if (true) function f() {}
ES6 容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構(Destructuring)。
模式匹配賦值,若是解構不成功,變量的值就等於 undefined。
let [a, [[b], c]] = [1, [[2], 3]]; console.log(a,b,c); // 1, 2, 3 let [x, , y, z] = [1, 2, 3]; console.log(x); // 1 console.log(y); // 3 console.log(z); // undefined
不徹底解構賦值,等號左邊的模式,只匹配一部分的等號右邊的數組。
let [x, [y], z] = [1, [2, 3], 4]; console.log(x); // 1 console.log(y); // 2 console.log(z); // 4
數組結構賦值右邊必須是數組,模式不匹配則報錯。
let [a] = {}; // 報錯
解構賦值能夠添加默認值,而且能夠引用解構賦值的其餘變量。
let [a = 1, b = 2] = [, 3]; console.log(a); // 1 console.log(b); // 3 let [x = 1, y = x] = []; // x = 1; y = 1 let [x = 1, y = x] = [2]; // x = 2; y = 2
數組解構賦值可用於交換變量的值。
let [a, b] = [1, 2]; console.log(a, b); // 1, 2 [b, a] = [a, b]; console.log(a, b); // 2, 1
變量必須與屬性同名
let { a, b, c } = { a: 'aaa', b: 'bbb' }; console.log(a); // 'aaa' console.log(b); // 'bbb' console.log(c); // undefined
變量名與屬性名不一致
let { a: x, b: y } = { a: 'aaa', b: 'bbb' }; console.log(x); // 'aaa' console.log(y); // 'bbb'
嵌套賦值,若是子對象所在的父屬性不存在,會報錯,慎用。
let { a, a: {x}, b: y } = { a: {x: 'xxx',y: 'yyy'}, b: "bbb" }; console.log(a); // { x: 'xxx', y: 'yyy' } console.log(x); // 'xxx' let {c: {d: {e}}} = {c: 'ccc'}; // 報錯 console.log(e)
字符串解構賦值,將字符串轉化成數組對象
const [a,b,c] = '123456789'; const {length} = '123456789'; console.log(a, b, c, length); // 1, 2, 3, 9
相似數組的對象都有一個length屬性,所以還能夠對這個屬性解構賦值。
let {length : len} = 'hello'; len // 5
function add([x, y]){ return x + y; } add([1, 2]); // 3
下面是另外一個例子:
const arr = [[1, 2], [3, 4]].map(([a, b]) => a + b); console.log(arr); // [ 3, 7 ]
函數參數的解構也可使用默認值。
function move({x = 0, y = 0} = {}) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, 0] move({}); // [0, 0] move(); // [0, 0]
上面代碼中,函數move的參數是一個對象,經過對這個對象進行解構,獲得變量x和y的值。若是解構失敗,x和y等於默認值。
注意,下面的寫法會獲得不同的結果。
function move({x, y} = { x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0]
上面代碼是爲函數move的參數指定默認值,而不是爲變量x和y指定默認值,因此會獲得與前一種寫法不一樣的結果。
for(let codePoint of 'string'){ console.log(codePoint) } // 's' // 't' // 'r' // 'i' // 'n' // 'g'
let s = 'Hello world!'; const [a, b, c] = [ s.startsWith('Hello', 2), s.endsWith('!'), s.includes('o w') ]; console.log(a, b, c); // false true true
repeat 方法返回一個新字符串,表示將原字符串重複 n 次。
'str'.repeat('3') // 'strstrstr'
padStart(),padEnd()有兩個參數,第一個參數爲字符串補全生效的最大長度,第二個參數爲補全的字符串。
第二個參數默認爲空格,省略第二個參數時默認用空格補全。
第一個參數小於字符串原長度時,返回原字符串。
若是用來補全的字符串與原字符串,二者的長度之和超過了最大長度,則會截去超出位數的補全字符串。
常見用途:補全指定位數,提示字符串格式。
'123456'.padStart(10, '0') // "0000123456" '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
const str = 'world'; const template = `Hello ${str}`; console.log(template); // Hello world
使用二進制表示法,前綴爲 0b,使用八進制表示法,前綴爲 0o,ES6 不支持使用 00 前綴表示八進制。
進制轉換使用 toString 方法,使用 Number 方法直接轉十進制。
0b1100100 === 100; // true 0o144 === 100; // true (0b1100100).toString(8); // 144 (0b1100100).toString(10); // 100 Number('0b1100100'); // 100
Number.isFinite()用來檢查一個數值是否爲有限的(finite),即不是 Infinity。參數類型不是數值,Number.isFinite 一概返回 false。
Number.isNaN()用來檢查一個值是否爲 NaN。參數類型不是 NaN,Number.isNaN 一概返回 false。
Number.isFinite(15); // true Number.isFinite(-Infinity); // false Number.isNaN(15) // false Number.isNaN(9/0) // true
ES6 將全局方法 parseInt()和parseFloat(),移植到Number對象上面,行爲徹底保持不變。
Number.isInteger()用來判斷一個數值是否爲整數。
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false
Number.isSafeInteger(9007199254740993) // false Number.isSafeInteger(990) // true Number.isSafeInteger(9007199254740993 - 990) // true
Math.trunc() 除去一個數的小數部分,返回整數部分。參數不是數值,內部會先調用 Number()專爲數值,對於空值和沒法截取整數的值,返回 NaN。(Math 對象的擴展的方法對於非數值的處理方法都同樣)
Math.trunc(5.9) // 5 Math.trunc(-4.9) // -4 Math.trunc(null) // 0 Math.trunc('foo'); // NaN
Math.sign() 判斷一個數是正數、負數、仍是零。
Math.sign(-5) // -1 負數 Math.sign(5) // +1 正數 Math.sign(0) // +0 零 Math.sign(-0) // -0 零 Math.sign(NaN) // NaN
Math.cbrt() 計算一個數的立方根。
Math.cbrt(2) // 1.2599210498948734 // Math.sqrt(x) 計算平方根 Math.sqrt(2) // 1.4142135623730951 // 冪運算 Math.pow(x,y) Math.pow(2, 3)
Math.hypot() 返回全部參數的平方和的平方根。
Math.hypot(3, 4); // 5 Math.hypot(3, 4, 5); // 7.0710678118654755
ES6 引入 rest 參數(形式爲...變量名),用於獲取函數的多餘參數,rest 參數搭配的變量是一個數組,該變量將多餘的參數放入數組中。只能是最後一個參數,函數的 length 屬性,不包括 rest 參數。
function sum1(x, y, ...args) { let sum = 0; for (let arg of args) { sum += arg; } return sum; } console.log(sum1(1, 2, 3, 4)) // 7 function sum2(...args) { return args.reduce((prev, curr) => { return prev + curr }, 0) } console.log(sum2(1, 2, 3)); // 6
函數的 name 屬性,返回該函數的函數名。對於匿名函數,ES5 返回' ',ES6 返回變量名; Function 構造函數返回的函數實例,name 屬性的值爲 anonymous;bind 返回的函數,name 屬性值會加上 bound 前綴。
function fn() {} fn.name // 'fn' function foo() {}; foo.bind({}).name // 'bound foo' (new Function).name // "anonymous" (function(){}).bind({}).name // 'bound '
const fn = v => v; // 等同於 const fn = function (v) { return v; };
注意要點
擴展運算符(spread)是三個點(...)。它比如rest參數的逆運算,將一個數組轉爲用逗號分隔的參數序列。
const arr = [1, 2, 3];
arr.push(...[4, 5, 6]);
擴展運算符的應用:
const arr = [1, 2, 3]; ...arr // 1, 2, 3
const a1 = [1, 2]; // 寫法一 const a2 = [...a1]; // 寫法二 const [...a2] = a1; // 至關於 const a1 = [1, 2]; const a2 = a1.concat();
const list = [1, 2, 3]; [a, ...b] = list; console.log(a) // 1 console.log(b) // [2, 3] [...'hello'] // ['h', 'e', 'l', 'l', 'o']
Array.from 方法用於將兩類對象轉爲真正的數組:相似數組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數據結構 Set 和 Map)。
常見的相似數組的對象有 DOM 操做返回的 NodeList 集合,以及函數內部的 arguments 對象。
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'] Array.from('hello'); // ['h', 'e', 'l', 'l', 'o'] let namesSet = new Set(['a', 'b']); Array.from(namesSet); // ['a', 'b']
Array.from 還能夠接受第二個參數,做用相似於數組的map方法,用來對每一個元素進行處理,將處理後的值放入返回的數組。
let arrayLike = { '0': 1, '1': 2, '2': 3, length: 3 }; Array.from(arrayLike, x => x * x); // [ 1, 4, 9 ]
Array.of 方法用於將一組值,轉換爲數組。這個方法的主要目的,是彌補數組構造函數 Array()的不足。由於參數個數的不一樣,會致使 Array()的行爲有差別。
Array.of() // [] Array.of(undefined) // [undefined] Array.of(1) // [1] Array.of(1, 2) // [1, 2]
參數:
這三個參數都應該是數值,若是不是,會自動轉爲數值。
var result = [1, 2, 3, 4, 5].copyWithin(0, 3) console.log(result)//[4,5,3,4,5]
數組實例的 find 方法,用於找出第一個符合條件的數組成員,若是沒有符合條件的成員,則返回 undefined。
findIndex 方法返回第一個符合條件的數組成員的位置,若是全部成員都不符合條件,則返回-1。
[1, 4, -5, 10].find(n => n < 0); // -5 [1, 4, -5, 10].findIndex(n => n < 0); // 2
兩個方法均可以接受第二個參數,用來綁定回調函數的 this 對象。
function f(v){ return v > this.age; } let person = {name: 'John', age: 20}; [10, 12, 26, 15].find(f, person); // 26
這兩個方法均可以發現 NaN,彌補了數組的 indexOf 方法的不足。
fill 方法使用給定值,填充一個數組。fill 方法能夠接受第二個和第三個參數,用於指定填充的起始位置和結束位置。若是填充的類型爲對象,那麼被賦值的是同一個內存地址的對象,而不是深拷貝對象,改變數組中的一項,則全部項都改變。
let arr = Array.of(1, 2, 3).fill({ num: 20 }); console.log(arr); // [ { num: 20 }, { num: 20 }, { num: 20 } ] arr[0].num = 10; console.log(arr); // [ { num: 10 }, { num: 10 }, { num: 10 } ]
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"
includes 方法返回一個布爾值,表示某個數組是否包含給定的值,與字符串的 includes 方法相似。該方法的第二個參數表示搜索的起始位置,第二參數是負數,取它的倒數,第二參數大於數組長度,取 0。
[1, 2, 3].includes(3, -1); // true
flat()默認只會「拉平」一層,若是想要「拉平」多層的嵌套數組,能夠將 flat()方法的參數寫成一個整數,表示想要拉平的層數,默認爲 1。
flat()的參數爲 2,表示要「拉平」兩層的嵌套數組。若是無論有多少層嵌套,都要轉成一維數組,能夠用 Infinity 關鍵字做爲參數。
[1, [2, [3]]].flat(Infinity); // [1, 2, 3]
flatMap()先遍歷數組,再「拉平」一層,也只能拉平一層。參數與 map()方法相似。
[2, 3, 4].flatMap(x => [x, x * 2]); // [2, 4, 3, 6, 4, 8] // 至關於 [2, 3, 4].map(x => [x, x * 2]).flat(); // [2, 4, 3, 6, 4, 8]
const a = 1; const b = 2; const c = {a, b}; // 等同於 const c = {a: a, b: b}; const o = { method() { return "Hello!"; } }; // 等同於 const o = { method: function() { return "Hello!"; } }; function f(x, y) { return {x, y}; } // 等同於 function f(x, y) { return {x: x, y: y}; }
對象擴展符相似數組擴展符,主要用於解構賦值。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; x // 1 y // 2 z // { a: 3, b: 4 } let ab = { ...a, ...b }; // 等同於 let ab = Object.assign({}, a, b);
它用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行爲基本一致。
Object.is('str', 'str'); // true Object.is({}, {}); // false
不一樣之處只有兩個:一是+0不等於-0,二是NaN等於自身。
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
Object.assign方法用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。
Object.assign方法的第一個參數是目標對象,後面的參數都是源對象。若是目標對象與源對象有同名屬性,或多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性。
因爲undefined和null沒法轉成對象,因此若是它們做爲首參數,就會報錯。
const target = { a: 1, b: 1 }; const source1 = { b: 2, c: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
常見用途:
ES5 引入了Object.keys方法,返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵名。
var obj = { foo: 'bar', baz: 42 }; Object.keys(obj) // ["foo", "baz"]
Object.values方法返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值。
const obj = { foo: 'bar', baz: 42 }; Object.values(obj) // ["bar", 42]
const obj = { 100: 'a', 2: 'b', 7: 'c' }; Object.values(obj) // ["b", "c", "a"]
上面代碼中,屬性名爲數值的屬性,是按照數值大小,從小到大遍歷的,所以返回的順序是b、c、a。
Object.values只返回對象自身的可遍歷屬性。
const obj = Object.create({}, {p: {value: 42}}); Object.values(obj) // []
上面代碼中,Object.create方法的第二個參數添加的對象屬性(屬性p),若是不顯式聲明,默認是不可遍歷的,由於p的屬性描述對象的enumerable默認是false,Object.values不會返回這個屬性。只要把enumerable改爲true,Object.values就會返回屬性p的值。
const obj = Object.create({}, {p: { value: 42, enumerable: true } }); Object.values(obj) // [42]
Object.entries()方法返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值對數組。
const obj = { foo: 'bar', baz: 42 }; Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ]
除了返回值不同,該方法的行爲與Object.values基本一致。
Object.fromEntries()方法是Object.entries()的逆操做,用於將一個鍵值對數組轉爲對象。
Object.fromEntries([ ['foo', 'bar'], ['baz', 42] ]) // { foo: "bar", baz: 42 }