ES6小結

原文連接javascript

es6中有不少特性,使javascript語法更加豐滿,總結一波經常使用的es6知識點。java

1.變量聲明const和let

在ES6以前,咱們都是用var關鍵字聲明變量。不管聲明在何處,都會被視爲聲明在函數的最頂部(不在函數內即在全局做用域的最頂部)。這就是函數變量提高例如:es6

function a() {
    if(bool) {
        var str // 變量提高
        str = 'frontend'
    } else {
        //此處訪問str 值爲undefined
        console.log(str)
    }
}

因此不用關心bool是否爲true or false。實際上,不管如何str都會被建立聲明。
而es6以後,咱們一般用let和const來聲明。let表示變量、const表示常量,let和const都是塊級做用域。如何理解這個塊級做用域?面試

  • 在一個函數內部
  • 在一個代碼塊內部
一般來講{}大括號內的代碼塊即爲let 和 const的做用域。
function a() {
    if(bool) {
       let str = 'frontend
    } else {
        //str 在此處訪問不到
        console.log(str)
    }
  }

let 的做用域是在它所在當前代碼塊,但不會被提高到當前函數的最頂部。
const 聲明的變量都會被認爲是常量,表示它的值被設置完成後就不能再修改了。編程

const role = 'frontend'
  role = 'backend' //再次賦值此時會報錯

若是const的是一個對象,對象所包含的值是能夠被修改的。就是對象所指向的地址沒有變就行。json

const student = { name: 'cc' }
    // 不報錯
    student.name = 'yy'
    // 若是這樣子就會報錯了
    student  = { name: 'yy' }

常見面試題中:api

var funcs = []
    for (var i = 0; i < 10; i++) {
        funcs.push(function() { console.log(i) })
    }
    funcs.forEach(function(func) {
        func()
    })

這樣的面試題是你們常見,不少同窗一看就知道輸出 10 十次
可是若是咱們想依次輸出0到9呢?
有兩種解決方法。直接看一下代碼。數組

// ES5利用閉包解決這個問題
    var funcs = []
    for (var i = 0; i < 10; i++) {
        funcs.push(
          (function(value) {
            return function() {
                console.log(value)
            }
        })(i)
      )
    }
    funcs.forEach(function(func) {
        func()
    })
// es6解決
    const funcs = []
    for (let i = 0; i < 10; i++) {
        funcs.push(function() {
            console.log(i)
        })
    }
    funcs.forEach(func => func())

es6解決方案更加簡潔方便。promise

2.模板字符串

es6模板字符簡直是開發者的福音,解決了es5在字符串功能上的痛點。
第一個用途,基本的字符串格式化。將表達式嵌入字符串中進行拼接。用${}來界定。數據結構

//ES5 
    var name = 'frontend'
    console.log('hello' + name)
    //es6
    const name = 'frontend'
    console.log(`hello ${name}`)

第二個用途,在ES5時咱們經過反斜槓()來作多行字符串或者字符串一行行拼接。ES6反引號(``)直接搞定。

// ES5
    var msg = "Hi \
    man!
    "
    // ES6
    const template = `<div>
        <span>hello world</span>
    </div>`

對於字符串ES6固然也提供了不少厲害也頗有意思的方法。

// 1.includes:判斷是否包含而後直接返回布爾值
    const str = 'hahay'
    console.log(str.includes('y')) // true

    // 2.repeat: 獲取字符串重複n次
    const str = 'he'
    console.log(str.repeat(3)) // 'hehehe'
    //若是你帶入小數, Math.floor(num) 來處理
    // s.repeat(3.1) 或者 s.repeat(3.9) 都當作成 s.repeat(3) 來處理

    // 3. startsWith 和 endsWith 判斷是否以 給定文本 開始或者結束
    const str =  'hello world!'
    console.log(str.startsWith('hello')) // true
    console.log(str.endsWith('!')) // true

3.函數

函數默認參數
在ES5咱們給函數定義參數默認值是怎麼樣?

