国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

??
Object literal
屬性的簡(jiǎn)潔表示法
可計(jì)算的屬性名
方法定義
???? ??? ?? ????" >????? ?? ???????? ????? ? ?? ???? ??? ???? ???? ?? ??? ???? ???, ? ?? ?? ??? ???? ?? ??? ?? ??? ?????. ?? ??, ??? ?? ES5? ???? events ??? listeners ??? listeners?? ??? ????? ???. ??%%PRE_BLOCK_12%%??ES6? ???? ??? ?? ???? ??? ? ????. ??%%PRE_BLOCK_13%%?????, ?? ???? ???? ??? ?? ??? ???? ??? ??? ?? ?? ???? ??? ?? ? ????. ??????? ES6? ???? ?? ? ?????. ? ???? ??? ?? ?? ??? ???? ??? ???? ?? ?? ???? ?? ??????. ??

???? ??? ?? ????

????? ????
箭頭函數(shù)
詞法作用域
簡(jiǎn)寫(xiě)的箭頭函數(shù)
簡(jiǎn)寫(xiě)箭頭函數(shù)帶來(lái)的一些問(wèn)題
該何時(shí)使用箭頭函數(shù)
解構(gòu)賦值
對(duì)象解構(gòu)
數(shù)組解構(gòu)
函數(shù)默認(rèn)參數(shù)
函數(shù)參數(shù)解構(gòu)
解構(gòu)使用示例
剩余參數(shù)和拓展符
剩余參數(shù)rest
拓展運(yùn)算符
Shifting和Spreading
模板字符串
在字符串中插值
多行文本模板
標(biāo)記模板
let & const 聲明
塊作用域和let 聲明
Temporal Dead Zone
Const 聲明
通過(guò)const聲明的變量值并非不可改變
constlet的優(yōu)點(diǎn)
? ? ????? ????? Q&A es6?? ?? ?? ??? ?????

es6?? ?? ?? ??? ?????

Jan 12, 2023 pm 07:51 PM
es6

ES6 ?? ???? ??? ?????. 1. "{}" ???? ?? ???? ??? ???? ?? ??? 2. ?? ??? ???? ??? ??? ??? ?? 3. ??? ???? ?? ?? ??; ?? ??, ?? ?? ???? ?? ???? ??? ?? ?????. 4. ??? ???? ? ??? 5. ??? ???;

es6?? ?? ?? ??? ?????

? ????? ?? ??: Windows 7 ???, ECMAScript ?? 6, Dell G3 ???.

?? ??: ??? ? ?? ??? ? ?? ????? ??? ??? ????. ?? ?? ?????? ??? ? ?? ??? ? ??? ??? ? ???? ?????? ?????? ?? ??? ? ??? ? ????. ????? ?? .

ES6? ?? ?? ??? ?? ????? ????? ?????. ??? ????? ???? ?? ????? ?? ?? ???? ??? ? ????. ?? ? ??? ??? ??? ? ?? ??? ??? ES5?? ??? ? ??? ?????. ????? ?? ? ?????. ? ???? ??? ?? ??? ??? ? ????. ? ??? ?? ?? ??? ??? ES6 ?? ? ??? ??? ??? ? ????.

Object literal

Object literal? ??? ?? {} ???? ?? ???? ??? ?????.
{}形式直接表示的對(duì)象,比如下面這樣:

var book = {
  title: 'Modular ES6',
  author: 'Nicolas',
  publisher: 'O′Reilly'
}

ES6 為對(duì)象字面量的語(yǔ)法帶來(lái)了一些改進(jìn):包括屬性/方法的簡(jiǎn)潔表示,可計(jì)算的屬性名等等,我們逐一來(lái)看:

屬性的簡(jiǎn)潔表示法

你有沒(méi)有遇到過(guò)這種場(chǎng)景,一個(gè)我們聲明的對(duì)象中包含若干屬性,其屬性值由變量表示,且變量名和屬性名一樣的。比如下面這樣,我們想把一個(gè)名為 listeners 的數(shù)組賦值給events對(duì)象中的listeners屬性,用ES5我們會(huì)這樣做:

var listeners = []
function listen() {}
var events = {
  listeners: listeners,
  listen: listen
}

ES6則允許我們簡(jiǎn)寫(xiě)成下面這種形式:

var listeners = []
function listen() {}
var events = { listeners, listen }

怎么樣,是不是感覺(jué)簡(jiǎn)潔了許多,使用對(duì)象字面量的簡(jiǎn)潔寫(xiě)法讓我們?cè)诓挥绊懻Z(yǔ)義的情況下減少了重復(fù)代碼。

這是ES6帶來(lái)的好處之一,它提供了眾多更簡(jiǎn)潔,語(yǔ)義更清晰的語(yǔ)法,讓我們的代碼的可讀性,可維護(hù)性大大提升。

可計(jì)算的屬性名

對(duì)象字面量的另一個(gè)重要更新是允許你使用可計(jì)算的屬性名,在ES5中我們也可以給對(duì)象添加屬性名為變量的屬性,一般說(shuō)來(lái),我們要按下面方法這樣做,首先聲明一個(gè)名為expertise的變量,然后通過(guò)person[expertise]這種形式把變量添加為對(duì)象person的屬性:

var expertise = 'journalism'
var person = {
  name: 'Sharon',
  age: 27
}
person[expertise] = {
  years: 5,
  interests: ['international', 'politics', 'internet']
}

ES6 中,對(duì)象字面量可以使用計(jì)算屬性名了,把任何表達(dá)式放在中括號(hào)中,表達(dá)式的運(yùn)算結(jié)果將會(huì)是對(duì)應(yīng)的屬性名,上面的代碼,用ES6可以這樣寫(xiě):

var expertise = 'journalism'
var person = {
  name: 'Sharon',
  age: 27,
  [expertise]: {
    years: 5,
    interests: ['international', 'politics', 'internet']
  }
}

不過(guò)需要注意的是,簡(jiǎn)寫(xiě)屬性和計(jì)算的屬性名不可同時(shí)使用。這是因?yàn)椋?jiǎn)寫(xiě)屬性是一種在編譯階段的就會(huì)生效的語(yǔ)法糖,而計(jì)算的屬性名則在運(yùn)行時(shí)才生效。如果你把二者混用,代碼會(huì)報(bào)錯(cuò)。而且二者混用往往還會(huì)降低代碼的可讀性,所以JavaScript在語(yǔ)言層面上限制二者不能混用也是個(gè)好事。

var expertise = 'journalism'
var journalism = {
  years: 5,
  interests: ['international', 'politics', 'internet']
}
var person = {
  name: 'Sharon',
  age: 27,
  [expertise] // 這里會(huì)報(bào)語(yǔ)法錯(cuò)誤
}

遇到以下情景時(shí),可計(jì)算的屬性名會(huì)讓我們的代碼更簡(jiǎn)潔:

  • 某個(gè)新對(duì)象的屬性引自另一個(gè)對(duì)象:

var grocery = {
  id: 'bananas',
  name: 'Bananas',
  units: 6,
  price: 10,
  currency: 'USD'
}
var groceries = {
  [grocery.id]: grocery
}
  • 需構(gòu)建的對(duì)象的屬性名來(lái)自函數(shù)參數(shù)。如果使用ES5來(lái)處理這種問(wèn)題,我們需要先聲明一個(gè)對(duì)象字面量,再動(dòng)態(tài)的添加屬性,再返回這個(gè)對(duì)象。下面的例子中,我們創(chuàng)建了一個(gè)響應(yīng)Ajax請(qǐng)求的函數(shù),這個(gè)函數(shù)的作用在于,請(qǐng)求失敗時(shí),返回的對(duì)象擁有一個(gè)名為error屬性及對(duì)應(yīng)的描述,請(qǐng)求成功時(shí),該對(duì)象擁有一個(gè)名為success屬性及對(duì)應(yīng)的描述。

// ES5 寫(xiě)法
function getEnvelope(type, description) {
  var envelope = {
    data: {}
  }
  envelope[type] = description
  return envelope
}

使用ES6提供的利用計(jì)算屬性名,更簡(jiǎn)潔的實(shí)現(xiàn)如下:

// ES6 寫(xiě)法
function getEnvelope(type, description) {
  return {
    data: {},
    [type]: description
  }
}

對(duì)象字面量的屬性可以簡(jiǎn)寫(xiě),方法其實(shí)也是可以的。

方法定義

