你需要了解的ES6語法大總結
前言
ES6已結出來6年多了,相信大家的ES6常用語法都用的爐火純青了,自己打算把ES6的語法全部總結一遍,也方便自己更加的靈活使用和鞏固知識。希望能對你有幫助!
let
介紹
let 語句聲明一個塊級作用域的本地變量,并且可選的將其初始化為一個值。新的變量的聲明方式。
它具有如下的特性
不允許重復聲明
var可以多次重復聲明(最后一次聲明會覆蓋前面的聲明),而let不能(會報錯),可以避免重復命名
var a = 1; var a = 2; console.log(a); // 2
let a = 1; let a = 2; console.log(a); // Uncaught SyntaxError: Identifier 'a' has already been declared
這個特性有一大好處就是避免多人開發(fā)時,自己或者別人命名了相同名稱的變量,把以前的變量給覆蓋掉了。
塊級作用域
為什么需要塊級作用域?
ES5時只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。
第一種場景,內層變量可能會覆蓋外層變量。
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; // 這里會有變量提升 } } f(); // undefined
if代碼塊的外部使用外層的tmp變量,內部使用內層的tmp變量。但是,函數f執(zhí)行后,輸出結果為undefined,原因在于變量提升,導致內層的tmp變量覆蓋了外層的tmp變量。
第二種場景,用來計數的循環(huán)變量泄露為全局變量。
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } // for循環(huán)外面打印 console.log(i); // 5
上面代碼中,變量i只用來控制循環(huán),但是循環(huán)結束后,它并沒有消失,泄露成了全局變量。
es6的塊級作用域
{ let a = 10; var b = 1; } console.log(b); // 1 console.log(a); // Uncaught ReferenceError: a is not defined
let實際上為 JavaScript 新增了塊級作用域,let聲明的變量只在它所在的代碼塊有效,外層拿不到let聲明的變量。上面代碼中{}就是塊級作用域
ES6 允許塊級作用域的任意嵌套。
{{{{ {let insane = 'Hello World'} console.log(insane); // 報錯 }}}};
上面代碼使用了一個五層的塊級作用域,每一層都是一個單獨的作用域。第四層作用域無法讀取第五層作用域的內部變量。
內層作用域可以定義外層作用域的同名變量。
{{{{ let insane = 'Hello World'; {let insane = 'Hello World'} }}}};
for循環(huán)的計數器,就很合適使用let命令。
for (let i = 0; i < 10; i++) { // ... } console.log(i); // ReferenceError: i is not defined
上面代碼中,計數器i只在for循環(huán)體內有效,在循環(huán)體外引用就會報錯。
改成var聲明的話
for (var i = 0; i < 3; i++) { console.log(i); //輸出0 1 2 } console.log(i); //只會輸出3
如果想要更加深入的研究for循環(huán)作用域的問題,請參考這篇 文章
暫時性死區(qū)
var a = 1 { a = 6 let a // Uncaught ReferenceError: Cannot access 'a' before initialization }
在代碼塊內,使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為暫時性死區(qū) ES6 規(guī)定暫時性死區(qū),主要是為了減少運行時錯誤,防止在變量聲明前就使用這個變量,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規(guī)定,避免此類錯誤就很容易了。
變量提升
關于是否有變量提升,目前來看并不統(tǒng)一。阮一峰的 ECMAScript 6 入門中的let和const章節(jié)中明確說明 不存在變量提升。但自己的理解還是存在變量提升的。下面舉例說明一下:
var a = 1; (function () { console.log(a); // 1 })();
我們簡單改造一下
var a = 1; (function () { console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization let a = 2 })();
從這里其實可以看出let也是存在變量提升的,只是在變量賦值之前不能對變量進行讀寫,否則就會報錯,這也就是暫時性死區(qū)。
變量不會掛載到window
在本人看來,var聲明的變量掛載到window是一種很不好的設計,這很容易會導致變量被污染,以及全局變量被濫用。所以,新的聲明方式已經不將聲明的變量再掛載到window上面了。
// var聲明的變量會掛載到window上 var a = 1; window.a // 1 // let聲明的變量不會掛載到window let b = 1; window.b // undefined
頂層對象的屬性與全局變量掛鉤,被認為是 JavaScript 語言最大的設計敗筆之一。這樣的設計帶來了幾個很大的問題,首先是沒法在編譯時就報出變量未聲明的錯誤,只有運行時才能知道(因為全局變量可能是頂層對象的屬性創(chuàng)造的,而屬性的創(chuàng)造是動態(tài)的);其次,程序員很容易不知不覺地就創(chuàng)建了全局變量(比如打字出錯);最后,頂層對象的屬性是到處可以讀寫的,這非常不利于模塊化編程。另一方面,window對象有實體含義,指的是瀏覽器的窗口對象,頂層對象是一個有實體含義的對象,也是不合適的。
從 ES6 開始,全局變量將逐步與頂層對象的屬性脫鉤。
const
const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。其他特性和let一樣。
const a = 1; a = 3; // TypeError: Assignment to constant variable.
const聲明的變量不得改變值,這意味著,const一旦聲明變量,就必須立即初始化,不能留到以后賦值。
const a; // SyntaxError: Missing initializer in const declaration
const實際上保證的,并不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對于簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,因此等同于常量。但對于復合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即總是指向另一個固定的地址),至于它指向的數據結構是不是可變的,就完全不能控制了。因此,將一個對象聲明為常量必須非常小心。
const a = {}; // 為 a 添加一個屬性,可以成功 a.prop = 123; // 將 a 指向另一個對象,就會報錯 a = {}; // TypeError: "a" is read-only
總結
對于var的使用:
我們需要明白一個事實,var所表現出來的特殊性:比如作用域提升、window全局對象、沒有塊級作用域等都是一些歷史遺留問題;是JavaScript在設計之初的一種語言缺陷
當然目前也在利用這種缺陷出一系列的面試題,來考察大家對JavaScript語言本身以及底層的理解
但是在實際工作中,我們可以使用最新的規(guī)范來編寫,也就是不再使用var來定義變量了
對于let和const:
對于let和const來說,是目前開發(fā)中推薦使用的
我們推薦使用const,這樣可以保證數據的安全性不會被隨意的篡改
只有當我們明確知道一個變量后續(xù)會需要被重新賦值時,這個時候再使用let
反正就一句話,以后不要再用var啦!
解構賦值
ES6 允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構
對象的解構
以前的取值方式
const obj = { a:1, b:2, c:3 } const a = obj.a; const b = obj.b; const c = obj.c;
現在的取值方式
const obj = { a:1, b:2, c:3 } const {a,b,c} = obj;
修改解構出來的名稱
const obj = { a:1, b:2, c:3 } const {a:a1, b:b1, c} = obj; console.log(a1) // 1 console.log(b1) // 2 console.log(c) // 3
給默認值
const obj = { a:1, b:2, c:3 } const {d = 5} = obj; const {e:e1 = 6} = obj; console.log(d) // 5 console.log(e1) // 6
深層對象的解構
const metadata = { title: "english-title", translations: [ { title: "我是深處的title", }, ], friend: { girlFriend: { name: { firstName: "chimmy", }, }, }, }; let { title: englishTitle, // rename translations: [ { title: localeTitle, // rename }, ], friend: { girlFriend: { name: { firstName }, }, }, } = metadata; console.log(englishTitle); // "english-title" console.log(localeTitle); // "我是深處的title" console.log(firstName); // "chimmy"
數組的解構
// 以前獲取數組里面值得方式 var names = ["abc", "cba", "nba"] // var item1 = names[0] // var item2 = names[1] // var item3 = names[2] // 對數組的解構: [] let [item1, item2, item3] = names console.log(item1, item2, item3) // abc cba nba // 解構后面的元素 let [, , itemz] = names console.log(itemz) // nba // 解構出一個元素,后面的元素放到一個新數組中 let [itemx, ...newNames] = names console.log(itemx, newNames) // abc ['cba', 'nba'] // 解構的默認值 let [itema, itemb, itemc, itemd = "aaa"] = names console.log(itemd) // aaa // 通過解構交換變量 let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1
函數參數解構
函數的參數也可以使用解構賦值。
function add([x, y]){ return x + y; } add([1, 2]); // 3
函數參數的解構也可以使用默認值。
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]
對象字面量增強寫法和計算屬性名
let name = "jimmy" let age = 18 // 屬性的簡寫 let obj = { name, age } // 等同于 let obj = { name:name, age:age } // 方法的簡寫 let obj2 = { bar() { console.log(this) }, } // 等同于 let obj2 = { bar:function() { console.log(this) } } // 計算屬性名(對象的鍵可以是變量) let obj3 = {} obj3[name] = "chimmy"; console.log(obj3.jimmy); // chimmy
展開語法( ... )
展開語法(Spread syntax), 可以在函數調用/數組構造時, 將數組表達式或者string在語法層面展開;還可以在構造字面量對象時, 將對象表達式按key-value的方式展開。(字面量一般指 [1, 2, 3] 或者 {name: "mdn"}
這種簡潔的構造方式)
示例
const names = ["abc", "cba", "nba"] const info = {name: "why", age: 18} // 1.函數調用時 function foo(x, y, z) { console.log(x, y, z) // abc cba nba } foo(...names) // 2.構造數組時 const newNames = [...names] console.log(newNames) // ['abc', 'cba', 'nba'] console.log(...[1, 2, 3]) // 1 2 3 // 3.構建對象字面量時ES2018(ES9) const obj = { ...info, address: "成都市", ...names } console.log(obj) // {0: 'abc', 1: 'cba', 2: 'nba', name: 'why', age: 18, address: '成都市'}
復制數組(淺拷貝)
數組是復合的數據類型,直接復制的話,只是復制了指向底層數據結構的指針,而不是克隆一個全新的數組。
const a1 = [1, 2]; const a2 = a1; a2[0] = 2; console.log(a1) // [2, 2]
const a1 = [1, 2]; const a2 = [...a1]; a2[0] = 2; console.log(a1) // [1, 2]
合并數組
const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; // ES5 的合并數組 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合并數組 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
模板字符串
ES6之前拼接字符串和其他標識符
const name = "jimmy"; const age = 18; const height = 1.88; console.log("my name is " + name + ", age is " + age + ", height is " + height)
ES6提供的模板字符串
const age = 18; const height = 1.88; // 支持變量 const message = `my name is ${name}, age is ${age}, height is ${height}`; console.log(message); // my name is , age is 18, height is 1.88 // 支持表達式 const info = `age double is ${age * 2}`; console.log(info); // age double is 36 // 支持函數調用 function doubleAge() { return age * 2; } const info2 = `double age is ${doubleAge()}`; console.log(info2); // double age is 36
對象擴展
object.is()
語法
Object.is(value1, value2);
被比較的第一個值。value1
被比較的第二個值。 value2
介紹
ES5 比較兩個值是否相等,只有兩個運算符:相等運算符(==)和嚴格相等運算符(===)。它們都有缺點,前者會自動轉換數據類型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一種運算,在所有環(huán)境中,只要兩個值是一樣的,它們就應該相等。
ES6 提出“Same-value equality”(同值相等)算法,用來解決這個問題。Object.is
就是部署這個算法的新方法。它用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行為基本一致。
Object.is('foo', 'foo') // true Object.is({}, {}) // false
不同之處只有兩個:一是+0不等于-0,二是NaN等于自身。
console.log(NaN === NaN); // false console.log(0 === -0); // true Object.is(NaN, NaN) // true Object.is(+0, -0) // false
Object.assign()
Object.assign()
方法用于將所有可枚舉屬性的值從一個或多個源對象分配到目標對象。它將返回目標對象。
// 目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。 const target = { a: 1, b: 2 }; const source = { b: 4, c: 5 }; const returnedTarget = Object.assign(target, source); console.log(target); // { a: 1, b: 4, c: 5 } 注意目標對象自身也會改變 console.log(returnedTarget); { a: 1, b: 4, c: 5 } // Object.assign()的返回值其實就是目標對象 target === returnedTarget // true
語法
Object.assign(target, ...sources)
- target 目標對象。
- sources 源對象。 個數沒有限制
返回值為 目標對象
如果只有一個參數,Object.assign()
會直接返回該參數。
const obj = {a: 1}; Object.assign(obj) === obj // true
如果該參數不是對象,則會先轉成對象,然后返回。
typeof Object.assign(2) // "object"
由于undefined和null無法轉成對象,所以如果它們作為參數,就會報錯。
Object.assign(undefined) // 報錯 Object.assign(null) // 報錯 // 如果`undefined`和`null`不在首參數,就不會報錯。 let obj = {a: 1}; Object.assign(obj, undefined) === obj // true Object.assign(obj, null) === obj // true
常見用途
(1)為對象添加屬性
class Point { constructor(x, y) { Object.assign(this, {x, y}); } }
上面方法通過Object.assign()
方法,將x屬性和y屬性添加到Point類的對象實例。
(2)為對象添加方法
Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } }); // 等同于下面的寫法 SomeClass.prototype.someMethod = function (arg1, arg2) { ··· }; SomeClass.prototype.anotherMethod = function () { ··· };
上面代碼使用了對象屬性的簡潔表示法,直接將兩個函數放在大括號中,再使用assign()
方法添加到SomeClass.prototype
之中。
(3)克隆對象
function clone(origin) { return Object.assign({}, origin); }
注意,這種方式是淺拷貝。詳細理解 請參考 js深拷貝和淺拷貝知多少
(4)合并多個對象
將多個對象合并到某個對象。
const merge = (target, ...sources) => Object.assign(target, ...sources);
如果希望合并后返回一個新對象,可以改寫上面函數,對一個空對象合并。
const merge = (...sources) => Object.assign({}, ...sources);
(5)為屬性指定默認值
const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { options = Object.assign({}, DEFAULTS, options); console.log(options); // ... }
上面代碼中,DEFAULTS對象是默認值,options對象是用戶提供的參數。Object.assign()
方法將DEFAULTS和options合并成一個新對象,如果兩者有同名屬性,則options的屬性值會覆蓋DEFAULTS的屬性值。
對象的遍歷方式(擴展)
如何能夠遍歷出對象中每個key和value的值呢?
let obj = { name: "jimmy", age: 18, like: "girl", };
主要有以下方式
// for...in的作用是用于遍歷對象的。 for (let key in obj) { console.log(key, obj[key]); } // Object.keys()用于返回對象所有key組成的數組。 Object.keys(obj).forEach((key) => { console.log(key, obj[key]); }); // Object.getOwnPropertyNames()用于返回對象所有key組成的數組。 Object.getOwnPropertyNames(obj).forEach((key) => { console.log(key, obj[key]); }); // Reflect.ownKeys()用于返回對象所有key組成的數組。 Reflect.ownKeys(obj).forEach((key) => { console.log(key, obj[key]); }); // 打印的都是 // name jimmy // age 18 // like girl
function擴展
函數默認值
ES6 之前,不能直接為函數的參數指定默認值,只能采用變通的方法。
function log(x, y) { if (typeof y === 'undefined' || typeof y === 'null') { y = 'World'; } console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello World
當一個函數有很多參數涉及初始化的時候,這樣寫代碼極其丑陋。ES6 允許為函數的參數設置默認值,即直接寫在參數定義的后面。
function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello
ES6 的寫法還有兩個好處:首先,閱讀代碼的人,可以立刻意識到哪些參數是可以省略的,不用查看函數體或文檔;其次,有利于將來的代碼優(yōu)化,即使未來的版本在對外接口中,徹底拿掉這個參數,也不會導致以前的代碼無法運行。
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
上面代碼的add函數是一個求和函數,利用 rest 參數,可以向該函數傳入任意數目的參數。
下面是一個 rest 參數代替arguments變量的例子。
// arguments變量的寫法 function sortNumbers() { return Array.from(arguments).sort(); } // rest參數的寫法 const sortNumbers = (...numbers) => numbers.sort();
上面代碼的兩種寫法,比較后可以發(fā)現,rest 參數的寫法更自然也更簡潔。
注意,rest 參數之后不能再有其他參數(即只能是最后一個參數),否則會報錯。
// 報錯 function f(a, ...b, c) { // ... }
箭頭函數
箭頭函數可以說是 ES6 很大的福利了,不管你是函數式愛好者還是面向對象開發(fā)者,函數是必須要用到的東西。之前聲明函數需要使用 function,如下:
function hello() { console.log('say hello') } // 或 let hello = function() { console.log('say hello') }
現在可以這樣做了:
let hello = () => { console.log('say hello') }
如果帶參數該怎么做呢?
let hello = (name) => { console.log('say hello', name) } // 或者 let hello = name => { console.log('say hello', name) } // 如果只有一個參數,可以省略括號,如果大于一個參數一定要記得帶括號
函數的聲明和參數寫的很清楚了,那么對于返回值有什么要注意的地方呢?
如果返回值是表達式可以省略 return 和 {}
let pow = x => x * x
如果返回值是字面量對象
let person = (name) => ({ age: 20, addr: 'Beijing City' })
箭頭函數注意點
箭頭函數中沒有this,內部的this就是定義時上層作用域中的this。也就是說,箭頭函數內部的this指向是固定的
不可以當作構造函數,也就是說,不可以對箭頭函數使用new命令,否則會拋出一個錯誤。
箭頭函數不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用 rest 參數代替。
不可以使用yield命令,因此箭頭函數不能用作 Generator 函數。
函數的length屬性
在函數體內,有時候需要判斷函數有幾個參數,一共有2個辦法。在 ES5 中可以在函數體內使用 arguments 來判斷。
function foo(a, b = 1, c) { console.log(arguments.length) } foo('a', 'b') //2
然而在 ES6 中不能再使用 arguments 來判斷了,但可以借助 Function.length 來判斷。
function foo(a, b = 1, c) { console.log(foo.length) } foo('a', 'b') // 1
細心的同學發(fā)現 Function.length 結果和 arguments 的結果不同!沒錯,Function.length 是統(tǒng)計第一個默認參數前面的變量數:
函數指定了默認值以后,函數的length屬性,將返回沒有指定默認值的參數個數。
數組的擴展
Array.from
介紹
Array.from
方法用于將兩類對象轉為真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數據結構 Set 和 Map)。
語法
Array.from(arrayLike[, mapFn[, thisArg]])
- arrayLike 想要轉換成數組的偽數組對象或可迭代對象。
- mapFn 可選 如果指定了該參數,新數組中的每個元素會執(zhí)行該回調函數。
- thisArg 可選 執(zhí)行回調函數mapFn時this對象
下面是一個類似數組的對象,Array.from
將它轉為真正的數組。
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']
實際應用中,常見的類似數組的對象是 DOM 操作返回的 NodeList 集合,以及函數內部的arguments對象。Array.from
都可以將它們轉為真正的數組。
// NodeList對象 let ps = document.querySelectorAll('p'); Array.from(ps).filter(p => { return p.textContent.length > 100; }); // arguments對象 function foo() { var args = Array.from(arguments); // ... } // Set const set = new Set(['foo', 'bar', 'baz', 'foo']); Array.from(set); // [ "foo", "bar", "baz" ] // Map const map = new Map([[1, 2], [2, 4], [4, 8]]); Array.from(map); // [[1, 2], [2, 4], [4, 8]]
如果參數是一個真正的數組,Array.from
會返回一個一模一樣的新數組。
Array.from([1, 2, 3]) // [1, 2, 3]
Array.from
可以接受第二個參數,作用類似于數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組。
Array.from(arrayLike, x => x * x); // 等同于 Array.from(arrayLike).map(x => x * x); Array.from([1, 2, 3], (x) => x * x) // [1, 4, 9]
for...of
ES6中新增的數組遍歷方式
for (let val of [1, 2, 3]) { console.log(val); // 1,2,3 }
Array.prototype.find()
介紹
find() 方法返回數組中滿足提供的測試函數的第一個元素的值,否則返回 undefined。
const array1 = [5, 12, 8, 130, 44]; const found = array1.find(element => element > 10); console.log(found); //12
語法
arr.find(callback[, thisArg])
callback 在數組每一項上執(zhí)行的函數,接收 3 個參數:
element當前遍歷到的元素。 index 可選 當前遍歷到的索引 array 數組本身
thisArg 執(zhí)行回調時用作this 的對象。
Array.prototype.findIndex()
findIndex()方法返回數組中滿足提供的測試函數的第一個元素的索引。否則返回-1。其實這個和 find() 是成對的,不同的是它返回的是索引而不是值。
let array = [5, 12, 8, 130, 44]; let found = array.find(function(element) { return element > 10; }); console.log(found); // 1
Array.prototype.fill()
介紹
fill()
方法用一個固定值填充一個數組中從起始索引到終止索引內的全部元素。不包括終止索引。
語法
arr.fill(value[, start[, end]])
value 用來填充數組元素的值 start 可選 起始索引 默認值為0 end 可選 終止索引 默認值為this.length 返回值 修改后的數組
如果 start 是個負數, 則開始索引會被自動計算成為 length+start, 其中 length 是 this 對象的 length 屬性值。如果 end 是個負數, 則結束索引會被自動計算成為 length+end。
const array1 = [1, 2, 3, 4]; console.log(array1.fill(0, 2, 4)); // [1, 2, 0, 0] console.log(array1.fill(5, 1)); // [1, 5, 5, 5] // 只有一個參數,說明其他兩項都是默認值,會替換數組全部內容 console.log(array1.fill(6)); // [6, 6, 6, 6]
Array.prototype.copyWithin()
數組實例的copyWithin()
方法,在當前數組內部,將指定位置的成員復制到其他位置(會覆蓋原有成員),然后返回當前數組。也就是說,使用這個方法,會修改當前數組。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三個參數。
- target(必需):從該位置開始替換數據。如果為負值,表示倒數。
- start(可選):從該位置開始讀取數據,默認為 0。如果為負值,表示從末尾開始計算。
- end(可選):到該位置前停止讀取數據,默認等于數組長度。如果為負值,表示從末尾開始計算。
// 將3號位復制到0號位 [1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5] // -2相當于3號位,-1相當于4號位 [1, 2, 3, 4, 5].copyWithin(0, -2, -1) // [4, 2, 3, 4, 5] // 參數不足三個,沒有的參數就是默認值 [1, 2, 3, 4, 5].copyWithin(-2) // [1, 2, 3, 1, 2] [1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
Array.of()
Array.of()
方法用于將一組值,轉換為數組。
Array.of(3, 11, 8) // [3,11,8] Array.of(3) // [3] Array.of(3).length // 1
這個方法的主要目的,是彌補數組構造函數Array()
的不足。因為參數個數的不同,會導致Array()
的行為有差異。
Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]
面代碼中,Array()
方法沒有參數、一個參數、三個參數時,返回的結果都不一樣。只有當參數個數不少于 2 個時,Array()
才會返回由參數組成的新數組。參數只有一個正整數時,實際上是指定數組的長度。
Array.of()
基本上可以用來替代Array()
或new Array()
,并且不存在由于參數不同而導致的重載。它的行為非常統(tǒng)一。
Array.of() // [] `Array.of()`總是返回參數值組成的數組。如果沒有參數,就返回一個空數組。 Array.of(undefined) // [undefined] Array.of(1) // [1] Array.of(1, 2) // [1, 2]
Number 擴展
Number.isFinite()
用來檢查一個數值是否為有限的(finite),即不是Infinity。
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.isFinite
一律返回false
。
Number.isNaN()
用來檢查一個值是否為NaN。
Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN('true' / 0) // true Number.isNaN('true' / 'true') // true
如果參數類型不是NaN
,Number.isNaN
一律返回false
。
Number.isInteger()
用來判斷一個數值是否為整數。
Number.isInteger(25) // true Number.isInteger(25.1) // false Number.isInteger() // false Number.isInteger(null) // false Number.isInteger('15') // false Number.isInteger(true) // false
Number.MAX_SAFE_INTEGER Number.MIN_SAFE_INTEGER
JavaScript 能夠準確表示的整數范圍在-2^53到2^53之間(不含兩個端點),超過這個范圍,無法精確表示這個值。
Math.pow(2, 53) // 9007199254740992 9007199254740992 // 9007199254740992 9007199254740993 // 9007199254740992 Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
上面代碼中,超出 2 的 53 次方之后,一個數就不精確了。
ES6 引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
這兩個常量,用來表示這個范圍的上下限。
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 // true Number.MAX_SAFE_INTEGER === 9007199254740991 // true Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER // true Number.MIN_SAFE_INTEGER === -9007199254740991 // true
Number.isSafeInteger()
JavaScript 能夠準確表示的整數范圍在-2^53到2^53之間(不含兩個端點),超過這個范圍,無法精確表示這個值。
Math.pow(2, 53) // 9007199254740992 Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
Number.parseInt(),Number.parseFloat()
ES6 將全局方法parseInt(), parseFloat()移植到Number對象上面,行為完全保持不變。這樣做的目的,是逐步減少全局性方法,使得語言逐步模塊化。
String擴展
String.prototype.includes()
ES5中可以使用indexOf方法來判斷一個字符串是否包含在另一個字符串中,indexOf返回出現的下標位置,如果不存在則返回-1。
const str = 'jimmy' console.log(str.indexOf('y')) // 4
ES6提供了includes方法來判斷一個字符串是否包含在另一個字符串中,返回boolean類型的值。
const str = 'jimmy' console.log(str.includes('mm')) // true // 區(qū)分大小寫 'Blue Whale'.includes('blue'); // returns false
語法
str.includes(searchString[, position])
- searchString 要在此字符串中搜索的字符串。
- position 從當前字符串的哪個索引位置開始搜尋子字符串,默認值為 0。
返回值
如果當前字符串包含被搜尋的字符串,就返回 true;否則返回 false。;
String.prototype.startsWith()
判斷參數字符串是否在原字符串的頭部, 返回boolean類型的值。
const str = 'jimmy' console.log(str.startsWith('ji')) // true
String.prototype.endsWith()
判斷參數字符串是否在原字符串的尾部, 返回boolean類型的值。
const str = 'jimmy' console.log(str.endsWith('my')) // true
String.prototype.repeat()
repeat方法返回一個新字符串,表示將原字符串重復n次。
const str = 'jimmy' const newStr = str.repeat(2) console.log(newStr) // jimmyjimmy
Symbol
ES6 引入了一種新的原始數據類型 Symbol ,表示獨一無二的值。它是 JavaScript 語言的第七種數據類型,前六種是:undefined、null、布爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)。
為什么要引入Symbol呢
ES5 的對象屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個他人提供的對象,但又想為這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現有方法產生沖突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的沖突。這就是 ES6 引入Symbol的原因。
Symbol 值通過Symbol函數生成。這就是說,對象的屬性名現在可以有兩種類型,一種是原來就有的字符串,另一種就是新增的 Symbol 類型。凡是屬性名屬于 Symbol 類型,就都是獨一無二的,可以保證不會與其他屬性名產生沖突。
let a = Symbol(); let b = Symbol(); typeof a // symbol console.log(a === b); // false
Symbol函數可以接受一個字符串作為參數,表示對 Symbol 實例的描述,主要是為了在控制臺顯示,或者轉為字符串時,比較容易區(qū)分。
let s1 = Symbol('foo') let s2 = Symbol('foo') console.log(s1) //Symbol(foo) console.log(s2) // Symbol(foo) // `Symbol`函數的參數只是表示對當前 Symbol 值的描述,因此相同參數的`Symbol`函數的返回值是不相等的。 console.log(s1 === s2) // false
注意
Symbol函數前不能使用new命令,否則會報錯。這是因為生成的 Symbol 是一個原始類型的值,不是對象。也就是說,由于 Symbol 值不是對象,所以不能添加屬性。基本上,它是一種類似于字符串的數據類型。
如果 Symbol 的參數是一個對象,就會調用該對象的toString方法,將其轉為字符串,然后才生成一個 Symbol 值。
const obj = { toString() { return 'abc'; } }; const sym = Symbol(obj); // Symbol(abc)
Symbol 值不能與其他類型的值進行運算,會報錯。
let sym = Symbol('My symbol'); "your symbol is " + sym // TypeError: can't convert symbol to string `your symbol is ${sym}` // TypeError: can't convert symbol to string
Symbol 值可以顯式轉為字符串,布爾值 但是不能轉為數值。
let sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)' Boolean(sym) // true !sym // false Number(sym) // TypeError sym + 2 // TypeError
Symbol.for()
Symbol.for()
接受一個字符串作為參數,然后搜索有沒有以該參數作為名稱的 Symbol 值。如果有,就返回這個 Symbol 值,否則就新建一個以該字符串為名稱的 Symbol 值,并將其注冊到全局。
let s1 = Symbol.for('foo') let s2 = Symbol.for('foo') console.log(s1 === s2) // true
Symbol.for()與Symbol()這兩種寫法,都會生成新的 Symbol。它們的區(qū)別是,前者會被登記在全局環(huán)境中供搜索,后者不會。Symbol.for()不會每次調用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key是否已經存在,如果不存在才會新建一個值。
Symbol.keyFor()
Symbol.keyFor()方法返回一個已登記的 Symbol 類型值的key。
const s1 = Symbol('foo') console.log(Symbol.keyFor(s1)) // undefined const s2 = Symbol.for('foo') console.log(Symbol.keyFor(s2)) // foo
應用場景
消除魔術字符串
魔術字符串指的是,在代碼之中多次出現、與代碼形成強耦合的某一個具體的字符串或者數值。風格良好的代碼,應該盡量消除魔術字符串,改由含義清晰的變量代替。
function getArea(shape, options) { let area = 0; switch (shape) { case 'Triangle': // 魔術字符串 area = .5 * options.width * options.height; break; /* ... more code ... */ } return area; } getArea('Triangle', { width: 100, height: 100 }); // 魔術字符串
上面代碼中,字符串Triangle和Circle就是魔術字符串。它多次出現,與代碼形成“強耦合”,不利于將來的修改和維護。
常用的消除魔術字符串的方法,就是把它寫成一個變量。
const shapeType = { triangle: 'Triangle' }; function getArea(shape, options) { let area = 0; switch (shape) { case shapeType.triangle: area = .5 * options.width * options.height; break; } return area; } getArea(shapeType.triangle, { width: 100, height: 100 });
上面代碼中,我們把Triangle寫成shapeType對象的triangle屬性,這樣就消除了強耦合。
如果仔細分析,可以發(fā)現shapeType.triangle
等于哪個值并不重要,只要確保不會跟其他shapeType屬性的值沖突即可。因此,這里就很適合改用 Symbol 值。
const shapeType = { triangle: Symbol() };
最終的代碼
const shapeType = { triangle: Symbol(), circle: Symbol() } function getArea(shape) { let area = 0 switch (shape) { case shapeType.triangle: area = .5 * options.width * options.height; break; case shapeType.circle: // ... more code ... break } return area } console.log(getArea(shapeType.triangle))
Symbol 類型還可以用于定義一組常量,保證這組常量的值都是不相等的。
const COLOR_RED = Symbol(); const COLOR_GREEN = Symbol(); function getComplement(color) { switch (color) { case COLOR_RED: return COLOR_GREEN; case COLOR_GREEN: return COLOR_RED; default: throw new Error('Undefined color'); } }
常量使用 Symbol 值最大的好處,就是其他任何值都不可能有相同的值了,因此可以保證上面的switch語句會按設計的方式工作。
Set
ES6 提供了新的數據結構 Set。它類似于數組,但是成員的值都是唯一的,沒有重復的值。
Set本身是一個構造函數,用來生成 Set 數據結構。
let s = new Set() // 在實例化的同時傳入默認的數據。 let s2 = new Set([1,2,3]) // 初始化的參數必須是可遍歷的,可以是數組或者自定義遍歷的數據結構。
添加數據
let s = new Set() s.add('chimmy') s.add('18') s.add('jimmy').add('18') // Set 數據結構不允許數據重復,所以添加重復的數據是無效的 console.log(s); // Set(3) { 'chimmy', '18', 'jimmy' }
刪除數據
// 刪除指定數據 s.delete('jimmy') // true // 刪除全部數據 s.clear()
查找和總數
// 判斷是否包含數據項,返回 true 或 false s.has('hello') // true // 計算數據項總數 s.size // 3
應用場景
數組去重
let arr = [1, 2, 3, 4, 2, 3] let s = [...new Set(arr)] console.log(s) // [1,2,3,4]
合并去重
let arr1 = [1, 2, 3, 4] let arr2 = [2, 3, 4, 5, 6] let s = new Set([...arr1, ...arr2]) console.log(s) // Set(6) { 1, 2, 3, 4, 5, 6 } console.log([...s]) // [ 1, 2, 3, 4, 5, 6 ] console.log(Array.from(s)) // [ 1, 2, 3, 4, 5, 6 ]
交集
let arr1 = [1, 2, 3, 4] let arr2 = [2, 3, 4, 5, 6] let s1 = new Set(arr1) let s2 = new Set(arr2) let result = new Set(arr1.filter(item => s2.has(item))) console.log(Array.from(result)) // [ 2, 3, 4 ]
差集
let arr1 = [1, 2, 3, 4]; let arr2 = [2, 3, 4, 5, 6]; let s1 = new Set([1, 2, 3, 4]) let s2 = new Set([2, 3, 4, 5, 6]) let arr3 = new Set(arr1.filter(item => !s2.has(item))) let arr4 = new Set(arr2.filter(item => !s1.has(item))) console.log(arr3) // Set(1) { 1 } console.log(arr4) // Set(2) { 5, 6 } console.log([...arr3, ...arr4]) // [ 1, 5, 6 ]
WeakSet
WeakSet 結構與 Set 類似,也是不重復的值的集合。但是,它與 Set 有兩個區(qū)別。
WeakSet 的成員只能是對象,而不能是其他類型的值。
const ws = new WeakSet() ws.add(1) // TypeError: Invalid value used in weak set ws.add(Symbol()) // TypeError: invalid value used in weak set
let ws = new WeakSet() const obj1 = { name: 'imooc' } const obj2 = { age: 5 } ws.add(obj1) ws.add(obj2) ws.delete(obj1) console.log(ws) console.log(ws.has(obj2))
WeakSet 沒有size屬性,沒有辦法遍歷它的成員。
WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用,也就是說,如果其他對象都不再引用該對象,那么垃圾回收機制會自動回收該對象所占用的內存,不考慮該對象還存在于 WeakSet 之中。
Map
JavaScript 的對象(Object),本質上是鍵值對的集合(Hash 結構),但是傳統(tǒng)上只能用字符串當作鍵。這給它的使用帶來了很大的限制。
const data = {}; const element = document.getElementById('myDiv'); data[element] = 'metadata'; data['[object HTMLDivElement]'] // "metadata"
上面代碼原意是將一個 DOM 節(jié)點作為對象data的鍵,但是由于對象只接受字符串作為鍵名,所以element被自動轉為字符串[object HTMLDivElement]
。
為了解決這個問題,ES6 提供了 Map 數據結構。它類似于對象,也是鍵值對的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object 結構提供了“字符串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的數據結構,Map 比 Object 更合適。
const m = new Map(); const o = {p: 'Hello World'}; m.set(o, 'content') m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
上面代碼使用 Map 結構的set方法,將對象o當作m的一個鍵,然后又使用get方法讀取這個鍵,接著使用delete方法刪除了這個鍵。
添加數據
let map = new Map() let keyObj = {} let keyFunc = function() {} let keyString = 'a string' // 添加鍵 map.set(keyString, "和鍵'a string'關聯的值") map.set(keyObj, '和鍵keyObj關聯的值') map.set(keyFunc, '和鍵keyFunc關聯的值') console.log(map) // // Map(3) { // 'a string' => "和鍵'a string'關聯的值", // {} => '和鍵keyObj關聯的值', // [Function: keyFunc] => '和鍵keyFunc關聯的值' // }
刪除數據
// 刪除指定的數據 map.delete(keyObj) // 刪除所有數據 map.clear()
查找和統(tǒng)計
// 統(tǒng)計所有 key-value 的總數 console.log(map.size) //3 // 判斷是否有 key-value console.log(map.has(keyObj)) // true
獲取
console.log(map.get(keyObj)) // 和鍵keyObj關聯的值
weakMap
WeakMap結構與Map結構類似,也是用于生成鍵值對的集合。
// WeakMap 可以使用 set 方法添加成員 const wm1 = new WeakMap() const key = { foo: 1 } wm1.set(key, 2) wm1.get(key) // 2 // WeakMap 也可以接受一個數組, // 作為構造函數的參數 const k1 = [1, 2, 3] const k2 = [4, 5, 6] const wm2 = new WeakMap([ [k1, 'foo'], [k2, 'bar'] ]) wm2.get(k2) // "bar"
WeakMap與Map的區(qū)別有兩點。
- WeakMap的鍵名所指向的對象,不計入垃圾回收機制。
- WeakMap只接受對象作為鍵名(null除外),不接受其他類型的值作為鍵名。
const map = new WeakMap() map.set(1, 2) // TypeError: 1 is not an object! map.set(Symbol(), 2) // TypeError: Invalid value used as weak map key map.set(null, 2) // TypeError: Invalid value used as weak map key
最后
這篇文章已有萬字,受限于篇幅, ES6的Class,Proxy,Reflect,Promise,Module等自己后續(xù)會單獨寫幾篇文章,詳細講解。
推薦文章
到此這篇關于ES6語法大總結的文章就介紹到這了,更多相關ES6語法總結內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
初學js插入節(jié)點appendChild insertBefore使用方法
由于可見insertBefore()方法的特性是在已有的子節(jié)點前面插入新的節(jié)點但是兩種情況結合起來發(fā)現insertBefore()方法插入節(jié)點,是可以在子節(jié)點列表的任意位置。2011-07-07setTimeout函數兼容各主流瀏覽器運行執(zhí)行效果實例
setTimeout是一個很不錯的函數,網站頁面前端工程師經常將其用于幾秒后執(zhí)行的動作,下文要講的setTimeout可以很好地兼容IE6,7,8,9以及谷歌等主流瀏覽器2013-06-06