ES6基礎(chǔ)內(nèi)容概覽

最近在學(xué)習(xí)React,示例代碼都由ES6所書寫,所以對(duì)于ES6,不得不好好研究一下新的語法。
這篇文章就對(duì)自己現(xiàn)在經(jīng)常遇到的一些ES6語法進(jìn)行了一個(gè)梳理。

let, const

這兩個(gè)的用途與var類似,都是用來聲明變量的,但在實(shí)際運(yùn)用中他倆都有各自的特殊用途。
首先來看下面這個(gè)例子:

var name = 'zach'

while (true) {
    var name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //obama

使用var兩次輸出都是obama,這是因?yàn)镋S5只有全局作用域和函數(shù)作用域,沒有塊級(jí)作用域,這帶來很多不合理的場(chǎng)景。第一種場(chǎng)景就是你現(xiàn)在看到的內(nèi)層變量覆蓋外層變量。而let則實(shí)際上為JavaScript新增了塊級(jí)作用域。用它所聲明的變量,只在let命令所在的代碼塊內(nèi)有效。

let name = 'zach'

while (true) {
    let name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //zach

另外一個(gè)var帶來的不合理場(chǎng)景就是用來計(jì)數(shù)的循環(huán)變量泄露為全局變量,看下面的例子:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

上面代碼中,變量i是var聲明的,在全局范圍內(nèi)都有效。所以每一次循環(huán),新的i值都會(huì)覆蓋舊值,導(dǎo)致最后輸出的是最后一輪的i的值。而使用let則不會(huì)出現(xiàn)這個(gè)問題。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

再來看一個(gè)更常見的例子,了解下如果不用ES6,而用閉包如何解決這個(gè)問題。

var clickBoxs = document.querySelectorAll('.clickBox')
for (var i = 0; i < clickBoxs.length; i++){
    clickBoxs[i].onclick = function(){
        console.log(i)
    }
}

我們本來希望的是點(diǎn)擊不同的clickBox,顯示不同的i,但事實(shí)是無論我們點(diǎn)擊哪個(gè)clickBox,輸出的都是5。下面我們來看下,如何用閉包搞定它。

function iteratorFactory(i){
    var onclick = function(e){
        console.log(i)
    }
    return onclick;
}
var clickBoxs = document.querySelectorAll('.clickBox')
for (var i = 0; i < clickBoxs.length; i++){
    clickBoxs[i].onclick = iteratorFactory(i)
}

const也用來聲明變量,但是聲明的是常量。一旦聲明,常量的值就不能改變。

const PI = Math.PI

PI = 23 //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only

當(dāng)我們嘗試去改變用const聲明的常量時(shí),瀏覽器就會(huì)報(bào)錯(cuò)。
const有一個(gè)很好的應(yīng)用場(chǎng)景,就是當(dāng)我們引用第三方庫的時(shí)聲明的變量,用const來聲明可以避免未來不小心重命名而導(dǎo)致出現(xiàn)bug:

const monent = require('moment')

class, extends, super,constructor

這三個(gè)特性涉及了ES5中最令人頭疼的的幾個(gè)部分:原型、構(gòu)造函數(shù),繼承...你還在為它們復(fù)雜難懂的語法而煩惱嗎?你還在為指針到底指向哪里而糾結(jié)萬分嗎?

有了ES6我們不再煩惱!

ES6提供了更接近傳統(tǒng)語言的寫法,引入了Class(類)這個(gè)概念。新的class寫法讓對(duì)象原型的寫法更加清晰、更像面向?qū)ο缶幊痰恼Z法,也更加通俗易懂。

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        console.log(this.type + ' says ' + say)
    }
}

let animal = new Animal()
animal.says('hello') //animal says hello

class Cat extends Animal {
    constructor(){
        super()
        this.type = 'cat'
    }
}

let cat = new Cat()
cat.says('hello') //cat says hello

上面代碼首先用class定義了一個(gè)“類”,可以看到里面有一個(gè)constructor方法,這就是構(gòu)造方法,而this關(guān)鍵字則代表實(shí)例對(duì)象。簡單地說,constructor內(nèi)定義的方法和屬性是實(shí)例對(duì)象自己的,而constructor外定義的方法和屬性則是所有實(shí)力對(duì)象可以共享的。

Class之間可以通過extends關(guān)鍵字實(shí)現(xiàn)繼承,這比ES5的通過修改原型鏈實(shí)現(xiàn)繼承,要清晰和方便很多。上面定義了一個(gè)Cat類,該類通過extends關(guān)鍵字,繼承了Animal類的所有屬性和方法。

super關(guān)鍵字,它指代父類的實(shí)例(即父類的this對(duì)象)。子類必須在constructor方法中調(diào)用super方法,否則新建實(shí)例時(shí)會(huì)報(bào)錯(cuò)。這是因?yàn)樽宇悰]有自己的this對(duì)象,而是繼承父類的this對(duì)象,然后對(duì)其進(jìn)行加工。如果不調(diào)用super方法,子類就得不到this對(duì)象。

ES6的繼承機(jī)制,實(shí)質(zhì)是先創(chuàng)造父類的實(shí)例對(duì)象this(所以必須先調(diào)用super方法),然后再用子類的構(gòu)造函數(shù)修改this。

P.S 如果你寫react的話,就會(huì)發(fā)現(xiàn)以上三個(gè)東西在最新版React中出現(xiàn)得很多。創(chuàng)建的每個(gè)component都是一個(gè)繼承React.Component的類。

arrow function

這個(gè)恐怕是ES6最最常用的一個(gè)新特性了,用它來寫function比原來的寫法要簡潔清晰很多。

function(i){ return i + 1; } //ES5
(i) => i + 1 //ES6

簡直是簡單的不像話對(duì)吧...
如果方程比較復(fù)雜,則需要用{}把代碼包起來:

function(x, y) { 
    x++;
    y--;
    return x + y;
}
(x, y) => {x++; y--; return x+y}

除了看上去更簡潔以外,arrow function還有一項(xiàng)超級(jí)無敵的功能!
長期以來,JavaScript語言的this對(duì)象一直是一個(gè)令人頭痛的問題,在對(duì)象方法中使用this,必須非常小心。例如:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout(function(){
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}

 var animal = new Animal()
 animal.says('hi')  //undefined says hi

運(yùn)行上面的代碼會(huì)報(bào)錯(cuò),這是因?yàn)閟etTimeout中的this指向的是全局對(duì)象。所以為了讓它能夠正確的運(yùn)行,傳統(tǒng)的解決方法有兩種:

