ES6--JavaScript的第六個版本 javascript預編譯的過程

 1、新的變量聲明方式 let/cons

與var不一樣,新的變量聲明方式帶來了一些不同的特性,其中最重要的兩個特性就是提供了塊級做用域與再也不具有變量提高。javascript

如果對變量提高不怎麼了解的話能夠去參考個人其餘文章   javascript預編譯的過程 。html

什麼是塊級做用域膩?java

寫在 「{}」 內的內容   都是塊級做用域。node

在es6以前,咱們想保護一個變量怎麼辦,將其放在一個當即執行函數裏面,寫在局部做用域中。react

這樣寫是否是挺麻煩的捏,多謝幾個單詞不花時間麼?!es6

在ES6中,咱們想保護一個變量 只要 寫在花括號中就行了。面試

{chrome

  let   a=10;數組

}promise

下面有兩個例子:

{
    let a = 20;
}

console.log(a);  // a is not defined
//寫在塊級做用域中的內容會被保護起來,因此會打印  a is not defined

而這個簡單的例子,會被編譯爲:

{
let _a = 20;
}
console.log(a); // a is not defined


// ES5
console.log(a); // undefined
var a = 20;
//  在es5中var一個變量 會變量提高
//  如同
//  var a;
//  console.log(a); //從上向下順序執行 固然會打印 undefined 的捏。
//  a=20


// ES6 
console.log(a); // a is not defined 
let a = 20;
//變量不會提高 打印時固然是 a is not defined

固然,你的代碼編譯成爲了ES5以後,仍然會存在變量提高,所以這一點只須要咱們記住便可。

在實際使用中,也須要儘可能避免使用變量提高的特性帶來的負面影響。只有在面試題中,纔會對變量提高不停的濫用。使用ES6,咱們須要全面使用let/const替換var,那麼何時用let,何時用const就成爲了一個你們要熟練區分的一個知識點。

咱們經常使用let來聲明一個值會被改變的變量,而使用const來聲明一個值不會被改變的變量,也能夠稱之爲常量。當值爲基礎數據類型時,那麼這裏的值,就是指值自己。而當值對應的爲引用數據類型時,那麼我這裏說的值,則表示指向該對象的引用。

這裏須要注意,正由於該值爲一個引用,只須要保證引用不變就能夠,咱們仍然能夠改變該引用所指向的對象。當咱們試圖改變const聲明的變量時,則會報錯。

寫幾個例子,你們能夠仔細揣摩一下:

let a = null;
a = 20;
const obDev = {
    a: 20,
    b: 30
}

obDev.a = 30;

console.log(obDev); // Object {a: 30, b: 30}
const fn = function() {}
const a = obDev.a;
... ...

只要抓住上面我說的特性,那麼在使用let/const時就會顯得遊刃有餘。根據我本身的經驗,使用const的場景要比使用let的場景多不少。

 

二,解構賦值

咱們常常定義許多對象和數組,而後有組織地從中提取相關的信息片斷。在ES6中添加了能夠簡化這種任務的新特性:解構。解構是一種打破數據結構,將其拆分爲更小部分的過程。

在ES5中,開發者們爲了從對象和數組中獲取特定數據並賦值給變量,編寫了許多看起來同質化的代碼

let options = {
    repeat: true,
    save: false
};
// 從對象中提取數據
let repeat = options.repeat,
save = options.save;

  這段代碼從options對象中提取repeat和save的值,並將其存儲爲同名局部變量,提取的過程極爲類似

  若是要提取更多變量,則必須依次編寫相似的代碼來爲變量賦值,若是其中還包含嵌套結構,只靠遍歷是找不到真實信息的,必需要深刻挖掘整個數據結構才能找到所需數據

  因此ES6添加了解構功能,將數據結構打散的過程變得更加簡單,能夠從打散後更小的部分中獲取所需信息

 對象解構:

對象字面量的語法形式是在一個賦值操做符左邊放置一個對象字面量。

let node = {
    type: "Identifier",
    name: "foo"
};
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
let node = {
    type: "Identifier",
    name: "foo"
},
type = "Literal",
name = 5;
// 使用解構來分配不一樣的值
({ type, name } = node);
console.log(type); // "Identifier"
console.log(name); // "foo"

 

