es6基本知識整合

rosenWang發表於2018-11-22

本文主要記錄常見的es6基礎知識

目錄

  1. es6是什麼
  2. let和const
  3. 字串模板
  4. 解構賦值
  5. set和map
  6. 箭頭函式
  7. class類和extends
  8. promise
  9. async/await
  10. export和import

1. es6是什麼

ECMAScript 6(以下簡稱ES6)是JavaScript語言的下一代標準。因為當前版本的ES6是在2015年釋出的,所以又稱ECMAScript 2015(簡稱ES2015)。雖然瀏覽器在不斷更新,但並不是所有使用者的電腦瀏覽器都支援ES6,所以在使用的過程中建議還是轉成es5,保證程式碼的可執行性。至於轉換的方式大家可以用Babel或者Traceur轉碼器。

ECMAScript的幾次大版本:es3-->es5-->es6(es7相對於es6變動不大)

2. let 和 const

let

在es5裡,定義變數用var,即相當於在全域性宣告一個變數,由於js沒有塊級作用域,會導致在使用變數的過程中存在變數提升的概念。

console.log(a);   // undefined
var a = 10;
console.log(a)    //10

function fn () {
    console.log(a); // undefined
    var a = 'aaa';
    console.log(a); // aaa
}
fn();
複製程式碼

在C,Java等語言中,變數必須先申明在使用,否則會報錯,但是在js中由於變數提升的原因不會報錯。

關於var定義變數還有一個經典的實列:

for(var i=0;i<5;i++){
    setTimeout(function(){
        console.log(i)
    },1000)
}
//5 5 5 5 5
複製程式碼

結果並不是預期的 0,1,2,3,4。這是因為由於js內部的執行機制,遇到定時器任務以及非同步請求時其回撥函式會先放到任務隊中,優先執行主程式任務,待主程式執行完畢再執行任務佇列中的程式碼。es5中用閉包來解決這個問題,使用了立即執行函式:

for(var i=0;i<5;i++){
    (function(i){
        setTimeout(function(){
            console.log(i)
        },1000)
    })(i)
}
// 0 1 2 3 4
複製程式碼

當然,也可以使用es6中的let,簡潔方便,直接將var改為let即可。

下面的列題方面理解,點選標籤彈出對應索引

window.onload = function(){
    var aInput = document.getElementsByTagName("input");
    // 傳統解決辦法
    for(var i=0;i<aInput.length;i++){
        (function(i){
        // 函式閉包自執行來解決i索引的問題
            aInput[i].onclick = function(){
                alert(i);
            };
        })(i);
    }

    // let變數的出現相當於給你加了一個封閉空間來極度簡化了i值索引的問題
    // let大家可以看成是匿名函式立即呼叫(IIFE)
    for(let i=0;i<aInput.length;i++){
        aInput[i].onclick = function(){
            alert(i);
        };  
    }
};
複製程式碼

const

es6中使用const來定義常量,顧名思義即不變的量,不可修改(這裡指的是普通常量,比如字串,數字,boolean),當對一個物件常量修改其屬性則是允許的。

const a = 10;
a = 20
//報錯: Uncaught SyntaxError: Identifier 'a' has already been declared

const obj = {
    a: 1,
    name: 'hello'
}
obj.a = 2
obj.name = 'world'
console.log(obj)
//{a: 2,name: 'world'}

複製程式碼

關於let和const的使用會存在 暫死性區域

3. 字串模板

在es5中使用字串拼接常這樣來做:

let name = 'wjb'
console.log('i am ' + name)
複製程式碼

當遇到字串比較長,涉及到的變數比較多的時候就顯得不那麼好看和使用了,es6新增字串拼接方法,使用``包裹內容,${}描述變數:

let name = 'wjb'
console.log(`i am ${name}`)
複製程式碼

4.解構賦值

// 以前我們給變數賦值,只能直接指定值
var a = 1;
var b = 2;
var c = 3;
console.log(a,b,c); // 1 2 3
// 現在用解構賦值的寫法就變得簡單了,只要模式匹配上了就行了,如下
// 注意陣列是有順序的
var [a,b,c] = [11,22,33];
console.log(a,b,c); // 11 22 33

var [b,a,c] = [11,22,33];
console.log(a,b,c); // 22 11 33

