JS知識點:ES6 中常見的知識點

前言

ES6(ESMAScript 6, ESMAScript2015)是指ESMA組織在2015年發佈的ESMAScript 2015標準,因爲相比ES5(2009年發佈),ES6的改動很是大,故廣義上,咱們也將ES6統稱全部ES5以後的新特性,包括ES7(ESMAScript2016),ES8(ESMAScript2017)...ES10(ESMAScript2019)等,這裏專指ES6編程

一、var,letconst

咱們知道在ES5中,用var關鍵字聲明的變量存在變量提高,函數及變量的聲明都將被提高到函數的最頂部,這就致使能夠在使用以後在聲明變量,這種不規範的行爲不少時候都依靠編程習慣來約束,尤爲對於新手來講存在很多隱患,因而letconst應運而生,兩者的做用域都是當前代碼塊域,let聲明的變量能夠修改,const聲明的是常量,聲明後不可修改。數組

let foo = 10;
const bar = {key: 20};
foo = 20;
bar.key = 30;
bar = 30; // TypeError
複製代碼

這裏解釋一下bar.key = 30這句話改變的不是bar的值,只是修改了其中的屬性,就比如古代的女人嫁雞隨雞、從一而終,後來這隻雞變了,那也只是變成了一隻「大雞」,但雞仍是這隻雞……app

1.一、 做用域的差異

JavaScript中,咱們總能聽到這樣的話:在JavaScript中只有函數做用域,沒有塊級做用域。那麼他們究竟是什麼呢?async

以一個function聲明的代碼塊是一個函數做用域,以下代碼塊中的scope 1scope 2就是同一個函數做用域,但他們是兩個不一樣的代碼塊函數

function fn(){

    console.log(foo1); // undefined
    console.log(bar1); // undefined
    
    // scope 1
    var foo1 = 10;
    let foo2 = 20;
    if(true) {
        // scope 2
        var bar1 = 30;
        let bar2 = 40;
    }
    
    console.log(foo1); // 10
    console.log(foo2); // 20
    console.log(bar1); // 30
    console.log(bar2); // ReferenceError
}
複製代碼

咱們能夠看出用let聲明的變量bar2if語句外是不能訪問的(ReferenceError引用錯誤,表明引用了一個未聲明的變量)ui

1.二、 變量提高

在上面的例子中第一句console.log(bar1);輸出了undefined,並未報錯,說明用var聲明的變量在函數開始時就已經定義(即變量提高),但須要注意的是此時並未初始化。但採用letconst聲明的變量則不存在變量提高,必須先聲明後使用,不然就會報錯:ReferenceError,同時咱們把這種在聲明以前不可以使用這種特性成爲 暫時性死區(Temproal Dead Zone, TDZ)。this

console.log(foo); // ReferenceError
let foo = 10;
複製代碼

1.三、 重複聲明

var foo = 10;
var foo = 20;

let bar = 20;
let bar = 20; // SyntaxError
複製代碼

當咱們用var聲明變量的時候,是能夠重複聲明的,後聲明的會覆蓋前面的聲明,但當咱們用letconst聲明的時候則會報語法錯誤SyntaxError,注意,function聲明的函數也和var有類似的表現:存在 變量提高重複聲明spa

二、解構賦值

解構顧名思義按照必定的結構「解析」一個對象,經過這種方式咱們能夠從數組或對象中取值,本質上屬於 模式匹配,這是ES6給咱們提供的新語法,只要等號兩邊的模式相同便可解析相應的值。prototype

// 解構一個對象,能夠只解構咱們須要的部分
const {foo, bar} = {foo: 1, bar: 2, other: 3};
console.log(foo, bar); // 1 2

// 解構一個數組
const [first, second] = [10, 20, 30];
console.log(first, second); // 10 20

// 忽略數組的某個值
const [first, , third] = [10, 20, 30];
console.log(first, third); // 10 30

// 解構一個不存在的鍵時,其值爲 undefined
const {foo, bar} = {foo: 1}; // undefined

// 注意數組的 length
const [length] = [12];
console.log(length); // 12
// 用方括號可花括號解析的差異
const {length} = [12];
console.log(length); // 1

// 當屬性中包含關鍵字或保留字時須要重命名
const {class: clazz} = {class: 'class-name'};

// 動態匹配,一樣須要重命名
const key = 'thisKey';
const {[key]: value} = {thisKey: 23};
console.log(value); // 23

// 一樣動態匹配也可用在對象聲明中
const key = 'currentKey';
const obj = {[key]: 20};
console.log(obj); // {currentKey: 20}

// 用解構賦值交換兩個變量,不須要聲明第三個變量
let foo = 10;
let bar = 20;
console.log(foo, bar); // 10 20
[foo, bar] = [bar, foo];
console.log(foo, bar); // 20 10
複製代碼

三、 擴展運算符

在使用解構賦值的時候,可使用擴展運算符...來將剩餘值統一放入一個對象中,rest

// 用於對象
const {foo, ...rest} = {foo: 1, bar: 2, other: 3};
console.log(foo, rest); // 1 {bar: 2, other: 3}