在這個示例中,聲明變量type和name時初始化了一個值,在後面幾行中,經過解構賦值的方法,從node對象讀取相應的值從新爲這兩個變量賦值

  [注意]必定要用一對小括號包裹解構賦值語句,JS引擎將一對開放的花括號視爲一個代碼塊。語法規定,代碼塊語句不容許出如今賦值語句左側,添加小括號後能夠將塊語句轉化爲一個表達式,從而實現整個解構賦值過程。

數組解構:

與對象解構的語法相比,數組解構就簡單多了,它使用的是數組字面量,且解構操做所有在數組內完成,而不是像對象字面量語法同樣使用對象的命名屬性

let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"

  在這段代碼中,咱們從colors數組中解構出了"red"和"green"這兩個值,並分別存儲在變量firstColor和變量secondColor中。在數組解構語法中,咱們經過值在數組中的位置進行選取,且能夠將其存儲在任意變量中,未顯式聲明的元素都會直接被忽略

  在解構模式中,也能夠直接省略元素,只爲感興趣的元素提供變量名

let colors = [ "red", "green", "blue" ];
let [ , , thirdColor ] = colors;
console.log(thirdColor); // "blue"

  這段代碼使用解構賦值語法從colors中獲取第3個元素,thirdColor前的逗號是前方元素的佔位符,不管數組中的元素有多少個,均可以經過這種方法提取想要的元素,不須要爲每個元素都指定變量名

混合解構:

  能夠混合使用對象解構和數組解構來建立更多複雜的表達式,如此一來,能夠從任何混雜着對象和數組的數據解構中提取想要的信息

let node = {
    type: "Identifier",
    name: "foo",
    loc: {
        start: {
            line: 1,
            column: 1
        },
        end: {
            line: 1,
            column: 4
        }
    },
    range: [0, 3]
};
let {
    loc: { start },
    range: [ startIndex ]
} = node;
console.log(start.line); // 1
console.log(start.column); // 1
console.log(startIndex); // 0

  這段代碼分別將node.loc.start和node.range[0]提取到變量start和startlndex中

  解構模式中的loc和range僅表明它們在node對象中所處的位置(也就是該對象的屬性)。當使用混合解構的語法時,則能夠從node提取任意想要的信息。這種方法極爲有效,尤爲是從JSON配置中提取信息時,再也不須要遍歷整個結構了

 

3、 箭頭函數的使用

以前我說ES6顛覆了js的編碼習慣,箭頭函數的使用佔了很大一部分。

首先是寫法上的不一樣:

// es5
var fn = function(a, b) {
    return a + b;
}

// es6 箭頭函數寫法,當函數直接被return時,能夠省略函數體的括號
const fn = (a, b) => a + b;

// es5
var foo = function() {
    var a = 20var b = 30;
    return a + b;
}

// es6
const foo = () => {
   const a = 20;
   const b = 30;
   return a + b;
}

箭頭函數能夠替換函數表達式,可是不能替換函數聲明

其次還有一個相當重要的一點,那就是箭頭函數中,沒有this。若是你在箭頭函數中使用了this,那麼該this必定就是外層的this。

也正是由於箭頭函數中沒有this,所以咱們也就無從談起用call/apply/bind來改變this指向。記住這個特性,能讓你在react組件之間傳值時少走無數彎路。

var person = {
    name: 'tom',
    getName: function() {
        return this.name;
    }
}

// 咱們試圖用ES6的寫法來重構上面的對象
const person = {
    name: 'tom',
    getName: () => this.name
}

// 可是編譯結果倒是
var person = {
    name: 'tom',
    getName: function getName() {
        return undefined.name;
    }
};

在ES6中,會默認採用嚴格模式,所以this也不會自動指向window對象了,而箭頭函數自己並無this,所以this就只能是undefined,這一點,在使用的時候,必定要慎重慎重再慎重,否則踩了坑你都不知道本身錯在哪!這種狀況,若是你還想用this,就不要用使用箭頭函數的寫法。

// 能夠稍作改動
const person = {
    name: 'tom',
    getName: function() {
        return setTimeout(() => this.name, 1000);
    }
}

// 編譯以後變成
var person = {
    name: 'tom',
    getName: function getName() {
        var _this = this;  // 使用了咱們在es5時經常使用的方式保存this引用

        return setTimeout(function () {
            return _this.name;
        }, 1000);
    }
};