function action(num) {
        num = num || 200
        //當傳入num時,num爲傳入的值
        //當沒傳入參數時,num即有了默認值200
        return num
    }

但細心觀察的同窗們確定會發現,num傳入爲0的時候就是false,可是咱們實際的需求就是要拿到num = 0,此時num = 200 明顯與咱們的實際想要的效果明顯不同。
ES6爲參數提供了默認值。在定義函數時便初始化了這個參數,以便在參數沒有被傳遞進去時使用。

function action(num = 200) {
        console.log(num)
    }
    action(0) // 0
    action() //200
    action(300) //300

箭頭函數

ES6頗有意思的一部分就是函數的快捷寫法。也就是箭頭函數。
箭頭函數最直觀的三個特色。

  • 不須要 function 關鍵字來建立函數
  • 省略 return 關鍵字
  • 繼承當前上下文的 this 關鍵字
//例如:
    [1,2,3].map(x => x + 1)
    
    //等同於:
    [1,2,3].map((function(x){
        return x + 1
    }).bind(this))

說個小細節。
當你的函數有且僅有一個參數的時候,是能夠省略掉括號的。當你函數返回有且僅有一個表達式的時候能夠省略{} 和 return;例如:

var people = name => 'hello' + name
  //參數name就沒有括號

做爲參考

var people = (name, age) => {
        const fullName = 'hello' + name
        return fullName
    } 
    //若是缺乏()或者{}就會報錯

來道筆試題:

// 請使用ES6重構如下代碼 
    var calculate = function(x, y, z) {
      if (typeof x != 'number') { x = 0 }
      if (typeof y != 'number') { y = 6 }

      var dwt = x % y
      var result

      if (dwt == z) { result = true }
      if (dwt != z) { result = false }
      
      return result
    }
const calculate = (x, y, z) => {
      x = typeof x !== 'number' ? 0 : x
      y = typeof y !== 'number' ? 6 : y
      return x % y === z
    }

4.拓展的對象功能

對象初始化簡寫

ES5咱們對於對象都是以鍵值對的形式書寫,是有可能出現鍵值對重名的。例如:

function people(name, age) {
        return {
            name: name,
            age: age
        };
    }

鍵值對重名,ES6能夠簡寫以下:

function people(name, age) {
        return {
            name,
            age
        };
    }

ES6 一樣改進了爲對象字面量方法賦值的語法。ES5爲對象添加方法:

const people = {
        name: 'sa',
        getName: function() {
            console.log(this.name)
        }
    }

ES6經過省略冒號與 function 關鍵字,將這個語法變得更簡潔

const people = {
        name: 'sa',
        getName () {
            console.log(this.name)
        }
    }

ES6 對象提供了 Object.assign() 這個方法來實現淺複製。
Object.assign() 能夠把任意多個源對象自身可枚舉的屬性拷貝給目標對象,而後返回目標對象。第一參數即爲目標對象。在實際項目中,咱們爲了避免改變源對象。通常會把目標對象傳爲{}

const objA = { name: 'cc', age: 18 }
    const objB = { address: 'beijing' }
    const objC = {} // 這個爲目標對象
    const obj = Object.assign(objC, objA, objB)

    // 咱們將 objA objB objC obj 分別輸出看看
    console.log(objA)   // { name: 'cc', age: 18 }
    console.log(objB) // { address: 'beijing' }
    console.log(objC) // { name: 'cc', age: 18, address: 'beijing' }
    console.log(obj) // { name: 'cc', age: 18, address: 'beijing' }

    // 是的,目標對象ObjC的值被改變了。
    // so,若是objC也是你的一個源對象的話。請在objC前面填在一個目標對象{}
    Object.assign({}, objC, objA, objB)

5.更方便的數據訪問--解構

數組和對象是JS中最經常使用也是最重要表示形式。爲了簡化提取信息,ES6新增瞭解構,這是將一個數據結構分解爲更小的部分的過程

ES5咱們提取對象中的信息形式以下:

const people = {
        name: 'lux',
        age: 20
    }
    const name = people.name
    const age = people.age
    console.log(name + ' --- ' + age)