// 當然解構賦值還有巢狀比較複雜的寫法,如下
let [foo,[[bar],[baz]]] = [111,[[222],[333]]];
console.log(foo,bar,baz); // 111 222 333

let [head,...foot] = [1,2,3,4];
console.log(head,foot); // 1 [2,3,4]

// 如果解構不成功,變數的值就等於undefined,如下
var [bar3,foo3] = [1000];
console.log(bar3,foo3); // 1000 undefined

// 另一種情況是不完全解構,即等號左邊的模式,只匹配一部分的等號右邊的陣列。這種情況下,解構依然可以成功
let [x,y] = [10000,20000,30000];
console.log(x,y); // 10000 20000

// 預設值可以引用解構賦值的其他變數,但該變數必須已經宣告
let [a=1,b=a] = [2,3];
console.log(a,b); // 2 3

// 物件的解構也可以指定預設值
var {x,y=5} = {x:1};
console.log(x,y); // 1 5

//物件的解構賦值解構不僅可以用於陣列,還可以用於物件(json)
//物件的解構與陣列有一個重要的不同。陣列的元素是按次序排列的,變數的取值由它的位置決定;
//而物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值
var {a,b} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana
var {b,a} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana

// 如果變數名與屬性名不一致,必須寫成下面這樣
let obj = {first:'hello',last:'world'};
// first ---> f,那麼此時f就是first,而不是undefined了,有點類似別名的概念
let {first:f,last} = obj;
console.log(f,last); // hello world

//1.也就是說,物件的解構賦值的內部機制,是先找到同名屬性,然後再賦給對應的變數。 真正被賦值的是後者,而不是前者
//2.v是匹配的模式,n才是變數。真正被賦值的是變數n,而不是模式v。  
//注意,採用這種寫法時,變數的宣告和賦值是一體的
// v ---> n,那麼此時n就是vue,而不是undefined了
var {v:n} = {v:'vue',r:'react'};
console.log(n); // vue
console.log(v); // Uncaught ReferenceError: v is not defined
console.log(r); // Uncaught ReferenceError: r is not defined
複製程式碼

5.set和map

有待完善
es6新增的兩種資料結構

filter: 過濾掉不符合條件的元素
forEach: 遍歷
reduce: 彙總

6.箭頭函式

改變了this指向,使this指向當前呼叫的物件,簡化了函式書寫的方式:

let a = {
    name: 'wjb',
    fn: function(){
        return this.name
    }
}
a.fn()
//  wjb

let a = {
    name: 'wjb',
    fn: () =>{
        return this.name
    }
}
a.fn()

// undefined
複製程式碼

常用形式:

//箭頭函式寫法 function(){} 變為 ()=>{}
window.onload = () => {
        var oBox = document.getElementById("box");
        oBox.onclick = () => {
        oBox.style.backgroundColor = '#ff0000';
    };
};

//注意this指向會有問題
var json = {
    a:1,
    b:2,
    showName:() => {
        return this.a;
    }
};
// 因為使用了箭頭函式this指向了object window 所以result:undefined
console.log(json.showName());
複製程式碼

7. class類和extends

es6新增了類的概念,即像C,Java一樣宣告類,在class類中定義方法和屬性。

//傳統物件導向寫法
function Person(name,age){ // 類、建構函式
    this.name = name;
    this.age = age;
}
Person.prototype.showName = function(){
    return this.name;
};
Person.prototype.showAge = function(){
    return this.age;
};

//ES6物件導向寫法
class Person{
    // 構造器(在構造器中也可以設定預設引數)
    constructor(name='default',age=10){   
        this.name = name;
        this.age = age;
    }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}

//物件導向class給預設值
class Person{
    // 構造器
    constructor(name='default',age=0){
        this.name = name;
        this.age = age;
    }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}
複製程式碼

js實現繼承的方式有很多種,本文主要講解原型繼承以及類繼承

原型繼承: 通過指定物件的prototype屬性,來繼承目標建構函式的方法和屬性。

類繼承 : 通過es6提供的extends方法來實現類的繼承。

如下所示:

//傳統寫法原型繼承
function Person(name,age){ // 類、建構函式
    this.name = name;
    this.age = age;
}
Person.prototype.showName = function(){
    return this.name;
};
Person.prototype.showAge = function(){
    return this.age;
};
// 工人類
function Worker(name,age){
    // 屬性繼承過來
    Person.apply(this,arguments);
}
// 原型繼承
Worker.prototype = new Person();
var p1 = new Person('allen',28);
var w1 = new Person('worker',1000);
console.log(w1.showName()); // 確實繼承過來了 result:worker