先記住箭頭函數的寫法,並留意箭頭函數中關於this的特殊性,更過實踐與注意事項咱們在封裝react組件時再慢慢來感覺。

還有就是  原函數中  arguments (實參) 在箭頭函數中是是用不了的。

 

四,promise  (承諾)

 他就是一個對象,主要是用來處理異步數據的。

在promise中,有三種狀態:

pending(等待,處理中)   -->  1.resolve(完成)   2.rejected(失敗,拒絕)。

又,這三種狀態的變化只有兩種模式,而且一旦狀態改變,就不會再變:

  一、異步操做從pending到resolved;

  二、異步操做從pending到rejected;

好了,既然它是屬於ES6規範,咱們再經過chrome,直接打印出Promise,看看這玩意:

 

恩,一目瞭然,Promise爲構造函數,歐克,這樣經過它,咱們就能夠實例化本身的Promise對象了,並加以利用。 

Promise對象中的then方法

能夠接收構造函數中處理的狀態變化,並分別對應執行。then方法有2個參數,第一個函數接收resolved狀態的執行,第二個參數接收reject狀態的執行。

function fn(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve();
        } else {
            reject();
        }
    }).then(function() {
        console.log('參數是一個number值');
    }, function() {
        console.log('參數不是一個number值');
    })
}

fn('hahha');
fn(1234);

 

then方法的執行結果也會返回一個Promise對象。所以咱們能夠進行then的鏈式執行,這也是解決回調地獄的主要方式。

function fn(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve();
        } else {
            reject();
        }
    })
    .then(function() {
        console.log('參數是一個number值');
    })
    .then(null, function() {
        console.log('參數不是一個number值');
    })
}

fn('hahha');
fn(1234);

 

then(null, function() {}) 就等同於catch(function() {})

catch的用法

咱們知道Promise對象除了then方法,還有一個catch方法,它是作什麼用的呢?其實它和then的第二個參數同樣,用來指定reject的回調,用法是這樣:
getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

效果和寫在then的第二個參數裏面同樣。不過它還有另一個做用:在執行resolve的回調(也就是上面then中的第一個參數)時,若是拋出異常了(代碼出錯了),那麼並不會報錯卡死js,而是會進到這個catch方法中。請看下面的代碼:

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此處的somedata未定義
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});
 
在resolve的回調中,咱們console.log(somedata);而somedata這個變量是沒有被定義的。若是咱們不用Promise,代碼運行到這裏就直接在控制檯報錯了,不往下運行了。可是在這裏,會獲得這樣的結果:
 
 
也就是說進到catch方法裏面去了,並且把錯誤緣由傳到了reason參數中。即使是有錯誤的代碼也不會報錯了,這與咱們的try/catch語句有相同的功能。

all的用法

Promise的all方法提供了並行執行異步操做的能力,而且在全部異步操做執行完後才執行回調。咱們仍舊使用上面定義好的runAsync一、runAsync二、runAsync3這三個函數,看下面的例子:
 
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});
用Promise.all來執行,all接收一個數組參數,裏面的值最終都算返回Promise對象。這樣,三個異步操做的並行執行的,等到它們都執行完後纔會進到then裏面。那麼,三個異步操做返回的數據哪裏去了呢?都在then裏面呢,all會把全部異步操做的結果放進一個數組中傳給then,就是上面的results。因此上面代碼的輸出結果就是:
 
有了all,你就能夠並行執行多個異步操做,而且在一個回調中處理全部的返回數據,是否是很酷?有一個場景是很適合用這個的,一些遊戲類的素材比較多的應用,打開網頁時,預先加載須要用到的各類資源如圖片、flash以及各類靜態文件。全部的都加載完後,咱們再進行頁面的初始化。

race的用法

all方法的效果其實是「誰跑的慢,以誰爲準執行回調」,那麼相對的就有另外一個方法「誰跑的快,以誰爲準執行回調」,這就是race方法,這個詞原本就是賽跑的意思。race的用法與all同樣,咱們把上面runAsync1的延時改成1秒來看一下:
Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});
這三個異步操做一樣是並行執行的。結果你應該能夠猜到,1秒後runAsync1已經執行完了,此時then裏面的就執行了。結果是這樣的:
相關文章
相關標籤/搜索