雖然在編寫專案的過程中,也會用到ES6的語法以及新特性。但感覺學習的不是特別系統,索性這兩天重新刷了一下Understanding The ES6,也對ES6有了更深的理解。這裡,針對感覺應用比較多,知識點比較重要的部分做了一下總結。內容有點多,因此預計將分為三個部分。
塊級繫結-作用域
在ES5中,有一個知識難點,就是變數提升和作用域。而這一部分之所以讓人困擾,則是因為JS中變數的宣告方式會影響變數實際的建立位置。
有一條規則是:使用var關鍵字宣告的變數,無論其實際宣告位置在何處,都會被宣告與函式的頂部,這就是變數提升(hosting)。
ES6中為了讓變數的生命週期更加可控,引入了塊級宣告。即,讓所宣告的變數在指定塊的作用域外無法被訪問。ES6主要引入了兩個新的關鍵字,let
和const
let
let
和var
類似,不同是的,let
宣告的變數不會存在變數提升的情況。因此,下面這個程式碼中:
{
let a = 1;
}
console.log(a);複製程式碼
是無法訪問到a
變數的。
另一個限制的地方是,禁止重複宣告相同的變數。
var a = 1;
let a = 2;複製程式碼
上面的程式碼也會報錯。
const
這個關鍵字也是限制了變數提升以及重複宣告,而且變數值是不可更改的。
const a = 1;
a = 2; //報錯複製程式碼
但是,如果繫結的是物件,可以更改物件屬性值。
const person = { 'name': 'xiaoming', 'age': 12};
person.name = 'zhangsan';
console.log(person.name); //zhangsan複製程式碼
暫時性死區
利用let
和const
宣告的變數,程式碼在訪問到宣告處前都是禁止訪問的。
let a = 2;
a = b; // b is not defined
let b = 3;複製程式碼
迴圈中的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
宣告的變數,會在每次迴圈中被共享了。因此,在迴圈內部建立的函式都用於同一個變數的引用。通常解決這種方式,在ES5中會採用閉包的方式。而ES6中,可以使用let
關鍵字
var array = [];
for(let i = 0 ; i < 10; i++) {
array.push(function() {
console.log(i);
});
}
array.forEach(function(func) { func(); }); //會依次輸出0...9複製程式碼
這是因為let
關鍵字在每次迴圈中,都會建立一個新的i
變數,而迴圈內部的函式獲取的i
變數其實都是副本。
const
關鍵字不可以用在傳統的for迴圈中,但是可以用在for...of
和for … in
語法中。使用效果和let
一樣。
函式
ES6中對函式的功能做了新的擴充套件,使之使用起來更方便。
箭頭函式
自從有了箭頭函式,在遇到回撥函式寫法的時候,就特別方便。當然它的功能肯定不止於此。箭頭函式的語法像這樣:
(變數名,[其他變數名]) => { //函式體}
//有以下幾種變形
(value1, value2) => {return value1 + value2;}
(value1, value2) => value1 + value2; //函式體只有一條return語句的時候,可以省略return關鍵字和{}
value => value1 + 1; //單引數函式時候,可以省略括號複製程式碼
是傳統函式寫法的一種便捷寫法,同時,有以下幾個特點:
- 不允許重複的具名引數
- 沒有arguments物件
- 不能更改this,在整個函式宣告週期內其值會保持不變,因此,在遇到this問題時,不用再像ES5之前的時期使用類似
var self =this
和bind(this)
之類的方法了。 - 不能被new呼叫
- 沒有this、super、arguments、也沒有new.target繫結
預設引數和不具名引數
先看下面這個例子,同時使用了預設引數和不具名引數(也叫做剩餘引數):
//num1如果不傳值或者為undefined的話為0, num2是一個陣列代表後續傳進來的引數集合
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複製程式碼
採用了預設引數值時,仍然可以使用arguments
物件來反應真實函式的呼叫狀態, 而arguments
物件也可以和不具名引數進行協同工作。
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]複製程式碼
同時,函式的預設值甚至可以利用函式來動態生成,而非寫死的,如:
function getNum() {
return parseInt(Math.random() * 10);
}
function add(num1 = getNum(), num2 = 1) {
console.log(num1 + num2);
}
//每次呼叫的結果是隨機的,以下為某一次呼叫的結果
add(); // 8
add(); // 4複製程式碼
需要注意的是,函式的不具名引數是不能夠在物件的setter
上使用的,這是因為setter
只接受單一值作為它的引數。
擴充套件運算子
…
這三個點運算子用在函式宣告的時候,就是不具名引數,但是它同時也能用在解構物件上,因此,在函式呼叫過程中,可以使用該運算子,進行多個引數傳遞。如
var randomArray = [2,3,1231,455,231,23,553];
console.log(Math.max(...randomArray)); //1231複製程式碼
new.target
這是ES6函式物件的元屬性,可以通過檢查new.target
物件是否被定義,可以判斷函式是否通過new
進行呼叫。
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複製程式碼
原理方面,其實就是當函式的構造器(constructor)方法被呼叫時,new.target 會被填入 new 運算子的作用目標,該目標通常是新建立的物件例項的構造器,並且會成為函式體內部的 this 值。而若call
方法被執行的時候, new.target 的值則會是undefined
。
解構
解構的意思是將資料結構分解為更小的部分,而ES6中引入瞭解構的方式目的就是能夠更好地提取資料。
##物件解構
直接上例子:
let { type, name } = node //普通解構
let { type, name, value = true } = node; //預設值解構
let { type: localType, name: localName } = node; //賦值給不同的本地物件
let { loc: { start: localStart }} = node; //巢狀的解構方式,等同於localStart = node.loc.start複製程式碼
以上需要注意的是,當解構賦值表示式的右側( = 後面的表示式)的計算結果為 null 或 undefined 時,會丟擲錯誤。
##陣列解構
陣列解構時,解構作用在陣列內部的位置上,而不是作用在物件的具名屬性上。
let [ , , thirdColor ] = colors;
let [ firstColor, secondColor = "green" ] = colors; //預設值
let [ firstColor, [ secondColor ] ] = colors; //巢狀解構
let [ firstColor, ...restColors ] = colors; //剩餘項
[a, b] = [b, a]; //陣列解構賦值有一個非常獨特的用例,能輕易地互換兩個變數的值複製程式碼
引數解構
function doSomething(value1, value2, {value3, value4, value5}) {
}複製程式碼
需要注意的是預設情況下呼叫函式時未給引數解構傳值會丟擲錯誤。但若你要求它是可選的,可以給解構的引數提供預設值來處理這種行為。
function dosomething(value1, value2, {value3, value4, value5} = {}) {
//dosomething
}複製程式碼
擴充套件的物件功能
在ES6中也對物件的使用方式做了進一步的擴充套件,使其無論是在程式碼編寫形式層面還是底層操作物件的層面都有了更多的特性。
物件類別
在ES6規範中,定義了物件的每種類別:
1.普通物件:擁有JS物件所有預設的內部行為
2.奇異物件:有別於預設的內部行為的物件
3.標準物件:是在ES6中被定義的物件,可以是普通物件也可以是奇異物件。
4.內建物件:在指令碼開始執行時由JS執行環境(瀏覽器或Node)提供的物件。所有標準物件都是內建物件。
屬性初始化器
當物件的一個屬性名稱與本地變數名相同的時候,可以省略冒號和值,如:
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;
}
}複製程式碼
需要注意的是,使用方法簡寫,在方法內部可以使用super
方法,而傳統的寫法是無法使用的。
計算性屬性名
屬性名可以使用拼接的寫法,如:
var name = 'name';
var person = {
['first' + name]: 'scq',
['last' + name]: '000',
getName() {
return this.firstname + this.lastname;
}
}複製程式碼
對於某些需要動態生成屬性名的場合,寫法更加方便。
Object.is方法
為了避免在物件比較過程中的強制物件轉換。通常該方法的執行結果和===
一樣,但是+0
和-0
,NaN
和NaN
不相同。
Object.assign(source, target) 方法
該方法接受一個接收者,以及任意數量的供應者,並會返回接收者。我通常在使用的時候,用來做繼承或者說深度拷貝??。
const person = {
name: 'scq000',
age: 23,
addressInfo: {
city: 'zs',
address: 'some address'
}
}
const person2 = Object.assign({}, person);複製程式碼
Object.assign() 方法接受任意數量的供應者,而接收者會按照供應者在引數中的順序來依次接收它們的屬性。這意味著在接收者中,第二個供應者的屬性可能會覆蓋第一個供應者的。
關於重複的物件字面量屬性
ES6 移除了重複屬性的檢查,嚴格模式與非嚴格模式都不再檢查重複的屬性。當存在重複屬性時,排在後面的屬性的值會成為該屬性的實際值。
自有屬性的列舉順序
1.所有的數字型別鍵,按升序排列。
2.所有的字串型別鍵,按被新增到物件的順序排列。
3.所有的符號型別鍵,也按新增順序排列。
修改物件的原型
物件原型的實際值被儲存在一個內部屬性[[Prototype]]
上, Object.getPrototypeOf()
方法會返回此屬性儲存的值,而 Object.setPrototypeOf() 方法則能夠修改該值。ES6 通過新增 Object.setPrototypeOf()
方法而改變了這種假定,此方法允許你修改任意指定物件的原型。它接受兩個引數:需要被修改原型的物件,以及將會成為前者原型的物件。
因為這個特性的新增,可以使用 super 進行簡單的原型訪問。super 是指向當前物件的原型的一個指標,實際上就是 Object.getPrototypeOf(this) 的值。這個功能在使用ES6類的繼承的時候,提供了更好的訪問父類的方式。
class Animal {
constructor(name, size) {
this.name = name;
this.size = size;
}
}
class Cat extends Animal {
constructor(size) {
super('cat', size);
}
}複製程式碼
系列二請點這裡
本文對你有幫助?歡迎掃碼加入前端學習小組微信群: