之前面試被問到ES6新特性,箭頭函數,generator、Module等等,表示剛開始上手用ES6,目前還沒有很深入,所以這次想對ES6一些常用的特性進行整理。
let和const
主要是實現塊級作用域的。因爲在原生JS中,沒有塊級作用域。只有在函數中,才構成一個作用域,但是利用函數是可以實現一個類似於塊級作用域的。詳見這裏。
不過,爲了更方便的解決類似於下面這種問題:
setTimeout(function(){
for( var i = 0; i < 10; i++)
{
console.log(i);
}
})
ES6引入了兩個新的特性,let
和const
。
let
和var
有什麼區別呢?
1. `let`聲明的變量有塊級作用域,不能作爲全局對象的全局屬性被訪問,即不能通過window來訪問;
2. `let` 聲明的變量不能被重複聲明,而`var`沒有關係;
3. 像 `for(let i ......)`這種形式,會在每次迭代對`i`進行綁定。
const與let類似,唯一的不同就在於const
聲明變量時必須初始化,並且初始化之後就不能再對值進行修改了,否則會報錯!
箭頭函數
箭頭函數出現的目的是爲了讓JS代碼顯得更簡潔一些。
// 六種語言中的簡單函數示例
function (a) { return a > 0; } // JS
[](int a) { return a > 0; } // C++
(lambda (a) (> a 0)) ;; Lisp
lambda a: a > 0 # Python
a => a > 0 // C#
a -> a > 0 // Java
我們都知道函數式編程很普遍,而且原生JS來實現比較冗長,所以ES6新引入了一種箭頭函數的寫法。
那麼箭頭函數和普通函數有什麼區別呢?主要區別就是this
的指向。在箭頭函數裏面是沒有自己的this
的,裏面的this
繼承自外圍作用域。而普通函數是有自己的作用域的,所以纔會在引入閉包的時候,this
一般都指向全局window
對象。
var obj = {
name: 'heihei',
sayName: function(){
return function(){
console.log(this);
}
}
}
var obj1 = {
name: 'Hellen',
sayName: function(){
return ()=>{ console.log(this) };
}
}
另一點不同,就是箭頭函數裏面是訪問不到arguments參數列表的,所以只能通過不定參數和默認參數這兩個機制來實現。可參考這篇文章。
class類
在原生ES5中,我們通常利用構造函數來創建對象,而ES6引入一種與Java,C++等更接近的方法來創建對象-class。
我們對比下構造函數和class有什麼不同。
function Circle( radius ) {
this.radius = radius;
this.area = function(){
return Math.pow(this.radius, 2) * Math.PI;
}
}
var c = new Circle(4);
var area = c.area();
console.log(area);
class Circle {
constructor( radius ) {
this.radius = radius;
};
area() {
return Math.pow(this.radius, 2) * Math.PI;
}
}
var c = new Circle(4);
var area = c.area();
console.log(area);
在寫法上,除了類的定義與構造函數不同,在用法上是相同的語法。不同的地方我們看下下面兩張圖就明白了。
這是利用構造函數構造的對象:
利用Class創建的對象:
Iterator(遍歷器)
在原生JS中,遍歷對象/數組有這麼幾種方式:
- for循環
//最簡單也最直觀的循環遍歷
var arr = [2,6,5];
for(var i = 0; i < arr; i++) {
console.log(arr[i]);
}
- forEach
//寫法比上面的簡單很多;
//但是對於對象不使用,並且不能跳出循環,即用break,continue,return等
var arr = [2,6,5];
arr.forEach(function(data) {
console.log(data);
})
- for … in
//對於數組而言,是按照arr['0'],arr['1']來訪問的;
//不一定按順序訪問的
//專門適用於對象的遍歷
var arr = [2,6,5];
for(var index in arr) {
console.log(arr[index]);
}
而Iterator
的出現就是爲解決上面的一些缺點出現的,爲的是給不同的數據結構產生統一的接口。只要部署一個Iterator
接口,就可以爲for ... of
服務,從而實現遍歷。
Iterator
的過程是這樣的:
- 創建一個指針對象,指向數據結構的起始位置;
- 調用指針對象的
next
方法,指針指向數據結構的第一個成員; 一次類推,直到返回數據結構的最後一個成員。
Iterator
的next
方法返回的是一個對象:{ value: 成員對象的值, done:false/true }
其實在數組、字符串,以及ES6的Map和Set中,都有默認的Iterator
接口,因爲有遍歷器接口,所以才能調for ... of
來實現遍歷。那我們看看默認的Iterator
接口是什麼樣的?
上圖是一個數組對象的原型鏈上的方法。其中有一個Symbol.iterator
函數,這個函數其實就是一個遍歷器接口。我們來使用一下:
這個函數返回的是一個對象表示數組Iterator。並且在這個對象裏有next方法,這跟我們前面說的是相吻合的。
每一次調用next方法之後,就會返回一個對象,value表示數據結構當前位置的數值,done表示是否到最後一個成員。
所以只要一個數據結構,有這個接口,都可以調用for…of方法來實現。
因爲Object裏面沒有Iterator接口,所以不能直接用for…of實現遍歷,那怎麼辦呢?
其實可以先調Object.keys將對象轉成數組。
後期還會續上generator生成器對象(迭代器對象)以及modules等新特性。