ES6部分總結(jié)

var、let、const 比較

var 會(huì)有聲明提升,并初始化為 undefined

console.log(a)  //  undefined
var a = 1
console.log(a)  //  1

console.log(b)  //  Uncaught ReferenceError: b is not defined
b = 3
console.log(b)  //  3
console.log(window.b)  //  3

疑問:

  • 為什么打印 b 會(huì)報(bào)錯(cuò)?
  • 為什么 window.bb 結(jié)果是一樣的?

因?yàn)?var 聲明有會(huì)一個(gè)提升。因?yàn)樽兞?b 沒有帶聲明的關(guān)鍵字,在非嚴(yán)格模式下 b 就會(huì)被當(dāng)作是全局變量,全局變量又會(huì)自動(dòng)加到全局對(duì)象屬性里,瀏覽器里的全局對(duì)象就是 window,所以才可以用 window.b 調(diào)用。關(guān)于 var 聲明提升可以看下面代碼:

var a = 'undefined'
console.log(a)  //  undefined
a = 1
console.log(a)  //  1

let 有聲明提升,但是不會(huì)初始化為 undefined ,而是保存在暫存區(qū)(TDZ)里。

let a = 'global'
{
  console.log(a)  //  Uncaught ReferenceError: a is not defined
  let a = 1
}

為什么不是打印出 'global',而是報(bào)錯(cuò)了?如果把 let a = 1 這個(gè)語句去掉,是可以正常打印出 'global' 的,所以代碼應(yīng)該是這樣的:

let a = 'global'
{
  let a  //  這時(shí)候,變量 a 進(jìn)了暫存區(qū),除非 `let a = 1` 執(zhí)行完,它才會(huì)出暫存區(qū),才能被調(diào)用。
  console.log(a)  //  Uncaught ReferenceError: a is not defined
  a = 1
}

let 是不允許重復(fù)聲明的,是有塊級(jí)作用域的。

//  不允許重復(fù)聲明
let a = 1
let a = 2  //  Uncaught SyntaxError: Identifier 'a' has already been declared

//  沒有塊級(jí)作用域
var x = 1
{
  var x = 2
}
console.log(x)  //  2

//  有塊級(jí)作用域
let x = 1
{
  let x = 2
}
console.log(x)  //  1

const 是常量聲明,聲明時(shí)要指定初始值,也有塊級(jí)作用域。

const c  //  Uncaught SyntaxError: Missing initializer in const declaration

const c = 1
{
  const c = 2
}
console.log(c)  //  輸出1,而且不會(huì)報(bào)錯(cuò)

c = 2  //  Uncaught TypeError: Assignment to constant variable.

最佳實(shí)踐

  • 常量用 const,能用 const 就不要用 let。
  • 能用 let 就不要用 var。

箭頭函數(shù)

一種語法糖:
語法糖(Syntactic sugar),也譯為糖衣語法,是由英國(guó)計(jì)算機(jī)科學(xué)家彼得·蘭丁發(fā)明的一個(gè)術(shù)語,指計(jì)算機(jī)語言中添加的某種語法,這種語法對(duì)語言的功能沒有影響,但是更方便程序員使用。語法糖讓程序更加簡(jiǎn)潔,有更高的可讀性。(概念來自維基百科)

ES6 允許使用“箭頭”(=>)定義函數(shù)。

let f = v => v
//  等同于
let f = function(v) {
  return v
}

let f = () => 5
//  等同于
let f = function () { return 5 }

let sum = (num1, num2) => num1 + num2
//  等同于
let sum = function(num1, num2) {
  return num1 + num2
}

//  這個(gè)涉及到與變量解構(gòu)的使用,后面講。
const full = ({ first, last }) => first + ' ' + last;

//  等同于
function full(person) {
  return person.first + ' ' + person.last;
}

部分語法:

  • 參數(shù)列表右括號(hào)要和箭頭在同一行上。
  • 單行箭頭函數(shù),函數(shù)體只能有一個(gè)語句。
  • 若箭頭函數(shù)返回一個(gè)對(duì)象字面量,要用括號(hào)括起來。

