雖然在編寫項(xiàng)目的過程中,也會用到ES6的語法以及新特性。但感覺學(xué)習(xí)的不是特別系統(tǒng),索性這兩天重新刷了一下Understanding The ES6挽荠,也對ES6有了更深的理解。這里平绩,針對感覺應(yīng)用比較多圈匆,知識點(diǎn)比較重要的部分做了一下總結(jié)。內(nèi)容有點(diǎn)多捏雌,因此預(yù)計(jì)將分為三個(gè)部分跃赚。
塊級綁定-作用域
在ES5中,有一個(gè)知識難點(diǎn)性湿,就是變量提升和作用域纬傲。而這一部分之所以讓人困擾,則是因?yàn)镴S中變量的聲明方式會影響變量實(shí)際的創(chuàng)建位置肤频。
有一條規(guī)則是:使用var關(guān)鍵字聲明的變量叹括,無論其實(shí)際聲明位置在何處,都會被聲明與函數(shù)的頂部宵荒,這就是變量提升(hosting)汁雷。
ES6中為了讓變量的生命周期更加可控净嘀,引入了塊級聲明。即摔竿,讓所聲明的變量在指定塊的作用域外無法被訪問。ES6主要引入了兩個(gè)新的關(guān)鍵字少孝,let
和const
let
let
和var
類似继低,不同是的,let
聲明的變量不會存在變量提升的情況稍走。因此袁翁,下面這個(gè)代碼中:
{
let a = 1;
}
console.log(a);
是無法訪問到a
變量的。
另一個(gè)限制的地方是婿脸,禁止重復(fù)聲明相同的變量粱胜。
var a = 1;
let a = 2;
上面的代碼也會報(bào)錯(cuò)。
const
這個(gè)關(guān)鍵字也是限制了變量提升以及重復(fù)聲明狐树,而且變量值是不可更改的焙压。
const a = 1;
a = 2; //報(bào)錯(cuò)
但是抑钟,如果綁定的是對象涯曲,可以更改對象屬性值。
const person = { 'name': 'xiaoming', 'age': 12};
person.name = 'zhangsan';
console.log(person.name); //zhangsan
暫時(shí)性死區(qū)
利用let
和const
聲明的變量在塔,代碼在訪問到聲明處前都是禁止訪問的幻件。
let a = 2;
a = b; // b is not defined
let b = 3;
循環(huán)中的let和const聲明
請看下面這段代碼:
var array = [];
for(var i = 0 ; i < 10; i++) {
array.push(function() {
console.log(i);
});
}
array.forEach(function(func) { func(); }); //會輸出10次10
使用var
聲明的變量,會在每次循環(huán)中被共享了蛔溃。因此绰沥,在循環(huán)內(nèi)部創(chuàng)建的函數(shù)都用于同一個(gè)變量的引用。通常解決這種方式贺待,在ES5中會采用閉包的方式徽曲。而ES6中,可以使用let
關(guān)鍵字
var array = [];
for(let i = 0 ; i < 10; i++) {
array.push(function() {
console.log(i);
});
}
array.forEach(function(func) { func(); }); //會依次輸出0...9
這是因?yàn)?code>let關(guān)鍵字在每次循環(huán)中麸塞,都會創(chuàng)建一個(gè)新的i
變量疟位,而循環(huán)內(nèi)部的函數(shù)獲取的i
變量其實(shí)都是副本。
const
關(guān)鍵字不可以用在傳統(tǒng)的for循環(huán)中喘垂,但是可以用在for...of
和for … in
語法中甜刻。使用效果和let
一樣。
函數(shù)
ES6中對函數(shù)的功能做了新的擴(kuò)展正勒,使之使用起來更方便得院。
箭頭函數(shù)
自從有了箭頭函數(shù),在遇到回調(diào)函數(shù)寫法的時(shí)候章贞,就特別方便祥绞。當(dāng)然它的功能肯定不止于此。箭頭函數(shù)的語法像這樣:
(變量名,[其他變量名]) => { //函數(shù)體}
//有以下幾種變形
(value1, value2) => {return value1 + value2;}
(value1, value2) => value1 + value2; //函數(shù)體只有一條return語句的時(shí)候,可以省略return關(guān)鍵字和{}
value => value1 + 1; //單參數(shù)函數(shù)時(shí)候蜕径,可以省略括號
是傳統(tǒng)函數(shù)寫法的一種便捷寫法两踏,同時(shí),有以下幾個(gè)特點(diǎn):
- 不允許重復(fù)的具名參數(shù)
- 沒有arguments對象
- 不能更改this兜喻,在整個(gè)函數(shù)聲明周期內(nèi)其值會保持不變梦染,因此,在遇到this問題時(shí)朴皆,不用再像ES5之前的時(shí)期使用類似
var self =this
和bind(this)
之類的方法了帕识。 - 不能被new調(diào)用
- 沒有this、super遂铡、arguments肮疗、也沒有new.target綁定
默認(rèn)參數(shù)和不具名參數(shù)
先看下面這個(gè)例子,同時(shí)使用了默認(rèn)參數(shù)和不具名參數(shù)(也叫做剩余參數(shù)):
//num1如果不傳值或者為undefined的話為0, num2是一個(gè)數(shù)組代表后續(xù)傳進(jìn)來的參數(shù)集合
function add(num1 = 0, ...num2) {
num2.forEach(i => num1 = num1 + i);
return num1;
}
console.log(add(undefined,2)); //2
console.log(add(undefined,2,3)); //5
console.log(add(1,2,3)); //6
采用了默認(rèn)參數(shù)值時(shí)扒接,仍然可以使用arguments
對象來反應(yīng)真實(shí)函數(shù)的調(diào)用狀態(tài)伪货, 而arguments
對象也可以和不具名參數(shù)進(jìn)行協(xié)同工作。
function add(num1 = 1, num2 = 1) {
console.log(arguments);
}
add(1); //[1]
add(2, 3); //[2,3]
add(); //[]
add(2,3,4,5,6); //[2,3,4,5,6]
同時(shí)钾怔,函數(shù)的默認(rèn)值甚至可以利用函數(shù)來動態(tài)生成超歌,而非寫死的,如:
function getNum() {
return parseInt(Math.random() * 10);
}
function add(num1 = getNum(), num2 = 1) {
console.log(num1 + num2);
}
//每次調(diào)用的結(jié)果是隨機(jī)的蒂教,以下為某一次調(diào)用的結(jié)果
add(); // 8
add(); // 4
需要注意的是巍举,函數(shù)的不具名參數(shù)是不能夠在對象的setter
上使用的,這是因?yàn)?code>setter只接受單一值作為它的參數(shù)凝垛。
擴(kuò)展運(yùn)算符
…
這三個(gè)點(diǎn)運(yùn)算符用在函數(shù)聲明的時(shí)候懊悯,就是不具名參數(shù),但是它同時(shí)也能用在解構(gòu)對象上梦皮,因此炭分,在函數(shù)調(diào)用過程中,可以使用該運(yùn)算符剑肯,進(jìn)行多個(gè)參數(shù)傳遞捧毛。如
var randomArray = [2,3,1231,455,231,23,553];
console.log(Math.max(...randomArray)); //1231
new.target
這是ES6函數(shù)對象的元屬性,可以通過檢查new.target
對象是否被定義让网,可以判斷函數(shù)是否通過new
進(jìn)行調(diào)用呀忧。
function Cat() {
if(new.target === undefined) {
console.log('this is not called by NEW keyword');
}else {
console.log('this is called by NEW keyword');
}
}
new Cat(); //this is called by NEW keyword
Cat.call(this); //this is not called by NEW keyword
原理方面,其實(shí)就是當(dāng)函數(shù)的構(gòu)造器(constructor)方法被調(diào)用時(shí)溃睹,new.target 會被填入 new 運(yùn)算符的作用目標(biāo)而账,該目標(biāo)通常是新創(chuàng)建的對象實(shí)例的構(gòu)造器,并且會成為函數(shù)體內(nèi)部的 this 值因篇。而若call
方法被執(zhí)行的時(shí)候泞辐, new.target 的值則會是undefined
笔横。
解構(gòu)
解構(gòu)的意思是將數(shù)據(jù)結(jié)構(gòu)分解為更小的部分,而ES6中引入了解構(gòu)的方式目的就是能夠更好地提取數(shù)據(jù)咐吼。
對象解構(gòu)
直接上例子:
let { type, name } = node //普通解構(gòu)
let { type, name, value = true } = node; //默認(rèn)值解構(gòu)
let { type: localType, name: localName } = node; //賦值給不同的本地對象
let { loc: { start: localStart }} = node; //嵌套的解構(gòu)方式,等同于localStart = node.loc.start
以上需要注意的是吹缔,當(dāng)解構(gòu)賦值表達(dá)式的右側(cè)( = 后面的表達(dá)式)的計(jì)算結(jié)果為 null 或 undefined 時(shí),會拋出錯(cuò)誤锯茄。
數(shù)組解構(gòu)
數(shù)組解構(gòu)時(shí)厢塘,解構(gòu)作用在數(shù)組內(nèi)部的位置上,而不是作用在對象的具名屬性上撇吞。
let [ , , thirdColor ] = colors;
let [ firstColor, secondColor = "green" ] = colors; //默認(rèn)值
let [ firstColor, [ secondColor ] ] = colors; //嵌套解構(gòu)
let [ firstColor, ...restColors ] = colors; //剩余項(xiàng)
[a, b] = [b, a]; //數(shù)組解構(gòu)賦值有一個(gè)非常獨(dú)特的用例俗冻,能輕易地互換兩個(gè)變量的值
參數(shù)解構(gòu)
function doSomething(value1, value2, {value3, value4, value5}) {
}
需要注意的是默認(rèn)情況下調(diào)用函數(shù)時(shí)未給參數(shù)解構(gòu)傳值會拋出錯(cuò)誤礁叔。但若你要求它是可選的牍颈,可以給解構(gòu)的參數(shù)提供默認(rèn)值來處理這種行為。
function dosomething(value1, value2, {value3, value4, value5} = {}) {
//dosomething
}
擴(kuò)展的對象功能
在ES6中也對對象的使用方式做了進(jìn)一步的擴(kuò)展琅关,使其無論是在代碼編寫形式層面還是底層操作對象的層面都有了更多的特性煮岁。
對象類別
在ES6規(guī)范中,定義了對象的每種類別:
1.普通對象:擁有JS對象所有默認(rèn)的內(nèi)部行為
2.奇異對象:有別于默認(rèn)的內(nèi)部行為的對象
3.標(biāo)準(zhǔn)對象:是在ES6中被定義的對象涣易,可以是普通對象也可以是奇異對象画机。
4.內(nèi)置對象:在腳本開始運(yùn)行時(shí)由JS運(yùn)行環(huán)境(瀏覽器或Node)提供的對象。所有標(biāo)準(zhǔn)對象都是內(nèi)置對象新症。
屬性初始化器
當(dāng)對象的一個(gè)屬性名稱與本地變量名相同的時(shí)候步氏,可以省略冒號和值,如:
function createPerson(name, age) {
return {
name,
age
}
}
//等同于
function createPerson(name, age) {
return {
name: name,
age: age
}
}
方法簡寫
方法簡寫如下:
var person = {
name: 'nihao',
getName() {
return this.name;
}
}
//等同與
var person = {
name: 'nihao',
getName: function() {
return this.name;
}
}
需要注意的是徒爹,使用方法簡寫荚醒,在方法內(nèi)部可以使用super
方法,而傳統(tǒng)的寫法是無法使用的隆嗅。
計(jì)算性屬性名
屬性名可以使用拼接的寫法界阁,如:
var name = 'name';
var person = {
['first' + name]: 'scq',
['last' + name]: '000',
getName() {
return this.firstname + this.lastname;
}
}
對于某些需要動態(tài)生成屬性名的場合,寫法更加方便胖喳。
Object.is方法
為了避免在對象比較過程中的強(qiáng)制對象轉(zhuǎn)換泡躯。通常該方法的運(yùn)行結(jié)果和===
一樣,但是+0
和-0
,NaN
和NaN
不相同丽焊。
Object.assign(source, target) 方法
該方法接受一個(gè)接收者较剃,以及任意數(shù)量的供應(yīng)者,并會返回接收者技健。我通常在使用的時(shí)候重付,用來做繼承或者說深度拷貝?凫乖?确垫。
const person = {
name: 'scq000',
age: 23,
addressInfo: {
city: 'zs',
address: 'some address'
}
}
const person2 = Object.assign({}, person);
Object.assign() 方法接受任意數(shù)量的供應(yīng)者弓颈,而接收者會按照供應(yīng)者在參數(shù)中的順序來依次接收它們的屬性。這意味著在接收者中删掀,第二個(gè)供應(yīng)者的屬性可能會覆蓋第一個(gè)供應(yīng)者的翔冀。
關(guān)于重復(fù)的對象字面量屬性
ES6 移除了重復(fù)屬性的檢查,嚴(yán)格模式與非嚴(yán)格模式都不再檢查重復(fù)的屬性披泪。當(dāng)存在重復(fù)屬性時(shí)纤子,排在后面的屬性的值會成為該屬性的實(shí)際值。
自有屬性的枚舉順序
1.所有的數(shù)字類型鍵款票,按升序排列控硼。
2.所有的字符串類型鍵,按被添加到對象的順序排列艾少。
3.所有的符號類型鍵卡乾,也按添加順序排列。
修改對象的原型
對象原型的實(shí)際值被存儲在一個(gè)內(nèi)部屬性[[Prototype]]
上缚够, Object.getPrototypeOf()
方法會返回此屬性存儲的值幔妨,而 Object.setPrototypeOf() 方法則能夠修改該值。ES6 通過添加 Object.setPrototypeOf()
方法而改變了這種假定谍椅,此方法允許你修改任意指定對象的原型误堡。它接受兩個(gè)參數(shù):需要被修改原型的對象,以及將會成為前者原型的對象雏吭。
因?yàn)檫@個(gè)特性的添加锁施,可以使用 super 進(jìn)行簡單的原型訪問。super 是指向當(dāng)前對象的原型的一個(gè)指針杖们,實(shí)際上就是 Object.getPrototypeOf(this) 的值悉抵。這個(gè)功能在使用ES6類的繼承的時(shí)候,提供了更好的訪問父類的方式胀莹。
class Animal {
constructor(name, size) {
this.name = name;
this.size = size;
}
}
class Cat extends Animal {
constructor(size) {
super('cat', size);
}
}