1.第一種是將this傳給self,再用self來指代this

 says(say){
     var self = this;
     setTimeout(function(){
         console.log(self.type + ' says ' + say)
     }, 1000)

2.第二種方法是用bind(this),即

says(say){
     setTimeout(function(){
         console.log(self.type + ' says ' + say)
     }.bind(this), 1000)

但現(xiàn)在我們有了箭頭函數(shù),就不需要這么麻煩了:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout( () => {
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}
 var animal = new Animal()
 animal.says('hi')  //animal says hi

當(dāng)我們使用箭頭函數(shù)時(shí),函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。
并不是因?yàn)榧^函數(shù)內(nèi)部有綁定this的機(jī)制,實(shí)際原因是箭頭函數(shù)根本沒有自己的this,它的this是繼承外面的,因此內(nèi)部的this就是外層代碼塊的this。

template string

這個(gè)東西也是非常有用,當(dāng)我們要插入大段的html內(nèi)容到文檔中時(shí),傳統(tǒng)的寫法非常麻煩,所以之前我們通常會(huì)引用一些模板工具庫,比如mustache等等。

大家可以先看下面一段代碼:

$("#result").append(
  "There are <b>" + basket.count + "</b> " +
  "items in your basket, " +
  "<em>" + basket.onSale +
  "</em> are on sale!"
);

我們要用一堆的'+'號(hào)來連接文本與變量,而使用ES6的新特性模板字符串``后,我們可以直接這么來寫:

$("#result").append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

用反引號(hào)(`)來標(biāo)識(shí)起始,用${}來引用變量,而且所有的空格和縮進(jìn)都會(huì)被保留在輸出之中,是不是非常爽?!

React Router從第1.0.3版開始也使用ES6語法了,比如這個(gè)例子:
<Link to={/taco/${taco.name}}>{taco.name}</Link>

destructuring

ES6允許按照一定模式,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值,這被稱為解構(gòu)(Destructuring)。

看下面的例子:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat: cat, dog: dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

用ES6完全可以像下面這么寫:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat, dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

反過來可以這么寫:

let dog = {type: 'animal', many: 2}
let { type, many} = dog
console.log(type, many)   //animal 2

Set

ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。

Set本身是一個(gè)構(gòu)造函數(shù),用來生成Set數(shù)據(jù)結(jié)構(gòu)。

一個(gè)Set是一群值的集合。它是可變的,能夠增刪元素。

set 它和數(shù)組不同是不會(huì)包含相同元素。試圖再次加入一個(gè)已有元素不會(huì)產(chǎn)生任何效果。

new Set:創(chuàng)建一個(gè)新的、空的Set。

new Set(iterable):從任何可遍歷數(shù)據(jù)中提取元素,構(gòu)造出一個(gè)新的集合。

set.size:獲取集合的大小,即其中元素的個(gè)數(shù)。

set.has(value):判定集合中是否含有指定元素,返回一個(gè)布爾值。

set.add(value):添加元素。如果與已有重復(fù),則不產(chǎn)生效果。

set.delete(value):刪除元素。如果并不存在,則不產(chǎn)生效果。.add()和.delete()都會(huì)返回集合自身,所以我們可以用鏈?zhǔn)秸Z法。

_ setSymbol.iterator:返回一個(gè)新的遍歷整個(gè)集合的迭代器。一般這個(gè)方法不會(huì)被直接調(diào)用,因?yàn)閷?shí)際上就是它使集合能夠被遍歷,也就是說,我們可以直接寫for (v of set) {...}等等。

set.forEach(f):類似于數(shù)組的.forEach()方法。 直接上代碼。

for (let value of set) { f(value, value, set); }

set.clear():清空集合。

set.keys()、set.values()和set.entries()返回各種迭代器,它們是為了兼容Map而提供的.

var s = new Set();

[2,3,5,4,5,2,2].map(x => s.add(x))

for (i of s) {console.log(i)}
// 2 3 5 4

上面代碼通過add方法向Set結(jié)構(gòu)加入成員,結(jié)果表明Set結(jié)構(gòu)不會(huì)添加重復(fù)的值。

> arrayOfWords[15000] 
    "anapanapa" 
> setOfWords[15000]    
    undefined

Set不支持索引

arr.indexOf('a') !== -1 //慢
//true
setOfWords.has('a') //快 
//true

Set的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)專門為一種操作作了速度優(yōu)化:包含性檢測(cè)。

Map

一個(gè)Map對(duì)象由若干鍵值對(duì)組成,支持:

new Map:返回一個(gè)新的、空的Map。

new Map(pairs):根據(jù)所含元素形如[key,value]的數(shù)組pairs來創(chuàng)建一個(gè)新的Map。這里提供的 pairs可以是一個(gè)已有的Map對(duì)象,可以是一個(gè)由二元數(shù)組組成的數(shù)組,也可以是逐個(gè)生成二元數(shù)組的一個(gè)生成器,等等。

map.size:返回Map中項(xiàng)目的個(gè)數(shù)。

map.has(key):測(cè)試一個(gè)鍵名是否存在,類似key in obj。

map.get(key):返回一個(gè)鍵名對(duì)應(yīng)的值,若鍵名不存在則返回undefined,類似obj[key]。

map.set(key, value):添加一對(duì)新的鍵值對(duì),如果鍵名已存在就覆蓋。

map.delete(key):按鍵名刪除一項(xiàng),類似delete obj[key]。

map.clear():清空Map。

mapSymbol.iterator:返回遍歷所有項(xiàng)的迭代器,每項(xiàng)用一個(gè)鍵和值組成的二元數(shù)組表示。

map.forEach(f) 類似 for (let [key, value] of map) { f(value, key, map); } 。 這 里 詭 異 的 參 數(shù) 順 序 , 和 Set 中 一 樣 , 是 對(duì) 應(yīng) 著數(shù)組的forEach()。

map.keys():返回遍歷所有鍵的迭代器。

map.values():返回遍歷所有值的迭代器。

map.entries():返回遍歷所有項(xiàng)的迭代器,就像mapSymbol.iterator。實(shí)際上,它們就是同一個(gè)方法,不同名字。
先從書上把map的api記下來,

Map數(shù)據(jù)結(jié)構(gòu)類似于對(duì)象,同樣是鍵值對(duì)的集合,但是“鍵”的范圍不限于字符串,各種類型的值(包括對(duì)象)都可以當(dāng)作鍵。也就是說,Object結(jié)構(gòu)提供了“字符串—值”的對(duì)應(yīng),Map結(jié)構(gòu)提供了“值—值”的對(duì)應(yīng),是一種更完善的Hash結(jié)構(gòu)實(shí)現(xiàn)。如果你需要“鍵值對(duì)”的數(shù)據(jù)結(jié)構(gòu),Map比Object更合適。

var m = new Map();
var o = {p: "Hello World"};
m.set(o, "content")
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false

作為構(gòu)造函數(shù),Map也可以接受一個(gè)數(shù)組作為參數(shù)。該數(shù)組的成員是一個(gè)個(gè)表示鍵值對(duì)的數(shù)組。(感覺真強(qiáng)大)

var map = new Map([["name", "張三"], ["title", "Author"]]);
map.size // 2
map.has("name") // true
map.get("name") // "張三"
map.has("title") // true
map.get("title") // "Author"

上面代碼在新建Map實(shí)例時(shí),就指定了兩個(gè)鍵name
和title。

* 注意,只有對(duì)同一個(gè)對(duì)象的引用,Map結(jié)構(gòu)才將其視為同一個(gè)鍵。這一點(diǎn)要非常小心。

var map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined

let b = ['b'];
map.set(b, 555);
map.get(b) // 555

上面代碼的set和get方法,表面是針對(duì)同一個(gè)鍵,但實(shí)際上這是兩個(gè)值,內(nèi)存地址是不一樣的,因此get方法無法讀取該鍵,返回undefined。
這個(gè)也比較好理解,因?yàn)檫@兩個(gè)['a']是兩個(gè)不同的數(shù)組對(duì)象。

有一個(gè)壞處。 Map和Set都為內(nèi)部的每個(gè)鍵或值保持了強(qiáng)引用,也就是說,如果一個(gè) DOM 元素被移除了,回收機(jī)制無法取回它占用的內(nèi)存,除非 movingSet中也刪除了它。在最理想的情況下,庫在善后工作上對(duì)使用者都有復(fù)雜的要求,所以,這很可能引發(fā)內(nèi)存泄露。

所已有了 WeakSet

WeakSet與Set有兩個(gè)區(qū)別:

WeakSet的成員只能是對(duì)象,而不能是其他類型的值。
WeakSet中的對(duì)象都是弱引用,即垃圾回收機(jī)制不考慮WeakSet對(duì)該對(duì)象的引用,也就是說,如果其他對(duì)象都不再引用該對(duì)象,那么垃圾回收機(jī)制會(huì)自動(dòng)回收該對(duì)象所占用的內(nèi)存,不考慮該對(duì)象還存在于WeakSet之中。這個(gè)特點(diǎn)意味著,無法引用WeakSet的成員,因此WeakSet是不可遍歷的。

var ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set

上面代碼試圖向WeakSet添加一個(gè)數(shù)值和Symbol值,結(jié)果報(bào)錯(cuò)。

WeakSet結(jié)構(gòu)有以下三個(gè)方法。

WeakSet.prototype.add(value):向WeakSet實(shí)例添加一個(gè)新成員。

WeakSet.prototype.delete(value):清除WeakSet實(shí)例的指定成員。

WeakSet.prototype.has(value):返回一個(gè)布爾值,表示某個(gè)值是否在WeakSet實(shí)例之中。

下面是一個(gè)例子。

var ws = new WeakSet();
var obj = {};
var foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false

ws.delete(window);
ws.has(window);    // false
WeakSet沒有size屬性,沒有辦法遍歷它的成員。
ws.size // undefined
ws.forEach // undefined

ws.forEach(function(item){ console.log('WeakSet has ' + item)})
// TypeError: undefined is not a function

上面代碼試圖獲取size和forEach屬性,結(jié)果都不能成功。

WeakSet不能遍歷,是因?yàn)槌蓡T都是弱引用,隨時(shí)可能消失,遍歷機(jī)制無法保證成員的存在,很可能剛剛遍歷結(jié)束,成員就取不到了。WeakSet的一個(gè)用處,是儲(chǔ)存DOM節(jié)點(diǎn),而不用擔(dān)心這些節(jié)點(diǎn)從文檔移除時(shí),會(huì)引發(fā)內(nèi)存泄漏。

著作權(quán)聲明

本文參考資料 ES6學(xué)習(xí)筆記 30分鐘掌握ES6/ES2015核心內(nèi)容
著者 2Youngg,轉(zhuǎn)載請(qǐng)保留以上鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.Set 基本用法 ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。Set本...
    雨飛飛雨閱讀 1,940評(píng)論 0 7
  • 下面不屬于ECMAScript規(guī)范的范圍的是:( )A、 數(shù)據(jù)類型B、 語法C、 DOM事件D、 內(nèi)置對(duì)象和函數(shù)的...
    亓凡閱讀 3,137評(píng)論 3 9
  • 強(qiáng)大的for-of循環(huán) ES6不會(huì)破壞你已經(jīng)寫好的JS代碼。目前看來,成千上萬的Web網(wǎng)站依賴for-in循環(huán),其...
    Awe閱讀 7,582評(píng)論 2 7
  • 三,字符串?dāng)U展 3.1 Unicode表示法 ES6 做出了改進(jìn),只要將碼點(diǎn)放入大括號(hào),就能正確解讀該字符。有了這...
    eastbaby閱讀 1,667評(píng)論 0 8
  • 人生都要遇見四個(gè)人,第一個(gè)是你愛但不愛你的人,第二個(gè)是愛你但你不愛的人,第三個(gè)是你愛又愛你但最后不能在一起的人,第...
    五木先生努力閱讀 447評(píng)論 4 2

友情鏈接更多精彩內(nèi)容