// 用於數組
const [first, ...others] = [1, 2, 3];
console.log(first, others); // 1 [2, 3]

// 函數調用中傳遞參數
function callback(first, second){
    console.log(first, second);
}
let params = [1, 2];
callback(...params); // 輸出 1 2

// 用於獲取 arguments,這種特性主要用於箭頭函數
const arrow = (...args) => {
    console.log(args);
};
arrow(1, 2, 3); // 輸出數組 [1, 2, 3]
複製代碼

擴展運算符其實是調用了Iterator接口,一般的Array,String,NodeList,Arguments,Set,Map都實現了這個接口,因此可經過擴展運算符得到arguments,也可用於函數調用時傳遞參數,但Object並未實現Iterator,因此當咱們調用fn(...obj)時,會報錯TypeError

四、默認值和默認參數

在咱們使用解構賦值的時候,能夠爲不存在的值或值爲undefined賦默認值

// 不存在指定映射
const {foo, bar = 20} = {foo: 10};
console.log(foo, bar); // 10 20

// 不存在指定值爲 undefined
const {foo, bar = 20} = {foo: 10, bar: undefined};
console.log(foo, bar); // 10 20
複製代碼

只有爲undefined時纔會執行默認值賦值步驟

const {foo, bar = (function(){
    console.log('默認值');
    return 20;
})()} = {foo: 10};
// 默認值
console.log(foo, bar); // 10 20

function defaultVal(){
    console.log('默認值');
    return 20;
}
const {key1, key2 = defaultVal()} = {key1: 10, key2: null};
console.log(key1, key2); // 10 null
// 可見 key2 爲 null,並未輸出 「默認值」
複製代碼

在聲明函數時,咱們也能夠爲函數參數賦默認值,一樣只有在傳入參數爲undefined時,才執行默認值賦值操做

function fn(foo, bar = 20){
    console.log(foo, bar);
}

fn(10); // 10 20
複製代碼

五、箭頭函數

ES6給咱們帶來了新的函數語法箭頭函數,在日常的書寫中,極大的提升了咱們的開發效率,可是箭頭函數和普通函數卻有着很大的差異

// 基本使用
const arrow = () => {};

// 有一個參數
const arrow = (arg) => {};
const arrow = arg => {}; // 括號可省略

// 有多個參數
const arrow = (arg1, arg2, arg3) => {}

// 函數體只有一句
const arrow = () => 1; // 花括號可省略
// 等價於
const arrow = () => {return 1;};

// 函數體有多句
const arrow = arg => {
    if(arg){
        return 1;
    } else {
        return 2;
    }
};

// 返回一個空對象
// 這實際上只有一句,但括號不能省略,不然會被解析成代碼塊
const arrow = () => ({});
複製代碼

5.一、沒有自身this

箭頭函數的this是和聲明時的父做用域綁定的,不可經過其餘方式(call,apply,bind)更改this指向,如:

'use strict';
function fn1(){
    console.log(this.foo);
    return function() {
        console.log(this.foo)
    }
}

const obj = {foo: 10};
const bar = fn1.call(obj); // 10
bar(); // TypeError(由於此時 this 指向 undefined)

const changedObj = {foo: 60};
bar.call(changedObj); // 60 (this 能夠從新綁定)
複製代碼

若是是箭頭函數:

'use strict';
function fn1(){
    console.log(this.foo);
    return () => {
        console.log(this.foo)
    }
}

const obj = {foo: 10};
const bar = fn1.call(obj); // 10
bar(); // 10 (此時 this 仍然指向 obj)
obj.foo = 20;
bar(); // 20

const changedObj = {foo: 60};
bar.call(changedObj); // 20
複製代碼

5.二、不可以使用new

const foo = () => {};
const bar = function(){
}

new bar(); // bar {}
new foo(); // TypeError
複製代碼

5.三、沒有arguments

箭頭函數沒有arguments,但可使用上面的解構語法得到 參數列表,但獲得的不是Arguments對象,而是一個包含全部參數的數組

const arrow = (...args) => {
    console.log(args);
}
arrow(1, 2, 3); // [1, 2, 3]
複製代碼

六、class語法

ES5中,只能經過Function.prototype來實現類的聲明和繼承,ES6提供了新的語法來聲明一個類,使其更加貼合面向對象編程,

// ES5
function Student(){}

Student.staticMethod = function(){
    // ES5 的靜態方法
}
Student.prototype.memberMethod = function(){
    // ES5 的實例方法
}

// ES6
class Person {
    constructor(name) {
        this.name = name;
        this.memberMethod = this.memberMethod.bind(this);
    }
    
    static staticMethod(){
        console.log('這是一個靜態方法');
    }
    
    static memberMethod(){
        // 注意實例方法 ES6 並未綁定 this
        // React 中也建議了,在構造器中應手動綁定 this
        // 如上
        console.log('這是實例方法');
    }
}
複製代碼

七、Promise

(相比以上,這是大概是最複雜的,下一篇將專門將這一特性,後續還會介紹ES7async,await,他們都是依賴Promise,因此……很重要!)

相關文章
相關標籤/搜索