//ES6中物件導向實現類繼承
class Person{
    // 構造器
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}
class Worker extends Person{
    constructor(name,age,job='拖地的'){
        // 繼承超父類的屬性
        super(name,age);
        this.job = job;
    }
    showJob(){
        return this.job;
    }
}
var p1 = new Person('aaa',18);
var w1 = new Person('www',36);
var w2 = new Worker('wwwwwwww',90);
console.log(w1.showName()); // www
console.log(w2.showJob()); // 預設給的值 ‘拖地的’
複製程式碼

8. promise

promise主要是用來解決非同步操作,它存在三種狀態: peding(等待),fulfilled(已完成),rejected(已拒絕),關於promise的用法,需要注意:

  1. 一個promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
  2. promise必須實現then方法(可以說,then就是promise的核心),而且then必須返回一個promise,同一個promise的then可以呼叫多次,並且回撥的執行順序跟它們被定義時的順序一致
  3. then方法接受兩個引數,第一個引數是成功時的回撥,在promise由“等待”態轉換到“完成”態時呼叫,另一個是失敗時的回撥,在promise由“等待”態轉換到“拒絕”態時呼叫。同時,then可以接受另一個promise傳入,也接受一個“類then”的物件或方法,即thenable物件。
//基本用法
let p = new Promise((resolve, reject) => {
    $.ajax({
        url: '1.txt',
        dataType: 'json',
        success(json){
            resolve(json);
        },
        error(err){
            reject(err);
        }
    })
});
p.then(json=>{
    console.log('成功',json);
}, err=>{
    console.log('獲取失敗');
})
複製程式碼

promise.all

Promise.all可以將多個Promise例項包裝成一個新的Promise例項。同時,成功和失敗的返回值是不同的,成功的時候返回的是一個結果陣列,而失敗的時候則返回最先被reject失敗狀態的值。

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promse.reject('失敗')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失敗了,打出 '失敗'
})
複製程式碼

promise.race

將多個promise實列打包成一個新的實列傳送,並返回獲得結果最快的回撥。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 開啟的是 'failed'
})
複製程式碼

更多的promise訪問 www.jianshu.com/p/7e60fc1be…

9.async/await

在進行非同步請求時,往往會存在一個請求裡巢狀著多個請求,後續的請求需要用到之前的請求回撥,當請求巢狀過多時,則會出現回撥地獄,不利於程式碼的開發和維護,因此需要使用async/await來處理巢狀非同步請求。

let value
getInfo(){
    let promise = new Promise(function(resolve,reject){
        resolve('成功了');
    });
    promise.then(function(res){
        return res
    }).catch(function(err){
        return err
    });
}
ajax(){
    getInfo().then((res)=>{
        value = res     //2-後執行
    })
    console.log(res)    //1-先執行
}
ajax()    //  undefined
複製程式碼

使用async/await後

let value
getInfo(){
    let promise = new Promise(function(resolve,reject){
        resolve('成功了');
    });
    promise.then(function(res){
        return res
    }).catch(function(err){
        return err
    });
}
async ajax(){
    await getInfo().then((res)=>{
        value = res     //1-回撥限制性
    })
    console.log(res)    //2-後執行
}
ajax()    //  res
複製程式碼

10. export和import

export用於對外輸出本模組(一個檔案可以理解為一個模組)變數的介面

import用於在一個模組中載入另一個含有export介面的模組。

也就是說使用export命令定義了模組的對外介面以後,其他JS檔案就可以通過import命令載入這個模組(檔案)。

export和export default的區別

  1. export與export default均可用於匯出常量、函式、檔案、模組等
  2. 你可以在其它檔案或模組中通過import+(常量 | 函式 | 檔案 | 模組)名的方式,將其匯入,以便能夠對其進行使用
  3. 在一個檔案或模組中,export、import可以有多個,export default僅有一個
  4. 通過export方式匯出,在匯入時要加{ },export default則不需要
let name1="李四";
let name2="張三";
export { name1 ,name2 }  //匯出
//匯入
import { name1 , name2 } from "/.a.js" //路徑根據實際情況填寫

let name="李四";
export default name
import name from "/.a.js" 這裡name不需要大括號
複製程式碼