特性:
this 指向是固定的,不可改的,將函數(shù)內(nèi)部的 this 延伸到上一層作用域。

//  ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

//  使用 Bable 轉(zhuǎn)為 ES5 的代碼如下:
'use strict';

function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
  //  也就是說箭頭函數(shù)的 `this` 指向的是 foo() 函數(shù)體里的 `this`,而不是匿名函數(shù)的 `this`
}
function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭頭函數(shù)
  setInterval(() => this.s1++, 1000);
  // 普通函數(shù)
  setInterval(function () {
    this.s2++;
  }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);  //  s1: 3
setTimeout(() => console.log('s2: ', timer.s2), 3100);  //  s2: 0
setTimeout(() => console.log(s2), 3300);  //  NaN

為什么 console.log(s2) 沒有報(bào)錯(cuò),而是 NaN ??這是因?yàn)樵诘诙€(gè)定時(shí)器里頭,回調(diào)函數(shù)是一個(gè)匿名函數(shù)嘛,所以它的 this 指向的是全局對(duì)象(windows),所以 this.s2++; 就等同于 window.s2++; 也等同于全局變量 s2++ ,s2 初始值應(yīng)該是 undefined,所以 s2++ 就變成 NaN

模板字符串

模板字符串(template string)是增強(qiáng)版的字符串,用反引號(hào)(`)標(biāo)識(shí)。它可以當(dāng)作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量、調(diào)用函數(shù)等。

以前如果寫 html 可能要用到很多字符串拼接。

var str =
  'There are <b>' + basket.count + '</b> ' +
  'items in your basket, ' +
  '<em>' + basket.onSale +
  '</em> are on sale!'

現(xiàn)在利用模板字符串就可以大大簡(jiǎn)化工作了。

var str =`
  There are <b> ${basket.count} </b> items in your basket,  
  <em> ${basket.onSale} </em> are on sale!
  `

變量的解構(gòu)賦值

實(shí)際場(chǎng)景:我請(qǐng)求接口,然后返回的是一個(gè)對(duì)象。

//  假設(shè)返回的就是這個(gè) data
data = {
    name: 'tao',
    age: 20,
    sex: '男'
}

//  我要拿 data 里頭的屬性,以前我可能這樣寫:
let str = `
    <span>姓名:${data.name}</span>
    <span>年齡:${data.age}</span>
    <span>性別:${data.sex}</span>
`
//  這時(shí)候,寫接口的很坑,假設(shè)他給我那個(gè) age 是個(gè)空值,沒有初始值的。這時(shí)候我沒做檢測(cè)的話頁面就崩了。
//  利用對(duì)象解構(gòu)可以這樣寫:
let {name, age, sex} = data
let str = `
    <span>姓名:${name}</span>
    <span>年齡:${age}</span>
    <span>性別:${sex}</span>
`
//  這時(shí)候是不是簡(jiǎn)化了,還有就是沒有值,頁面也不會(huì)崩了,因?yàn)槎冀o初始值為 undefined

//  還有一個(gè)場(chǎng)景就是,楚育把 name 用爛了,用了很多次,那如果我還用 name 的話,就沖突了。所以可以下面這樣

let { name: className } = { name: '15軟2', bar: "bbb" }
console.log(className) // '15軟2'

... 操作符

展開運(yùn)算符(用三個(gè)連續(xù)的點(diǎn) ( ... ) 表示)是 ES6 中的新概念,使你能夠?qū)?字面量對(duì)象 展開為多個(gè)元素。

const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];

const produce = [...fruits,...vegetables];

console.log(produce);  //  [ 'apples', 'bananas', 'pears', 'corn', 'potatoes', 'carrots' ]

//  在之前的話,結(jié)合數(shù)組可能要用到 concat 方法

剩余參數(shù)也用三個(gè)連續(xù)的點(diǎn) ( ... ) 表示,使你能夠?qū)⒉欢〝?shù)量的元素表示為數(shù)組。

function sum(...nums) {
  let total = 0;  
  for(const num of nums) {
    total += num;
  }
  return total;
}

sum(10, 36, 7, 84, 90, 110);  //  337

//  其中 for...of 是一個(gè)新的循環(huán)形式,該循環(huán)將只循環(huán)訪問對(duì)象中的值。
const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const number of numbers) {
  console.log(number);
}
//  依次打印出 0 到 9

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

function say(message = 'hello') {
  console.log(message)
}

say()  //  hello
say('你好')  //  你好
//  這種用法我記得 PHP 也有,挺方便的。

把默認(rèn)參數(shù)和解構(gòu)結(jié)合起來

function quadrature([sum1 = 2, sum2 = 4]){
  console.log(sum1 * sum2)
}
quadrature([])  //  8
quadrature([4])  //  16
quadrature([, 5])  //  10
quadrature([3, 4])  // 12

//  這種寫法有個(gè)不好的地方就是如果不傳參數(shù)就會(huì)報(bào)錯(cuò)
quadrature()  //  Uncaught TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined

//  改成這樣就不會(huì)了
function quadrature([sum1 = 2, sum2 = 2] = []){
  console.log(sum1 * sum2)
}
function quadrature({sum1 = 2, sum2 = 2, arrs = ['hello', '你好']} = {}){
  
  console.log(sum1 * sum2)

  for (const arr of arrs){
    console.log(arr)
  }
  
}
quadrature()  //  4  hello  你好

對(duì)象解構(gòu)與數(shù)組解構(gòu)的區(qū)別

使用對(duì)象解構(gòu),它是根據(jù)鍵值對(duì)去配對(duì),而數(shù)組解構(gòu),它是基于位置的。個(gè)人建議用對(duì)象解構(gòu)比較好。

Class 類

關(guān)鍵字 class constructor() static extends super

es6-class.png

通過對(duì)比,我們可以看出:
Event 類里頭的構(gòu)造函數(shù) constructor() 好像就是右邊的 function Event(){}

prototype.png

其實(shí)我們?cè)?new Event() 的時(shí)候,它就是調(diào)用了 constructor() 然后它會(huì)返回一個(gè)對(duì)象,而這個(gè)對(duì)象它的 __proto__ 會(huì)指向 Event.prototpye。

然后我們?cè)谡{(diào)用 event.on(),因?yàn)樗旧硎菦]有這個(gè)方法的,所以會(huì)去 event.__proto__ 指向的 Event.prototpye 去找,如果 Event.prototpye 也沒有,會(huì)繼續(xù)根據(jù) Event.__proto__ 指向的 Function.prototype 里頭找,會(huì)根據(jù)原型鏈一直到找下去,直到 __proto__ 指向 null 這時(shí)會(huì)報(bào)錯(cuò)說不存在該方法。

繼承

//  stream.js

const Writable = require('stream').Writable; 
const util = require('util');

module.exports = class MyStream extends Writable {
    constructor(matchText, options) {
        super();
        this.count = 0;
        this.matcher = new RegExp(matchText, 'ig');
    }

    _write(chunk, encoding, cb) {
        let matches = chunk.toString().match(this.matcher);
        if (matches) {
            this.count += matches.length;
        }
        if (cb) {
            cb();
        }
    }

    end() {
        this.emit('total', this.count);
    }
}

我們可以看到,只是簡(jiǎn)單的用關(guān)鍵字 extends,然后里面有一點(diǎn)要注意的是,你用子類的構(gòu)造器時(shí),要先調(diào)用 super(),不然會(huì)報(bào)錯(cuò)。然后還有其他用法,我自己也沒怎么試,我就不細(xì)寫,其實(shí)我是想早點(diǎn)回宿舍。。。

這只是一部分代碼,整份代碼可以看這個(gè) node.js-demo

ES5 的繼承可以看我之前寫的 JavaScript類的繼承

ES6 Module

不打算細(xì)講,因?yàn)闉g覽器支持不是很好,如果我們團(tuán)隊(duì)以后用 Webpack 等打包工具了,那沒問題,直接通過 Babel 打包成兼容 ES5 的文件,那就比較方便。但是現(xiàn)在我們還在用。。

在此之前市面上的模塊實(shí)現(xiàn)的規(guī)范有這么幾種,CommonJS、AMD、CMD、UMD。像 Node.js 就是用 CommonJS 那一套,RequireJS 就是 AMD,CMD 的話有 Sea.js 等等,你們可以去了解這些關(guān)于模塊的規(guī)范?,F(xiàn)在 ES6 也有了自己的模塊。

好,接下來。講我們的主角

關(guān)于模塊有兩個(gè)關(guān)鍵字:import、export

接觸過編程的人,一看就大概知道這兩個(gè)干嘛用的吧。import 肯定就是導(dǎo)入東西,就好像 Java 的導(dǎo)入包也是用這個(gè)關(guān)鍵字嘛。那 export 應(yīng)該就是導(dǎo)出,輸出的意思吧。

//  sum.js
export default (a, b) => console.log(a + b)

//  寫成這樣也沒問題
function sum (a, b) {
  console.log(a + b)
}

export {sum as default}

//  為什么會(huì)多一個(gè) default ,不這樣的話,我導(dǎo)入的時(shí)候變量名要跟導(dǎo)出的變量對(duì)應(yīng)得上。
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Module Demo</title>
</head>
<body>
  <h1> ES6 </h1>
</body>
<script type="module">
  import fn from './sum.js'
  fn(5, 6)  //  11
</script>
</html>

注意

  • 導(dǎo)入的時(shí)候要寫文件后綴名,不像 Node.js 的 require 方法一樣,js 文件可以不加后綴。
  • 要以'/''./'、'../'開頭,反正現(xiàn)在還不支持直接用 sum.js。
  • 會(huì)有 CORS 檢查,也就是說會(huì)有跨域問題,比如你直接右鍵文件用瀏覽器打開肯定是報(bào)錯(cuò)的,你要放服務(wù)器里頭,用本地主機(jī)去訪問。
  • 記得腳本標(biāo)簽要加上 type="module"。

可以參考這些文檔

題外話

如果對(duì)于作用域,聲明前置,this 指向不怎么清楚的,可以看工作室書架里的書 《你不知道的JavaScript 上卷》。然后更多關(guān)于 ES6 的新特性可以看下面參考資料的鏈接。

參考資料

最后編輯于
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 一、ES6簡(jiǎn)介 ? 歷時(shí)將近6年的時(shí)間來制定的新 ECMAScript 標(biāo)準(zhǔn) ECMAScript 6(亦稱 ...
    一歲一枯榮_閱讀 6,208評(píng)論 8 25
  • 函數(shù)參數(shù)的默認(rèn)值 基本用法 在ES6之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值,只能采用變通的方法。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,703評(píng)論 0 1
  • 三,字符串?dāng)U展 3.1 Unicode表示法 ES6 做出了改進(jìn),只要將碼點(diǎn)放入大括號(hào),就能正確解讀該字符。有了這...
    eastbaby閱讀 1,667評(píng)論 0 8
  • 雙十一將至,相信不少的同學(xué)已經(jīng)把購(gòu)物車填的滿滿的。隨著每年雙十一各種電商的促銷,雙十一“購(gòu)物節(jié)”似乎已經(jīng)成為了一...
    喵先生如是說閱讀 463評(píng)論 0 3
  • 前兩天翻出以前買的6部星戰(zhàn)的碟片,可惜在電腦里都已經(jīng)讀不出來,足見它們被封存了多久。時(shí)隔這么長(zhǎng)時(shí)間出來的續(xù)作,讓所...
    智說星語閱讀 400評(píng)論 0 0

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