是否是以爲很熟悉,沒錯,在ES6以前咱們就是這樣獲取對象信息的,一個一個獲取。如今,解構能讓咱們從對象或者數組裏取出數據存爲變量,例如

//對象
    const people = {
        name: 'sa',
        age: 20
    }
    const { name, age } = people
    console.log(`${name} --- ${age}`)
    //數組
    const color = ['red', 'blue']
    const [first, second] = color
    console.log(first) //'red'
    console.log(second) //'blue'

面試題:

// 請使用 ES6 重構一下代碼

    // 第一題
    var jsonParse = require('body-parser').jsonParse

    // 第二題
    var body = request.body
    var username = body.username
    var password = body.password
// 1.
    import { jsonParse } from 'body-parser'
    // 2. 
    const { body, body: { username, password } } = request

6.Spread Operator 展開運算符

ES6中另一個好玩的特性就是Spread Operator 也是三個點兒...接下來就展現一下它的用途。

組裝對象或者數組

//數組
    const color = ['red', 'yellow']
    const colorful = [...color, 'green', 'pink']
    console.log(colorful) //[red, yellow, green, pink]
    
    //對象
    const alp = { fist: 'a', second: 'b'}
    const alphabets = { ...alp, third: 'c' }
    console.log(alphabets) //{ "fist": "a", "second": "b", "third": "c" }

有時候咱們想獲取數組或者對象除了前幾項或者除了某幾項的其餘項

//數組
    const number = [1,2,3,4,5]
    const [first, ...rest] = number
    console.log(rest) //2,3,4,5
    //對象
    const user = {
        username: 'lux',
        gender: 'female',
        age: 19,
        address: 'peking'
    }
    const { username, ...rest } = user
    console.log(rest) //{"address": "peking", "age": 19, "gender": "female" }

對於 Object 而言,還能夠用於組合成新的 Object 。(ES2017 stage-2 proposal) 固然若是有重複的屬性名,右邊覆蓋左邊

const first = {
        a: 1,
        b: 2,
        c: 6,
    }
    const second = {
        c: 3,
        d: 4
    }
    const total = { ...first, ...second }
    console.log(total) // { a: 1, b: 2, c: 3, d: 4 }

7.import 和 export

import導入模塊、export導出模塊

//所有導入
import people from './example'

//有一種特殊狀況,即容許你將整個模塊看成單一對象進行導入
//該模塊的全部導出都會做爲對象的屬性存在
import * as example from "./example.js"
console.log(example.name)
console.log(example.age)
console.log(example.getName())

//導入部分
import {name, age} from './example'

// 導出默認, 有且只有一個默認
export default App

// 部分導出
export class App extend Component {};

導入的時候有沒有大括號的區別是什麼。下面是總結:

1.當用export default people導出時,就用 import people 導入(不帶大括號)

2.一個文件裏,有且只能有一個export default。但能夠有多個export。

3.當用export name 時,就用import { name }導入(記得帶上大括號)

4.當一個文件裏,既有一個export default people, 又有多個export name 或者 export age時,導入就用 import people, { name, age } 

5.當一個文件裏出現n多個 export 導出不少模塊,導入時除了一個一個導入,也能夠用import * as example

8. Promise

在promise以前代碼過多的回調或者嵌套,可讀性差、耦合度高、擴展性低。經過Promise機制,扁平化的代碼機構,大大提升了代碼可讀性;用同步編程的方式來編寫異步代碼,保存線性的代碼邏輯,極大的下降了代碼耦合性而提升了程序的可擴展性。
說白了就是用同步的方式去寫異步代碼。
發起異步請求:

fetch('/api/todos')
      .then(res => res.json())
      .then(data => ({ data }))
      .catch(err => ({ err }));

面試題:

setTimeout(function() {
      console.log(1)
    }, 0);
    new Promise(function executor(resolve) {
      console.log(2);
      for( var i=0 ; i<10000 ; i++ ) {
        i == 9999 && resolve();
      }
      console.log(3);
    }).then(function() {
      console.log(4);
    });
    console.log(5);