我們先看看傳統(tǒng)上如何定義對(duì)象方法,下述代碼中,我們構(gòu)建了一個(gè)事件發(fā)生器,其中的on方法用以注冊(cè)事件,emit方法用以執(zhí)行事件:

var emitter = {
  events: {},
  on: function (type, fn) {
    if (this.events[type] === undefined) {
      this.events[type] = []
    }
    this.events[type].push(fn)
  },
  emit: function (type, event) {
    if (this.events[type] === undefined) {
      return
    }
    this.events[type].forEach(function (fn) {
      fn(event)
    })
  }
}

ES6 的對(duì)象字面量方法簡(jiǎn)寫(xiě)允許我們省略對(duì)象方法的function關(guān)鍵字及之后的冒號(hào),改寫(xiě)后的代碼如下:

var emitter = {
  events: {},
  on(type, fn) {
    if (this.events[type] === undefined) {
      this.events[type] = []
    }
    this.events[type].push(fn)
  },
  emit(type, event) {
    if (this.events[type] === undefined) {
      return
    }
    this.events[type].forEach(function (fn) {
      fn(event)
    })
  }
}

ES6中的箭頭函數(shù)可謂大名鼎鼎了,它有一些特別的優(yōu)點(diǎn)(關(guān)于this

function name(parameters) {
  // function body
}

ES6? object? ?????. ??? ??/???? ??? ??, ?? ??? ?? ?? ? ? ?? ??? ???????. ??? ???????. ??

????? ?? ???????? ????? ? ?? ???? ??? ???? ???? ?? ??? ???? ???, ? ?? ?? ??? ???? ?? ??? ?? ??? ?????. ?? ??, ??? ?? ES5? ???? events ??? listeners ??? listeners?? ??? ????? ???. ??
var example = function (parameters) {
  // function body
}
??ES6? ???? ??? ?? ???? ??? ? ????. ??
var example = (parameters) => {
  // function body
}
?????, ?? ???? ???? ??? ?? ??? ???? ??? ??? ?? ?? ???? ??? ?? ? ????. ??????? ES6? ???? ?? ? ?????. ? ???? ??? ?? ?? ??? ???? ??? ???? ?? ?? ???? ?? ??????. ??

???? ??? ?? ????

???? ???? ?? ? ?? ??? ????? ES5?? ?? ??? ?? ??? ??? ? ??? ???? ????. ??? ??? ?????. ????? ??? ?? ???? ? ??? ???? ???. ?? expertise?? ??? ??? ?? person[expertise]? ?????. ? ??? ??? ??? ?? ?????. ?? person? ??: ??
// ES5
var timer = {
  seconds: 0,
  start() {
    setInterval(function(){
      this.seconds++
    }, 1000)
  }
}

timer.start()
setTimeout(function () {
  console.log(timer.seconds)
}, 3500)

> 0
??ES6?? ?? ???? ??? ?? ??? ??? ? ????. ???? ??? ?? ??? ???? ??? ?? ?? ??? ???. . ? ??? ES6?? ??? ?? ??? ? ????. ??
// ES6
var timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++
    }, 1000)
  }
}

timer.start()
setTimeout(function () {
  console.log(timer.seconds)
}, 3500)
// <- 3
???, ????? ??? ??? ?? ??? ??? ??? ? ??????. ?? ??? ??? ??? ???? ???? ?? ??? ?? ??? ?? ??? ???? ???? ?????. ? ??? ???? ???? ??? ?????. ??? ?? ???? ??? ???? ???? ??? ?? ??? JavaScript? ?? ???? ?? ??? ???? ?? ?? ????. ??
function puzzle() {
  return function () {
    console.log(arguments)
  }
}
puzzle(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)(1, 2, 3)
???? ????? ??? ? ?? ??? ?? ??? ??? ?? ???? ????. ??
  • ??? ??? ??? ?? ???? ?????. : ??
function puzzle() {
  return ()=>{
    console.log(arguments)
  }
}
puzzle(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)(1, 2, 3)
  • ????? ??? ?? ??? ?? ?????? ???. ? ??? ???? ?? ES5? ????? ?? ?? ???? ??? ?? ???? ??? ???? ??? ???? ???. ?? ???? Ajax ??? ???? ??? ????. ? ??? ??? ??? ??? ? ??? ??? error?? ??? ?? ??? ??? ????. ???? ???? success?? ??? ?? ??? ????. ??
var example = (parameters) => {
  // function body
}
??ES6?? ???? ??? ???? ???? ??? ?? ?? ???? ?? ?????. ??
var double = value => {
  return value * 2
}
???? ???? ??? ??? ????, ??? ???? ?????. ??

????? ????

???? ????? ?? ???? ???? ??? ???????. ?? ????? on ???? ???? ???? ? ???? emit ???? ???? ???? ? ?????. ??
var double = (value) => value * 2
??ES6? ?? ??? ??? ??? ???? ??? ??? ? ????. ?? ??? ???? ? ?? ??? ?? ?? ??? ??? ??? ????: ??
var double = value => value * 2
??ES6? ??? ??? ???? ? ?? ??? ??? ????(??? ??). ??? ???? ??? ????. ?, ??? ??? ???? ?? ???? ??? ???, ??? ??? ?? ??? ?? ? ?? ?? ? ???? ???? ??? ? ?? ?? ??? ????. ??

箭頭函數(shù)


JS中聲明的普通函數(shù),一般有函數(shù)名,一系列參數(shù)和函數(shù)體,如下:

function name(parameters) {
  // function body
}

普通匿名函數(shù)則沒(méi)有函數(shù)名,匿名函數(shù)通常會(huì)被賦值給一個(gè)變量/屬性,有時(shí)候還會(huì)被直接調(diào)用:

var example = function (parameters) {
  // function body
}

ES6 為我們提供了一種寫(xiě)匿名函數(shù)的新方法,即箭頭函數(shù)。箭頭函數(shù)不需要使用function關(guān)鍵字,其參數(shù)和函數(shù)體之間以=>相連接:

var example = (parameters) => {
  // function body
}

盡管箭頭函數(shù)看起來(lái)類似于傳統(tǒng)的匿名函數(shù),他們卻具有根本性的不同:

  • 箭頭函數(shù)不能被直接命名,不過(guò)允許它們賦值給一個(gè)變量;

  • 箭頭函數(shù)不能用做構(gòu)造函數(shù),你不能對(duì)箭頭函數(shù)使用new關(guān)鍵字;

  • 箭頭函數(shù)也沒(méi)有prototype屬性;

  • 箭頭函數(shù)綁定了詞法作用域,不會(huì)修改this的指向。

最后一點(diǎn)是箭頭函數(shù)最大的特點(diǎn),我們來(lái)仔細(xì)看看。

詞法作用域

我們?cè)诩^函數(shù)的函數(shù)體內(nèi)使用的this,arguments,super等都指向包含箭頭函數(shù)的上下文,箭頭函數(shù)本身不產(chǎn)生新的上下文。下述代碼中,我們創(chuàng)建了一個(gè)名為timer的對(duì)象,它的屬性seconds用以計(jì)時(shí),方法start用以開(kāi)始計(jì)時(shí),若我們?cè)谌舾擅牒笳{(diào)用start方法,將打印出當(dāng)前的seconds值。

// ES5
var timer = {
  seconds: 0,
  start() {
    setInterval(function(){
      this.seconds++
    }, 1000)
  }
}

timer.start()
setTimeout(function () {
  console.log(timer.seconds)
}, 3500)

> 0
// ES6
var timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++
    }, 1000)
  }
}

timer.start()
setTimeout(function () {
  console.log(timer.seconds)
}, 3500)
// <- 3

第一段代碼中start方法使用的是常規(guī)的匿名函數(shù)定義,在調(diào)用時(shí)this將指向了windowconsole出的結(jié)果為undefined,想要讓代碼正常工作,我們需要在start方法開(kāi)頭處插入var self = this,然后替換匿名函數(shù)函數(shù)體中的thisself,第二段代碼中,我們使用了箭頭函數(shù),就不會(huì)發(fā)生這種情況了。

還需要說(shuō)明的是,箭頭函數(shù)的作用域也不能通過(guò).call,.apply,.bind等語(yǔ)法來(lái)改變,這使得箭頭函數(shù)的上下文將永久不變。

我們?cè)賮?lái)看另外一個(gè)箭頭函數(shù)與普通匿名函數(shù)的不同之處,你猜猜,下面的代碼最終打印出的結(jié)果會(huì)是什么:

function puzzle() {
  return function () {
    console.log(arguments)
  }
}
puzzle(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)(1, 2, 3)

答案是1,2,3,原因是對(duì)常規(guī)匿名函數(shù)而言,arguments指向匿名函數(shù)本身。

作為對(duì)比,我們看看下面這個(gè)例子,再猜猜,打印結(jié)果會(huì)是什么?

function puzzle() {
  return ()=>{
    console.log(arguments)
  }
}
puzzle(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)(1, 2, 3)

答案是a,b,c,箭頭函數(shù)的特殊性決定其本身沒(méi)有arguments對(duì)象,這里的arguments其實(shí)是其父函數(shù)puzzle的。

前面我們提到過(guò),箭頭函數(shù)還可以簡(jiǎn)寫(xiě),接下來(lái)我們一起看看。

簡(jiǎn)寫(xiě)的箭頭函數(shù)

完整的箭頭函數(shù)是這樣的:

var example = (parameters) => {
  // function body
}

簡(jiǎn)寫(xiě)1:

當(dāng)只有一個(gè)參數(shù)時(shí),我們可以省略箭頭函數(shù)參數(shù)兩側(cè)的括號(hào):

var double = value => {
  return value * 2
}

簡(jiǎn)寫(xiě)2:

對(duì)只有單行表達(dá)式且,該表達(dá)式的值為返回值的箭頭函數(shù)來(lái)說(shuō),表征函數(shù)體的{},可以省略,return 關(guān)鍵字可以省略,會(huì)靜默返回該單一表達(dá)式的值。

var double = (value) => value * 2

簡(jiǎn)寫(xiě)3:

上述兩種形式可以合并使用,而得到更加簡(jiǎn)潔的形式

var double = value => value * 2

現(xiàn)在,你肯定學(xué)會(huì)了箭頭函數(shù)的基本使用方法,接下來(lái)我們?cè)倏磶讉€(gè)使用示例。

簡(jiǎn)寫(xiě)箭頭函數(shù)帶來(lái)的一些問(wèn)題

當(dāng)你的簡(jiǎn)寫(xiě)箭頭函數(shù)返回值為一個(gè)對(duì)象時(shí),你需要用小括號(hào)括起你想返回的對(duì)象。否則,瀏覽器會(huì)把對(duì)象的{}解析為箭頭函數(shù)函數(shù)體的開(kāi)始和結(jié)束標(biāo)記。

// 正確的使用形式
var objectFactory = () => ({ modular: &#39;es6&#39; })

下面的代碼會(huì)報(bào)錯(cuò),箭頭函數(shù)會(huì)把本想返回的對(duì)象的花括號(hào)解析為函數(shù)體,number被解析為label,value解釋為沒(méi)有做任何事情表達(dá)式,我們又沒(méi)有顯式使用return,返回值默認(rèn)是undefined。

[1, 2, 3].map(value => { number: value })
// <- [undefined, undefined, undefined]

當(dāng)我們返回的對(duì)象字面量不止一個(gè)屬性時(shí),瀏覽器編譯器不能正確解析第二個(gè)屬性,這時(shí)會(huì)拋出語(yǔ)法錯(cuò)誤。

[1, 2, 3].map(value => { number: value, verified: true })
// <- SyntaxError

解決方案是把返回的對(duì)象字面量包裹在小括號(hào)中,以助于瀏覽器正確解析:

[1, 2, 3].map(value => ({ number: value, verified: true }))
/* <- [
  { number: 1, verified: true },
  { number: 2, verified: true },
  { number: 3, verified: true }]
*/

該何時(shí)使用箭頭函數(shù)

其實(shí)我們并不應(yīng)該盲目的在一切地方使用ES6,ES6也不是一定比ES5要好,是否使用主要看其能否改善代碼的可讀性和可維護(hù)性。

箭頭函數(shù)也并非適用于所有的情況,比如說(shuō),對(duì)于一個(gè)行數(shù)很多的復(fù)雜函數(shù),使用=>代替function關(guān)鍵字帶來(lái)的簡(jiǎn)潔性并不明顯。不過(guò)不得不說(shuō),對(duì)于簡(jiǎn)單函數(shù),箭頭函數(shù)確實(shí)能讓我們的代碼更簡(jiǎn)潔。

給函數(shù)以合理的命名,有助于增強(qiáng)程序的可讀性。箭頭函數(shù)并不能直接命名,但是卻可以通過(guò)賦值給變量的形式實(shí)現(xiàn)間接命名,如下代碼中,我們把箭頭函數(shù)賦值給變量 throwError,當(dāng)函數(shù)被調(diào)用時(shí),會(huì)拋出錯(cuò)誤,我們可以追溯到是箭頭函數(shù)throwError報(bào)的錯(cuò)。

var throwError = message => {
  throw new Error(message)
}
throwError(&#39;this is a warning&#39;)
<- Uncaught Error: this is a warning
  at throwError

如果你想完全控制你的函數(shù)中的this,使用箭頭函數(shù)是簡(jiǎn)潔高效的,采用函數(shù)式編程尤其如此。

[1, 2, 3, 4]
  .map(value => value * 2)
  .filter(value => value > 2)
  .forEach(value => console.log(value))// <- 4// <- 6// <- 8

解構(gòu)賦值

ES6提供的最靈活和富于表現(xiàn)性的新特性莫過(guò)于解構(gòu)了。一旦你熟悉了,它用起來(lái)也很簡(jiǎn)單,某種程度上解構(gòu)可以看做是變量賦值的語(yǔ)法糖,可應(yīng)用于對(duì)象,數(shù)組甚至函數(shù)的參數(shù)。

對(duì)象解構(gòu)

為了更好的描述對(duì)象解構(gòu)如何使用,我們先構(gòu)建下面這樣一個(gè)對(duì)象(漫威迷一定知道這個(gè)對(duì)象描述的是誰(shuí)):

// 描述Bruce Wayne的對(duì)象
var character = {
  name: &#39;Bruce&#39;,
  pseudonym: &#39;Batman&#39;,
  metadata: {
    age: 34,
    gender: &#39;male&#39;
  },
  batarang: [&#39;gas pellet&#39;, &#39;bat-mobile control&#39;, &#39;bat-cuffs&#39;]
}

假如現(xiàn)有有一個(gè)名為 pseudonym 的變量,我們想讓其變量值指向character.pseudonym,使用ES5,你往往會(huì)按下面這樣做:

var pseudonym = character.pseudonym

ES6致力于讓我們的代碼更簡(jiǎn)潔,通過(guò)ES6我們可以用下面的代碼實(shí)現(xiàn)一樣的功能:

var { pseudonym } = character

如同你可以使用var加逗號(hào)在一行中同時(shí)聲明多個(gè)變量,解構(gòu)的花括號(hào)內(nèi)使用逗號(hào)可以做一樣的事情。

var { pseudonym, name } = character

我們還可以混用解構(gòu)和常規(guī)的自定義變量,這也是解構(gòu)語(yǔ)法靈活性的表現(xiàn)之一。

var { pseudonym } = character, two = 2

解構(gòu)還允許我們使用別名,比如我們想把character.pseudonym賦值給變量 alias,可以按下面的語(yǔ)句這樣做,只需要在pseudonym后面加上:即可:

var { pseudonym: alias } = character
console.log(alias)
// <- &#39;Batman&#39;

解構(gòu)還有另外一個(gè)強(qiáng)大的功能,解構(gòu)值還可以是對(duì)象:

var { metadata: { gender } } = character

當(dāng)然,對(duì)于多層解構(gòu),我們同樣可以賦予別名,這樣我們可以通過(guò)非常簡(jiǎn)潔的方法修改子屬性的名稱:

var { metadata: { gender: characterGender } } = character

在ES5 中,當(dāng)你調(diào)用一個(gè)未曾聲明的值時(shí),你會(huì)得到undefined:

console.log(character.boots)
// <- undefined
console.log(character[&#39;boots&#39;])
// <- undefined

使用解構(gòu),情況也是類似的,如果你在左邊聲明了一個(gè)右邊對(duì)象中不存在的屬性,你也會(huì)得到undefined.

var { boots } = character
console.log(boots)
// <- undefined

對(duì)于多層解構(gòu),如下述代碼中,boots并不存在于character中,這時(shí)程序會(huì)拋出異常,這就好比你你調(diào)用undefined或者null的屬性時(shí)會(huì)出現(xiàn)異常。

var { boots: { size } } = character
// <- Exception
var { missing } = null
// <- Exception

解構(gòu)其實(shí)就是一種語(yǔ)法糖,看以下代碼,你肯定就能很快理解為什么會(huì)拋出異常了。

var nothing = null
var missing = nothing.missing
// <- Exception

解構(gòu)也可以添加默認(rèn)值,如果右側(cè)不存在對(duì)應(yīng)的值,默認(rèn)值就會(huì)生效,添加的默認(rèn)值可以是數(shù)值,字符串,函數(shù),對(duì)象,也可以是某一個(gè)已經(jīng)存在的變量:

var { boots = { size: 10 } } = character
console.log(boots)
// <- { size: 10 }

對(duì)于多層的解構(gòu),同樣可以使用默認(rèn)值

var { metadata: { enemy = &#39;Satan&#39; } } = character
console.log(enemy)
// <- &#39;Satan&#39;

默認(rèn)值和別名也可以一起使用,不過(guò)需要注意的是別名要放在前面,默認(rèn)值添加給別名:

var { boots: footwear = { size: 10 } } = character

對(duì)象解構(gòu)同樣支持計(jì)算屬性名,但是這時(shí)候你必須要添加別名,這是因?yàn)橛?jì)算屬性名允許任何類似的表達(dá)式,不添加別名,瀏覽器解析時(shí)會(huì)有問(wèn)題,使用如下:

var { [&#39;boo&#39; + &#39;ts&#39;]: characterBoots } = character
console.log(characterBoots)
// <- true

還是那句話,我們也不是任何情況下都應(yīng)該使用解構(gòu),語(yǔ)句characterBoots = character[type]看起來(lái)比{ [type]: characterBoots } = character語(yǔ)義更清晰,但是當(dāng)你需要提取對(duì)象中的子對(duì)象時(shí),解構(gòu)就很簡(jiǎn)潔方便了。

我們?cè)倏纯丛跀?shù)組中該如何使用解構(gòu)。

數(shù)組解構(gòu)

數(shù)組解構(gòu)的語(yǔ)法和對(duì)象解構(gòu)是類似的。區(qū)別在于,數(shù)組解構(gòu)我們使用中括號(hào)而非花括號(hào),下面的代碼中,通過(guò)結(jié)構(gòu),我們?cè)跀?shù)組coordinates中提出了變量 x,y 。 你不需要使用x = coordinates[0]這樣的語(yǔ)法了,數(shù)組解構(gòu)不使用索引值,但卻讓你的代碼更加清晰。

var coordinates = [12, -7]
var [x, y] = coordinates
console.log(x)
// <- 12

數(shù)組解構(gòu)也允許你跳過(guò)你不想用到的值,在對(duì)應(yīng)地方留白即可:

var names = [&#39;James&#39;, &#39;L.&#39;, &#39;Howlett&#39;]
var [ firstName, , lastName ] = names
console.log(lastName)
// <- &#39;Howlett&#39;

和對(duì)象解構(gòu)一樣,數(shù)組解構(gòu)也允許你添加默認(rèn)值:

var names = [&#39;James&#39;, &#39;L.&#39;]
var [ firstName = &#39;John&#39;, , lastName = &#39;Doe&#39; ] = names
console.log(lastName)
// <- &#39;Doe&#39;

在ES5中,你需要借助第三個(gè)變量,才能完成兩個(gè)變量值的交換,如下:

var left = 5, right = 7;
var aux = left
left = right
right = aux

使用解構(gòu),一切就簡(jiǎn)單多了:

var left = 5, right = 7;
[left, right] = [right, left]

我們?cè)倏纯春瘮?shù)解構(gòu)。

函數(shù)默認(rèn)參數(shù)

在ES6中,我們可以給函數(shù)的參數(shù)添加默認(rèn)值了,下例中我們就給參數(shù) exponent 分配了一個(gè)默認(rèn)值:

function powerOf(base, exponent = 2) {
  return Math.pow(base, exponent)
}

箭頭函數(shù)同樣支持使用默認(rèn)值,需要注意的是,就算只有一個(gè)參數(shù),如果要給參數(shù)添加默認(rèn)值,參數(shù)部分一定要用小括號(hào)括起來(lái)。

var double = (input = 0) => input * 2

我們可以給任何位置的任何參數(shù)添加默認(rèn)值。

function sumOf(a = 1, b = 2, c = 3) {
  return a + b + c
}
console.log(sumOf(undefined, undefined, 4))
// <- 1 + 2 + 4 = 7

在JS中,給一個(gè)函數(shù)提供一個(gè)包含若干屬性的對(duì)象字面量做為參數(shù)的情況并不常見(jiàn),不過(guò)你依舊可以按下面方法這樣做:

var defaultOptions = { brand: &#39;Volkswagen&#39;, make: 1999 }
function carFactory(options = defaultOptions) {
  console.log(options.brand)
  console.log(options.make)
}
carFactory()
// <- &#39;Volkswagen&#39;
// <- 1999

不過(guò)這樣做存在一定的問(wèn)題,當(dāng)你調(diào)用該函數(shù)時(shí),如果傳入的參數(shù)對(duì)象只包含一個(gè)屬性,另一個(gè)屬性的默認(rèn)值會(huì)自動(dòng)失效:

carFactory({ make: 2000 })
// <- undefined
// <- 2000

函數(shù)參數(shù)解構(gòu)就可以解決這個(gè)問(wèn)題。

函數(shù)參數(shù)解構(gòu)

通過(guò)函數(shù)參數(shù)解構(gòu),可以解決上面的問(wèn)題,這里我們?yōu)槊恳粋€(gè)屬性都提供了默認(rèn)值,單獨(dú)改變其中一個(gè)并不會(huì)影響其它的值:

function carFactory({ brand = &#39;Volkswagen&#39;, make = 1999 }) {
  console.log(brand)
  console.log(make)
}
carFactory({ make: 2000 })
// <- &#39;Volkswagen&#39;
// <- 2000

不過(guò)這種情況下,函數(shù)調(diào)用時(shí),如果參數(shù)為空即carFactory()函數(shù)將拋出異常。這種問(wèn)題可以通過(guò)下面的方法來(lái)修復(fù),下述代碼中我們添加了一個(gè)空對(duì)象作為options的默認(rèn)值,這樣當(dāng)函數(shù)被調(diào)用時(shí),如果參數(shù)為空,會(huì)自動(dòng)以{}作為參數(shù)。

function carFactory({
  brand = &#39;Volkswagen&#39;,
  make = 1999
} = {}) {
  console.log(brand)
  console.log(make)
}
carFactory()
// <- &#39;Volkswagen&#39;
// <- 1999

除此之外,使用函數(shù)參數(shù)解構(gòu),還可以讓你的函數(shù)自行匹配對(duì)應(yīng)的參數(shù),看接下來(lái)的例子,你就能明白這一點(diǎn)了,我們定義一個(gè)名為car的對(duì)象,這個(gè)對(duì)象擁有很多屬性:owner,brand,make,model,preferences等等。

var car = {
  owner: {
    id: &#39;e2c3503a4181968c&#39;,
    name: &#39;Donald Draper&#39;
  },
  brand: &#39;Peugeot&#39;,
  make: 2015,
  model: &#39;208&#39;,
  preferences: {
    airbags: true,
    airconditioning: false,
    color: &#39;red&#39;
  }
}

解構(gòu)能讓我們的函數(shù)方便的只使用里面的部分?jǐn)?shù)據(jù),下面代碼中的函數(shù)getCarProductModel說(shuō)明了具體該如何使用:

var getCarProductModel = ({ brand, make, model }) => ({
  sku: brand + &#39;:&#39; + make + &#39;:&#39; + model,
  brand,
  make,
  model
})
getCarProductModel(car)

解構(gòu)使用示例

當(dāng)一個(gè)函數(shù)的返回值為對(duì)象或者數(shù)組時(shí),使用解構(gòu),我們可以非常簡(jiǎn)潔的獲取返回對(duì)象中某個(gè)屬性的值(返回?cái)?shù)組中某一項(xiàng)的值)。比如說(shuō),函數(shù)getCoordinates()返回了一系列的值,但是我們只想用其中的x,y,我們可以這樣寫(xiě),解構(gòu)幫助我們避免了很多中間變量的使用,也使得我們代碼的可讀性更高。

function getCoordinates() {
  return { x: 10, y: 22, z: -1, type: &#39;3d&#39; }
}
var { x, y } = getCoordinates()

通過(guò)使用默認(rèn)值,可以減少重復(fù),比如你想寫(xiě)一個(gè)random函數(shù),這個(gè)函數(shù)將返回一個(gè)位于minmax之間的值。我們可以分辨設(shè)置min默認(rèn)值為1,max默認(rèn)值為10,在需要的時(shí)候還可以單獨(dú)改變其中的某一個(gè)值:

function random({ min = 1, max = 10 } = {}) {
  return Math.floor(Math.random() * (max - min)) + min
}
console.log(random())// <- 7
console.log(random({ max: 24 }))// <- 18

解構(gòu)還可以配合正則表達(dá)式使用??聪旅孢@個(gè)例子:

function splitDate(date) {
  var rdate = /(\d+).(\d+).(\d+)/  
  return rdate.exec(date)
}
var [ , year, month, day] = splitDate(&#39;2015-11-06&#39;)

不過(guò)當(dāng).exec不比配時(shí)會(huì)返回null,因此我們需要修改上述代碼如下:

var matches = splitDate(&#39;2015-11-06&#39;)if (matches === null) {
  return
}
var [, year, month, day] = matches

下面我們繼續(xù)來(lái)講講spreadrest操作符。

剩余參數(shù)和拓展符

ES6之前,對(duì)于不確定數(shù)量參數(shù)的函數(shù)。你需要使用偽數(shù)組arguments,它擁有length屬性,卻又不具備很多一般數(shù)組有的特性。需要通過(guò)Array#slice.call轉(zhuǎn)換arguments對(duì)象真數(shù)組后才能進(jìn)行下一步的操作:

function join() {
  var list = Array.prototype.slice.call(arguments)  
  return list.join(&#39;, &#39;)
}
join(&#39;first&#39;, &#39;second&#39;, &#39;third&#39;)// <- &#39;first, second, third&#39;

對(duì)于這種情況,ES6提供了一種更好的解決方案:rest。

剩余參數(shù)rest

使用rest, 你只需要在任意JavaScript函數(shù)的最后一個(gè)參數(shù)前添加三個(gè)點(diǎn)...即可。當(dāng)rest參數(shù)是函數(shù)的唯一參數(shù)時(shí),它就代表了傳遞給這個(gè)函數(shù)的所有參數(shù)。它起到和前面說(shuō)的.slice一樣的作用,把參數(shù)轉(zhuǎn)換為了數(shù)組,不需要你再對(duì)arguments進(jìn)行額外的轉(zhuǎn)換了。

function join(...list) {
  return list.join(&#39;, &#39;)
}
join(&#39;first&#39;, &#39;second&#39;, &#39;third&#39;)// <- &#39;first, second, third&#39;

rest參數(shù)之前的命名參數(shù)不會(huì)被包含在rest中,

function join(separator, ...list) {
  return list.join(separator)
}
join(&#39;; &#39;, &#39;first&#39;, &#39;second&#39;, &#39;third&#39;)// <- &#39;first; second; third&#39;

在箭頭函數(shù)中使用rest參數(shù)時(shí),即使只有這一個(gè)參數(shù),也需要使用圓括號(hào)把它圍起來(lái),不然就會(huì)報(bào)錯(cuò)SyntaxError,使用示例如下:

var sumAll = (...numbers) => numbers.reduce(
  (total, next) => total + next
)
console.log(sumAll(1, 2, 5))// <- 8

上述代碼的ES5實(shí)現(xiàn)如下:

// ES5的寫(xiě)法
function sumAll() {
  var numbers = Array.prototype.slice.call(arguments)  
  return numbers.reduce(function (total, next) { 
     return total + next
  })
}
console.log(sumAll(1, 2, 5))// <- 8

拓展運(yùn)算符

拓展運(yùn)算符可以把任意可枚舉對(duì)象轉(zhuǎn)換為數(shù)組,使用拓展運(yùn)算符可以高效處理目標(biāo)對(duì)象,在拓展目前前添加...就可以使用拓展運(yùn)算符了。下例中...arguments就把函數(shù)的參數(shù)轉(zhuǎn)換為了數(shù)組字面量。

function cast() {
  return [...arguments]
}
cast(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)// <- [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]

使用拓展運(yùn)算符,我們也可以把字符串轉(zhuǎn)換為由每一個(gè)字母組成的數(shù)組:

[...&#39;show me&#39;]// <- [&#39;s&#39;, &#39;h&#39;, &#39;o&#39;, &#39;w&#39;, &#39; &#39;, &#39;m&#39;, &#39;e&#39;]

使用拓展運(yùn)算符,還可以拼合數(shù)組:

function cast() {
  return [&#39;left&#39;, ...arguments, &#39;right&#39;]
}
cast(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)// <- [&#39;left&#39;, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;right&#39;]
var all = [1, ...[2, 3], 4, ...[5], 6, 7]
console.log(all)// <- [1, 2, 3, 4, 5, 6, 7]

這里我還想再?gòu)?qiáng)調(diào)一下,拓展運(yùn)算符不僅僅適用于數(shù)組和arguments對(duì)象,對(duì)任意可迭代的對(duì)象都可以使用。迭代也是ES6新提出的一個(gè)概念,在[ Iteration and Flow Control]()這一章,我們將詳細(xì)敘述迭代。

Shifting和Spreading

當(dāng)你想要抽出一個(gè)數(shù)組的前一個(gè)或者兩個(gè)元素時(shí),常用的解決方案是使用.shift.盡管是函數(shù)式的,下述代碼在第一次看到的時(shí)候卻不好理解,我們使用了兩次.slicelist中抽離出兩個(gè)不同的元素。

var list = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;]
var first = list.shift()
var second = list.shift()
console.log(first)// <- &#39;a&#39;

在ES6中,結(jié)合使用拓展和解構(gòu),可以讓代碼的可讀性更好:

var [first, second, ...other] = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;]
console.log(other)// <- [&#39;c&#39;, &#39;d&#39;, &#39;e&#39;]

除了對(duì)數(shù)組進(jìn)行拓展,你同樣可以對(duì)函數(shù)參數(shù)使用拓展,下例展示了如何添加任意數(shù)量的參數(shù)到multiply函數(shù)中。

function multiply(left, right) {
  return left * right
}
var result = multiply(...[2, 3])
console.log(result)// <- 6

向在數(shù)組中一樣,函數(shù)參數(shù)中的拓展運(yùn)算符同樣可以結(jié)合常規(guī)參數(shù)一起使用。下例中,print函數(shù)結(jié)合使用了rest,普通參數(shù),和拓展運(yùn)算符:

function print(...list) {
  console.log(list)
}
print(1, ...[2, 3], 4, ...[5])// <- [1, 2, 3, 4, 5]

下表總結(jié)了,拓展運(yùn)算符的常見(jiàn)使用方法:

使用示例ES5ES6
Concatenation[1, 2].concat(more)[1, 2, ...more]
Push an array onto listlist.push.apply(list, items)list.push(...items)
Destructuringa = list[0], other = list.slice(1)<span class="Apple-tab-span" style="white-space: pre;"> </span>[a, ...other] = list
new and applynew (Date.bind.apply(Date, [null,2015,31,8]))new Date(...[2015,31,8])

模板字符串

模板字符串是對(duì)常規(guī)JavaScript字符串的重大改進(jìn),不同于在普通字符串中使用單引號(hào)或者雙引號(hào),模板字符串的聲明需要使用反撇號(hào),如下所示:

var text = `This is my first template literal`

因?yàn)槭褂玫氖欠雌蔡?hào),你可以在模板字符串中隨意使用單雙引號(hào)了,使用時(shí)不再需要考慮轉(zhuǎn)義,如下:

var text = `I&#39;m "amazed" at these opportunities!`

模板字符串具有很多強(qiáng)大的功能,可在其中插入JavaScript表達(dá)式就是其一。

在字符串中插值

通過(guò)模板字符串,你可以在模板中插入任何JavaScript表達(dá)式了。當(dāng)解析到表達(dá)式時(shí),表達(dá)式會(huì)被執(zhí)行,該處將渲染表達(dá)式的值,下例中,我們?cè)谧址胁迦肓俗兞?code>name:

var name = &#39;Shannon&#39;
var text = `Hello, ${ name }!`
console.log(text)// <- &#39;Hello, Shannon!&#39;

模板字符串是支持任何表達(dá)式的。使用模板字符串,代碼將更容易維護(hù),你無(wú)須再手動(dòng)連接字符串和JavaScript表達(dá)式了。

看下面插入日期的例子,是不是又直觀又方便:

`The time and date is ${ new Date().toLocaleString() }.`// <- &#39;the time and date is 8/26/2015, 3:15:20 PM&#39;

表達(dá)式中還可以包含數(shù)學(xué)運(yùn)算符:

`The result of 2+3 equals ${ 2 + 3 }`// <- &#39;The result of 2+3 equals 5&#39;

鑒于模板字符串本身也是JavaScript表達(dá)式,我們?cè)谀0遄址羞€可以嵌套模板字符串;

`This template literal ${ `is ${ &#39;nested&#39; }` }!`// <- &#39;This template literal is nested!&#39;

模板字符串的另外一個(gè)優(yōu)點(diǎn)是支持多行字符串;

多行文本模板

在ES6之前,如果你想表現(xiàn)多行字符串,你需要使用轉(zhuǎn)義,數(shù)組拼合,甚至使用使用注釋符做復(fù)雜的hacks.如下所示:

var escaped =&#39;The first line\n\
A second line\n\
Then a third line&#39;

var concatenated =&#39;The first line\n&#39; `
&#39;A second line\n&#39; `&#39;Then a third line&#39;

var joined = [&#39;The first line&#39;,&#39;A second line&#39;,&#39;Then a third line&#39;].join(&#39;\n&#39;)

應(yīng)用ES6,這種處理就簡(jiǎn)單多了,模板字符串默認(rèn)支持多行:

var multiline =`The first line
A second line
Then a third line`

當(dāng)你需要返回的字符串基于html和數(shù)據(jù)生成,使用模板字符串是很簡(jiǎn)潔高效的,如下所示:

var book = {
  title: &#39;Modular ES6&#39;,  
  excerpt: &#39;Here goes some properly sanitized HTML&#39;,  
  tags: [&#39;es6&#39;, &#39;template-literals&#39;, &#39;es6-in-depth&#39;]
}
var html = `<article>
  <header>
    <h1>${ book.title }</h1>
  </header>
  <section>${ book.excerpt }</section>
  <footer>
    <ul>      ${
        book.tags
          .map(tag => `<li>${ tag }</li>`)
          .join(&#39;\n      &#39;)
      }
    </ul>
  </footer>
</article>`

上述代碼將得到下面這樣的結(jié)果??崭竦靡员A簦鄠€(gè)li也按我們的預(yù)期被合適的渲染:

<article>
  <header>
      <h1>Modular ES6</h1>
   </header>
    <section>Here goes some properly sanitized HTML</section>  
    <footer>
              <ul>
                    <li>es6</li>      
                    <li>template-literals</li>      
                    <li>es6-in-depth</li>    
               </ul>  
       </footer>
</article>

不過(guò)有時(shí)候我們并不希望空格被保留,下例中我們?cè)诤瘮?shù)中使用包含縮進(jìn)的模板字符串,我們希望結(jié)果沒(méi)有縮進(jìn),但是實(shí)際的結(jié)果卻有四格的縮進(jìn)。

function getParagraph() {
  return `
    Dear Rod,

    This is a template literal string that&#39;s indented
    four spaces. However, you may have expected for it
    to be not indented at all.

    Nico
  `}

我們可以用下面這個(gè)功能函數(shù)對(duì)生成的字符串進(jìn)行處理已得到我們想要的結(jié)果:

function unindent(text) {
  return text
    .split(&#39;\n&#39;)
    .map(line => line.slice(4))
    .join(&#39;\n&#39;)
    .trim()
}

不過(guò),使用被稱為標(biāo)記模板的模板字符串新特性處理這種情況可能會(huì)更好。

標(biāo)記模板

默認(rèn)情況下,JavaScript會(huì)把\解析為轉(zhuǎn)義符號(hào),對(duì)瀏覽器來(lái)說(shuō),以\開(kāi)頭的字符一般具有特殊的含義。比如說(shuō)\n意味著新行,\u00f1表示?等等。如果你不想瀏覽器執(zhí)行這種特殊解析,你也可以使用String.raw來(lái)標(biāo)記模板。下面的代碼就是這樣做的,這里我們使用了String.row來(lái)處理模板字符串,相應(yīng)的這里面的\n沒(méi)有被解析為新行。

var text = String.raw`"\n" is taken literally.
It&#39;ll be escaped instead of interpreted.`
console.log(text)
// "\n" is taken literally.
// It&#39;ll be escaped instead of interpreted.

我們添加在模板字符串之前的String.raw前綴,這就是標(biāo)記模板,這樣的模板字符串在被渲染前被該標(biāo)記代表的函數(shù)預(yù)處理。

一個(gè)典型的標(biāo)記模板字符串如下:

tag`Hello, ${ name }. I am ${ emotion } to meet you!`

實(shí)際上,上面標(biāo)記模板可以用以下函數(shù)形式表示:

tag(
  [&#39;Hello, &#39;, &#39;. I am &#39;, &#39; to meet you!&#39;],  &#39;Maurice&#39;,  &#39;thrilled&#39;
 )

我們還是用代碼來(lái)說(shuō)明這個(gè)概念,下述代碼中,我們先定義一個(gè)名為tag函數(shù):

function tag(parts, ...values) {
  return parts.reduce(
      (all, part, index) => all + values[index - 1] + part
  )
}

然后我們調(diào)用使用使用標(biāo)記模板,不過(guò)此時(shí)的結(jié)果和不使用標(biāo)記模板是一樣的,這是因?yàn)槲覀兌x的tag函數(shù)實(shí)際上并未對(duì)字符串進(jìn)行額外的處理。

var name = &#39;Maurice&#39;
var emotion = &#39;thrilled&#39;
var text = tag`Hello, ${ name }. I am ${ emotion } to meet you!`
console.log(text)// <- &#39;Hello Maurice, I am thrilled to meet you!&#39;

我們看一個(gè)進(jìn)行額外處理的例子,比如轉(zhuǎn)換所有用戶輸入的值為大寫(xiě)(假設(shè)用戶只會(huì)輸入英語(yǔ)),這里我們定義標(biāo)記函數(shù)upper來(lái)做這件事:

function upper(parts, ...values) {
  return parts.reduce((all, part, index) =>
    all + values[index - 1].toUpperCase() + part
  )
}
var name = &#39;Maurice&#39;
var emotion = &#39;thrilled&#39;
upper`Hello, ${ name }. I am ${ emotion } to meet you!`
// <- &#39;Hello MAURICE, I am THRILLED to meet you!&#39;

既然可以轉(zhuǎn)換輸入為大寫(xiě),那我們?cè)龠M(jìn)一步想想,如果提供合適的標(biāo)記模板函數(shù),使用標(biāo)記模板,我們還可以對(duì)模板中的表達(dá)式進(jìn)行各種過(guò)濾處理,比如有這么一個(gè)場(chǎng)景,假設(shè)表達(dá)式的值都來(lái)自用戶輸入,假設(shè)有一個(gè)名為sanitize的庫(kù)可用于去除用戶輸入中的html標(biāo)簽,那通過(guò)使用標(biāo)記模板,就可以有效的防止XSS攻擊了,使用方法如下。

function sanitized(parts, ...values) {
  return parts.reduce((all, part, index) =>
    all + sanitize(values[index - 1]) + part
  )
}
var comment = &#39;Evil comment<iframe src="http://evil.corp">
    </iframe>&#39;
var html = sanitized`<div>${ comment }</div>`console.log(html)
// <- &#39;<div>Evil comment</div>&#39;

ES6中的另外一個(gè)大的改變是提供了新的變量聲明方式:letconst聲明,下面我們一起來(lái)學(xué)習(xí)。

let & const 聲明

可能很早之前你就聽(tīng)說(shuō)過(guò) let 了,它用起來(lái)像 var 但是,卻有不同的作用域規(guī)則。

JavaScript的作用域有一套復(fù)雜的規(guī)則,變量提升的存在常常讓新手忐忑不安。變量提升,意味著無(wú)論你在那里聲明的變量,在瀏覽器解析時(shí),實(shí)際上都被提升到了當(dāng)前作用域的頂部被聲明。看下面的這個(gè)例子:

function isItTwo(value) {
  if (value === 2) {
      var two = true
  }  return two
}
isItTwo(2)// <- true
isItTwo(&#39;two&#39;)// <- undefined

盡管two是在代碼分支中被聲明,之后被外部分支引用,上述的JS代碼還是可以工作的。var 聲明的變量two實(shí)際是在isItTwo頂部被聲明的。由于聲明提升的存在,上述代碼其實(shí)和下面代碼的效果是一樣的

function isItTwo(value) {
  var two  if (value === 2) {
    two = true
  }  return two
}

帶來(lái)了靈活性的同事,變量提升也帶來(lái)了更大的迷惑性,還好ES6 為我們提供了塊作用域。

塊作用域和let 聲明

相比函數(shù)作用域,塊作用域允許我們通過(guò)if,for,while聲明創(chuàng)建新作用域,甚至任意創(chuàng)建{}塊也能創(chuàng)建新的作用域:

{{{{{ var deep = &#39;This is available from outer scope.&#39;; }}}}}
console.log(deep)
// <- &#39;This is available from outer scope.&#39;

由于這里使用的是var,考慮到變量提升的存在,我們?cè)谕獠恳琅f可以讀取到深層中的deep變量,這里并不會(huì)報(bào)錯(cuò)。不過(guò)在以下情況下,我們可能希望這里會(huì)報(bào)錯(cuò):

  • 訪問(wèn)內(nèi)部變量會(huì)打破我們代碼中的某種封裝原則;

  • 父塊中已有有一個(gè)一個(gè)同名變量,但是內(nèi)部也需要用同名變量;

使用let就可以解決這個(gè)問(wèn)題,let 創(chuàng)建的變量在塊作用域內(nèi)有效,在ES6提出let以前,想要?jiǎng)?chuàng)建深層作用域的唯一辦法就是再新建一個(gè)函數(shù)。使用let,你只需添加另外一對(duì){}

let topmost = {}
{
  let inner = {}
  {
    let innermost = {}
  }
  // attempts to access innermost here would throw
}
// attempts to access inner here would throw
// attempts to access innermost here would throw

for循環(huán)中使用let是一個(gè)很好的實(shí)踐,這樣定義的變量只會(huì)在當(dāng)前塊作用域內(nèi)生效。

for (let i = 0; i < 2; i++) {
  console.log(i)  // <- 0  // <- 1
}
console.log(i)// <- i is not defined

考慮到let聲明的變量在每一次循環(huán)的過(guò)程中都重復(fù)聲明,這在處理異步函數(shù)時(shí)就很有效,不會(huì)發(fā)生使用var時(shí)產(chǎn)生的詭異的結(jié)果,我們看一個(gè)具體的例子。

我們先看看 var 聲明的變量是怎么工作的,下述代碼中 i變量 被綁定在 printNumber 函數(shù)作用域中,當(dāng)每個(gè)回調(diào)函數(shù)被調(diào)用時(shí),它的值會(huì)逐步升到10,但是當(dāng)每個(gè)回調(diào)函數(shù)運(yùn)行時(shí)(每100us),此時(shí)的i的值已經(jīng)是10了,因此每次打印的結(jié)果都是10.

function printNumbers() {
  for (var i = 0; i < 10; i++) {
      setTimeout(function () {
            console.log(i)
    }, i * 100)
  }
}
printNumbers()

使用let,則會(huì)把i綁定到每一個(gè)塊作用域中。每一次循環(huán) i 的值還是在增加,但是每次其實(shí)都是創(chuàng)建了一個(gè)新的 i ,不同的 i 之間不會(huì)相互影響 ,因此打印出的就是預(yù)想的0到9了。

function printNumbers() {
  for (let i = 0; i < 10; i++) {
      setTimeout(function () { 
           console.log(i)
    }, i * 100)
  }
}
printNumbers()

為了細(xì)致的講述let的工作原理, 我們還需要弄懂一個(gè)名為 Temporal Dead Zone 的概念。

Temporal Dead Zone

簡(jiǎn)言之,如果你的代碼類似下面這樣,就會(huì)報(bào)錯(cuò)。即在某個(gè)作用域中,在let聲明之前調(diào)用了let聲明的變量,導(dǎo)致的問(wèn)題就是由于,Temporal Dead Zone(TDZ)的存在。

{
  console.log(name)
  // <- ReferenceError: name is not defined
  let name = &#39;Stephen Hawking&#39;
}

如果定義的是一個(gè)函數(shù),函數(shù)中引用了name變量則是可以的,但是這個(gè)函數(shù)并未在聲明前執(zhí)行則不會(huì)報(bào)錯(cuò)。如果let聲明之前就調(diào)用了該函數(shù),同樣會(huì)導(dǎo)致TDZ。

// 不會(huì)報(bào)錯(cuò)
function readName() {
  return name
}
let name = &#39;Stephen Hawking&#39;
console.log(readName())
// <- &#39;Stephen Hawking&#39;
// 會(huì)報(bào)錯(cuò)
function readName() {
  return name
}
console.log(readName())
// ReferenceError: name is not defined
let name = &#39;Stephen Hawking&#39;

即使像下面這樣let定義的變量沒(méi)有被賦值,下面的代碼也會(huì)報(bào)錯(cuò),原因依舊是它試圖在聲明前訪問(wèn)一個(gè)被let定義的變量

function readName() {
  return name
}
console.log(readName())// ReferenceError: name is not definedlet name

下面的代碼則是可行的:

function readName() {
  return name
}
let nameconsole.log(readName())// <- undefined

TDZ的存在使得程序更容易報(bào)錯(cuò),由于聲明提升和不好的編碼習(xí)慣常常會(huì)存在這樣的問(wèn)題。在ES6中則可以比較好的避免了這種問(wèn)題了,需要注意的是let聲明的變量同樣存在聲明提升。這意味著,變量會(huì)在我們進(jìn)入塊作用域時(shí)就會(huì)創(chuàng)建,TDZ也是在這時(shí)候創(chuàng)建的,它保證該變量不許被訪問(wèn),只有在代碼運(yùn)行到let聲明所在位置時(shí),這時(shí)候TDZ才會(huì)消失,訪問(wèn)限制才會(huì)取消,變量才可以被訪問(wèn)。

Const 聲明

const聲明也具有類似let的塊作用域,它同樣具有TDZ機(jī)制。實(shí)際上,TDZ機(jī)制是因?yàn)?code>const才被創(chuàng)建,隨后才被應(yīng)用到let聲明中。const需要TDZ的原因是為了防止由于變量提升,在程序解析到const語(yǔ)句之前,對(duì)const聲明的變量進(jìn)行了賦值操作,這樣是有問(wèn)題的。

下面的代碼表明,const具有和let一致的塊作用域:

const pi = 3.1415{ 
 const pi = 6  console.log(pi)  // <- 6
}
console.log(pi)// <- 3.1415

下面我們說(shuō)說(shuō)constlet的主要區(qū)別,首先const聲明的變量在聲明時(shí)必須賦值,否則會(huì)報(bào)錯(cuò):

const pi = 3.1415
const e 
// SyntaxError, missing initializer

除了必須初始化,被const聲明的變量不能再被賦予別的值。在嚴(yán)格模式下,試圖改變const聲明的變量會(huì)直接報(bào)錯(cuò),在非嚴(yán)格模式下,改變被靜默被忽略。

const people = [&#39;Tesla&#39;, &#39;Musk&#39;]
people = []
console.log(people)// <- [&#39;Tesla&#39;, &#39;Musk&#39;]

請(qǐng)注意,const聲明的變量并非意味著,其對(duì)應(yīng)的值是不可變的。真正不能變的是對(duì)該值的引用,下面我們具體說(shuō)明這一點(diǎn)。

通過(guò)const聲明的變量值并非不可改變

使用const只是意味著,變量將始終指向相同的對(duì)象或初始的值。這種引用是不可變的。但是值并非不可變。

下面的例子說(shuō)明,雖然people的指向不可變,但是數(shù)組本身是可以被修改的。

const people = [&#39;Tesla&#39;, &#39;Musk&#39;]
people.push(&#39;Berners-Lee&#39;)
console.log(people)// <- [&#39;Tesla&#39;, &#39;Musk&#39;, &#39;Berners-Lee&#39;]

const只是阻止變量引用另外一個(gè)值,下例中,盡管我們使用const聲明了people,然后把它賦值給了humans,我們還是可以改變humans的指向,因?yàn)?code>humans不是由const聲明的,其引用可隨意改變。people 是由 const 聲明的,則不可改變。

const people = [&#39;Tesla&#39;, &#39;Musk&#39;]
var humans = people
humans = &#39;evil&#39;
console.log(humans)// <- &#39;evil&#39;

如果我們的目的是讓值不可修改,我們需要借助函數(shù)的幫助,比如使用Object.freeze

const frozen = Object.freeze(
  [&#39;Ice&#39;, &#39;Icicle&#39;, &#39;Ice cube&#39;]
)
frozen.push(&#39;Water&#39;)
// Uncaught TypeError: Can&#39;t add property 3
// object is not extensible

下面我們?cè)敿?xì)討論一下constlet的優(yōu)點(diǎn)

constlet的優(yōu)點(diǎn)

新功能并不應(yīng)該因?yàn)槭切鹿δ芏皇褂茫珽S6語(yǔ)法被使用的前提是它可以顯著的提升我們代碼的可讀寫(xiě)和可維護(hù)性。let聲明在大多數(shù)情況下,可以替換var以避免預(yù)期之外的問(wèn)題。使用let你可以把聲明在塊的頂部進(jìn)行而非函數(shù)的頂部進(jìn)行。

有時(shí),我們希望有些變量的引用不可變,這時(shí)候使用const就能防止很多問(wèn)題的發(fā)生。下述代碼中 在checklist函數(shù)外給items變量傳遞引用時(shí)就非常容易出錯(cuò),它返回的todo API和items有了交互。當(dāng)items變量被改為指向另外一個(gè)列表時(shí),我們的代碼就出問(wèn)題了。todo API 用的還是items之前的值,items本身的指代則已經(jīng)改變。

var items = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]
var todo = checklist(items)
todo.check()
console.log(items)
// <- [&#39;b&#39;, &#39;c&#39;]
items = [&#39;d&#39;, &#39;e&#39;]
todo.check()
console.log(items)
// <- [&#39;d&#39;, &#39;e&#39;], would be [&#39;c&#39;] if items had been constant
function checklist(items) {
  return {
    check: () => items.shift()
  }
}

這類問(wèn)題很難debug,找到問(wèn)題原因就會(huì)花費(fèi)你很長(zhǎng)一段時(shí)間。使用const運(yùn)行時(shí)就會(huì)報(bào)錯(cuò),可以幫助你可以避免這種問(wèn)題。

如果我們默認(rèn)只使用cosntlet聲明變量,所有的變量都會(huì)有一樣的作用域規(guī)則,這讓代碼更易理解,由于const造成的影響最小,它還曾被提議作為默認(rèn)的變量聲明。

總的來(lái)說(shuō),const不允許重新指定值,使用的是塊作用域,存在TDZ。let則允許重新指定值,其它方面和const類似,而var聲明使用函數(shù)作用域,可以重新指定值,可以在未聲明前調(diào)用,考慮到這些,推薦盡量不要使用var聲明了。

【相關(guān)推薦:javascript視頻教程、編程視頻

? ??? es6?? ?? ?? ??? ?????? ?? ?????. ??? ??? PHP ??? ????? ?? ?? ??? ?????!

? ????? ??
? ?? ??? ????? ???? ??? ??????, ???? ?????? ????. ? ???? ?? ???? ?? ??? ?? ????. ???? ??? ???? ???? ??? ?? admin@php.cn?? ?????.

? AI ??

Undresser.AI Undress

Undresser.AI Undress

???? ?? ??? ??? ?? AI ?? ?

AI Clothes Remover

AI Clothes Remover

???? ?? ???? ??? AI ?????.

Video Face Swap

Video Face Swap

??? ??? AI ?? ?? ??? ???? ?? ???? ??? ?? ????!

???

??? ??

???++7.3.1

???++7.3.1

???? ?? ?? ?? ???

SublimeText3 ??? ??

SublimeText3 ??? ??

??? ??, ???? ?? ????.

???? 13.0.1 ???

???? 13.0.1 ???

??? PHP ?? ?? ??

???? CS6

???? CS6

??? ? ?? ??

SublimeText3 Mac ??

SublimeText3 Mac ??

? ??? ?? ?? ?????(SublimeText3)

???

??? ??

??? ????
1601
29
PHP ????
1502
276
???
ES6?? ??? ??? ?? ES6?? ??? ??? ?? Oct 26, 2022 pm 06:19 PM

ES6??? ?? ??? reverse() ???? ???? ?? ??? ??? ? ????. ? ???? ??? ?? ??? ??? ???? ??? ??? ?? ???? ? ?? ??? ???? ???? ? ?????. .???()". reverse() ???? ?? ??? ?????. ???? ???? ?? ??? "..."? ?? ???? ?? ??? "[...array].reverse()???. ".

es6 ?? es7? ???? ????? es6 ?? es7? ???? ????? Jan 29, 2023 pm 05:36 PM

???? es7???. async ? wait? ES7? ?? ??? ???? ??? ??? ?? ??????. async/await? ?? ?? ? ??? ??? ?? ?? ????? ? ? ???, ? ??? ??? js ??? ??? ?????. ???? ? ? ??? async? "???"? ?????. async? async? wait ??? ??? ??? ??? ???? ? ????, wait? ??? ???? ??? ? ????.

?? ????? es6? es5? ???? ?? ??? ?????? ?? ????? es6? es5? ???? ?? ??? ?????? Nov 21, 2022 pm 06:15 PM

???? ???? ??. JS? ??? ???? ES6?? ??? ??? API? ?? ???????. ??? ?? ????? ES6? ??? ??? ? ???? ???? ES6 ??? ES5 ??? ???? ???. WeChat ? ??? ????? ???? ES6 ?? ??? ? ??? ???? ? ???? ES5 ??? ???? ? ????? babel? ?????. ?? ?? ???? ??????? ?? ?? ???? ?? ???? ?? ??? ??? ? ????. "ES6?? ES5?" ??.

es6? ? ???? ?? ?? ??? ?? ?? es6? ? ???? ?? ?? ??? ?? ?? Nov 01, 2022 pm 06:07 PM

??: 1. "newA=new Set(a);newB=new Set(b);" ??? ???? ? ??? ?? ?? ???? ?????. 2. has() ? filter()? ???? ??? ??? ????. , " new Set([...newA].filter(x =>!newB.has(x)))" ??? ???? ??? ?? ??? ?? ???? ???? ?????. 3. ??? ?????. from ??? ?? ???? ????? ?? "Array.from(collection)"? ?????.

es5 ? es6?? ?? ?? ??? ???? ?? es5 ? es6?? ?? ?? ??? ???? ?? Jan 16, 2023 pm 05:09 PM

es5??? for ?? indexOf() ??? ???? ?? ?? ??? ??? ? ????. ?? "for(i=0;i<array length;i++){a=newArr.indexOf(arr[i]);if( a== -1){...}}". es6??? ???? ??? Array.from() ? Set? ???? ??? ??? ? ????. ?? ??? Set ??? ???? ??? ??? ?? ???? ??? ?? Array.from() ??? ???? ???? ???. Set ??? ?? ??? ?????.

es6 ?? ???? ??? ?????? es6 ?? ???? ??? ?????? Jan 03, 2023 pm 03:56 PM

es6?? ?? ???? ??? ?? ??? ??? let ? const ??? ???? ?? ?????. ?? ?? ??? let/const ??? ???? ??? ???? ??? ?? ??? ??? ? ??? ??? ???? ??? ??? "?? ?"? ????. ?? ????? "?? ?? ?"??? ???. ES6??? ?? ???? let ? const ??? ?? ??? ???? ??? ???? ????. ?? ?? ??? ??? ??? ??? ???? ?? ??? ???? ??? ?? ??? ???? ?? ???? ?? ????.

es6 ??? ?????? es6 ??? ?????? Oct 21, 2022 pm 04:09 PM

???, require? CommonJS ??? ??? ???? es6 ??? ??? ??? import???. require? ???? ???? import? ??? ??? ?????. require? ??? ?? ??? ??? ? ???, import? ?? ???? ??? ? ??? ????? ?? ?? ????? ??? ? ????. require? ???? ??? ????? ????. ???? ??? ?? ??? ??? ??? ?? ? ????.

es6? ??? ?? ?? ???? ?? es6? ??? ?? ?? ???? ?? Jan 18, 2023 pm 07:22 PM

ES6??? ?? ??? ?? ??? ???? ??? ?? ?? ?? ??? ? ????. ?, ??? ?? ?? ???? ? ? ??? ??? ?? ?? ??? ? ????. "array.length" ?? ?????. ?? ??? ?? ?, ? ?? ?? ?????.

See all articles