9.Generators

生成器( generator)是能返回一個迭代器的函數。生成器函數也是一種函數,最直觀的表現就是比普通的function多了個星號*,在其函數體內可使用yield關鍵字,有意思的是函數會在每一個yield後暫停。

這裏生活中有一個比較形象的例子。我們到銀行辦理業務時候都得向大廳的機器取一張排隊號。你拿到你的排隊號,機器並不會自動爲你再出下一張票。也就是說取票機「暫停」住了,直到下一我的再次喚起纔會繼續吐票。

OK。說說迭代器。當你調用一個generator時,它將返回一個迭代器對象。這個迭代器對象擁有一個叫作next的方法來幫助你重啓generator函數並獲得下一個值。next方法不只返回值,它返回的對象具備兩個屬性:done和value。value是你得到的值,done用來代表你的generator是否已經中止提供值。繼續用剛剛取票的例子,每張排隊號就是這裏的value,打印票的紙是否用完就這是這裏的done。

// 生成器
    function *createIterator() {
        yield 1;
        yield 2;
        yield 3;
    }
    
    // 生成器能像正規函數那樣被調用,但會返回一個迭代器
    let iterator = createIterator();
    
    console.log(iterator.next().value); // 1
    console.log(iterator.next().value); // 2
    console.log(iterator.next().value); // 3

那生成器和迭代器又有什麼用處呢?
圍繞着生成器的許多興奮點都與異步編程直接相關。異步調用對於咱們來講是很困難的事,咱們的函數並不會等待異步調用完再執行,你可能會想到用回調函數,(固然還有其餘方案好比Promise好比Async/await)。

生成器可讓咱們的代碼進行等待。就不用嵌套的回調函數。使用generator能夠確保當異步調用在咱們的generator函數運行一下行代碼以前完成時暫停函數的執行。
那麼問題來了,我們也不能手動一直調用next()方法,你須要一個可以調用生成器並啓動迭代器的方法。就像這樣子的

function run(taskDef) { //taskDef即一個生成器函數

        // 建立迭代器,讓它在別處可用
        let task = taskDef();

        // 啓動任務
        let result = task.next();
    
        // 遞歸使用函數來保持對 next() 的調用
        function step() {
    
            // 若是還有更多要作的
            if (!result.done) {
                result = task.next();
                step();
            }
        }
    
        // 開始處理過程
        step();
    
    }

生成器與迭代器最有趣、最使人激動的方面,或許就是可建立外觀清晰的異步操做代碼。你沒必要處處使用回調函數,而是能夠創建貌似同步的代碼,但實際上卻使用 yield 來等待異步操做結束。

10.async 函數

es6引入了 async 函數,使得異步操做變得更加方便。
async 函數是什麼?一句話,它就是 Generator 函數的語法糖。

function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

一比較就會發現,async函數就是將 Generator 函數的星號(*)替換成async,將yield替換成await,僅此而已。

async函數對 Generator 函數的改進,體如今如下四點。

  • 內置執行器
  • 更好的語義
  • 更廣的適用性
  • 返回值是 Promise

11.Class基本語法

JavaScript 語言中,生成實例對象的傳統方法是經過構造函數

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

es6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,做爲對象的模板。經過class關鍵字,能夠定義類。

基本上,es6 的%(red)[class]能夠看做只是一個語法糖,它的絕大部分功能,es5 均可以作到,新的%(red)[class]寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。上面的代碼用 es6 的%(red)[class]改寫,就是下面這樣。

//定義類
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

上面代碼定義了一個「類」,能夠看到裏面有一個constructor方法,這就是構造方法,而this關鍵字則表明實例對象。也就是說,es5 的構造函數Point,對應 es6 的Point類的構造方法。

Point類除了構造方法,還定義了一個toString方法。注意,定義「類」的方法的時候,前面不須要加上function這個關鍵字,直接把函數定義放進去了就能夠了。另外,方法之間不須要逗號分隔,加了會報錯。

es6 的類,徹底能夠看做構造函數的另外一種寫法。

相關文章
相關標籤/搜索