天天看點

ES6文法

1.let

ES6新增了let指令,用來聲明變量。它的用法類似于var,但是所聲明的變量隻在let指令所在的代碼塊内有效

建立index.html,檔案内容為

<script type="text/javascript">
    {
        var a = 12;
    }
    console.log(a);
</script>           

從console中可以列印出a的值

ES6文法

在ES6中,使用let聲明a的值,然後再次列印a的值,會報錯

<script type="text/javascript">
    {
        let a = 12;
    }
    console.log(a);
</script>           

報錯如下所示,

ES6文法

上面兩個例子中,分别用let和var聲明了兩個變量。然後在代碼塊之外調用這兩個變量,結果let聲明的變量報錯,var聲明的變量傳回了正确的值。這表明,let聲明的變量隻在它所在的代碼塊有效

修改上面的代碼:

<script type="text/javascript">
    {
        var a = 12;
        var a = 20;
    }
    console.log(a);
</script>           

結果如下

ES6文法

同樣的把上面的var改為let,

<script type="text/javascript">
    {
        let a = 12;
        let a = 20;
    }
    console.log(a);
</script>           

重新整理浏覽器後可以看到,抛出的異常又不一樣了

ES6文法

因為let聲明的變量是塊級作用域,不能重複聲明

再次修改上面的代碼

<script type="text/javascript">
    var a = [];
    for(var i=0;i <10;i++){
        a[i] = function () {
            console.log(i);
        };
    }
    a[6]();
</script>           

重新整理浏覽器,得到的結果為:

ES6文法

修改代碼,把var改為let

<script type="text/javascript">
    var a = [];
    for(let i=0;i <10;i++){
        a[i] = function () {
            console.log(i);
        };
    }
    a[6]();
</script>           

再次重新整理浏覽器,得到的結果為:

ES6文法

上面代碼中,變量i是let聲明的,目前的i隻在本輪循環有效,是以每一次循環的i其實都是一個新的變量,是以最後輸出的是6。

如果每一輪循環的變量i都是重新聲明的,那它怎麼知道上一輪循環的值,進而計算出本輪循環的值?這是因為 JavaScript 引擎内部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算

修改代碼

<script type="text/javascript">
    console.log(foo);
    var foo = 2;
</script>           

重新整理浏覽器後,得到的結果為

ES6文法

同樣的,把var改為let,又會出現異常

ES6文法

var指令會發生”變量提升“現象,即變量可以在聲明之前使用,值為undefined。按照一般的邏輯,變量應該在聲明語句之後才可以使用。

為了糾正這種現象,let指令改變了文法行為,它所聲明的變量一定要在聲明後使用,否則報錯。

上面代碼中,變量foo用var指令聲明,會發生變量提升,即腳本開始運作時,變量foo已經存在了,但是沒有值,是以會輸出undefined。變量bar用let指令聲明,不會發生變量提升。

這表示在聲明它之前,變量bar是不存在的,這時如果用到它,就會抛出一個錯誤。

ES5 隻有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。

1.1 内層變量可能會覆寫外層變量。

<script type="text/javascript">
    var tmp = new Date();
    console.log(tmp)
    function f() {
        console.log(tmp);
        if(false){
            var tmp = "hello world"
        }
    }
    f();
</script>           

程式執行結果

ES6文法

上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,内部使用内層的tmp變量。但是,函數f執行後,輸出結果為undefined,原因在于變量提升,導緻内層的tmp變量覆寫了外層的tmp變量。

1.2 用來計數的循環變量洩露為全局變量。

<script type="text/javascript">
    var str = "hello";
    for(var i=0;i < str.length;i ++){
        console.log(str[i])
    }
    console.log(i)
</script>           

執行結果:

ES6文法

2.模闆字元串

傳統的JavaScript語言,輸出很長的資訊時,通常都是使用"+"号進行拼接的。

這種方法相當繁瑣不友善,ES6引入了模闆字元串解決這個問題

模闆字元串(template string)是增強版的字元串,用反引号(`)辨別。它可以當作普通字元串使用,也可以用來定義多行字元串,或者在字元串中嵌入變量

上面代碼中的模闆字元串,都是用反引号表示。如果在模闆字元串中需要使用反引号,則前面要用反斜杠轉義。

ES6文法

3.箭頭函數

ES6允許使用"箭頭"(=>)定義函數

var f = a => a
等同于
var f = function(a){
    return a;
}           

如果箭頭函數不需要參數或需要多個參數,就使用括号代表參數部分

//無參函數
var f = () => 5;
等同于
var f = function(){return 5};

//多個形參的函數
var f = (num1,num2) => num1 + num2
等同于
var f = function(num1,num2){
    return num1 + num2;
}           

使用箭頭函數需要注意的點:

3.1 函數體内的this對象,就是定義時所有的對象,而不是使用時所在的對象

代碼:

<script type="text/javascript">
    var animal = {
        name:"小狗",
        age:3,
        fav:function () {
            // this是使用時定義的對象
            console.log(this);
            console.log(this.name);
        }
    };
    animal.fav();
</script>           
ES6文法

使用箭頭函數定義上面的函數

<script type="text/javascript">
    var animal = {
        name:"小狗",
        age:3,
        fav: ()=>{
            // this指向定義時所在的對象(window)
            console.log(this);
            console.log(this.name);
        }
    };
    animal.fav();
</script>           
ES6文法

3.2 箭頭函數内部不可以使用arguments對象,該對象在函數體内不存在

再次修改上面的代碼,列印函數傳遞的參數arguments

<script type="text/javascript">
    var animal = {
        name:"小狗",
        age:3,
        fav:function () {
            // 沒有使用箭頭函數,可以使用arguments擷取傳遞的參數
            console.log(arguments);
            console.log(this.name);
        }
    };
    animal.fav(2,3,4);
</script>           
ES6文法

使用箭頭函數定義上面的fav函數,再次列印函數傳遞的參數arguments

<script type="text/javascript">
    var animal = {
        name:"小狗",
        age:3,
        fav: () => {
            // 使用箭頭函數時,arguments無法使用
            console.log(arguments);
            console.log(this.name);
        }
    };
    animal.fav(2,3,4);
</script>           

執行結果

ES6文法

4. 對象的單體模式

為了解決箭頭函數this指向的問題 推出來一種寫法 對象的單體模式

程式執行結果:

ES6文法

5. 面向對象

5.1 構造函數的方式建立對象

<script type="text/javascript">
    function Animal(name, age) {
        this.name = name;
        this.age = age;
    }
    Animal.prototype.showName = function () {
        console.log(this.name)
    }
    var dog = new Animal("dog", 2);
    console.log(dog.name);
    console.log(dog.age);
</script>           

上面這種寫法跟傳統的面向對象語言(比如 Java 和 python)差異很大,很容易讓新學習這門語言的程式員感到困惑。

ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模闆。

通過class關鍵字,可以定義類。ES6 的class可以看作隻是一個文法糖,它的絕大部分功能,ES5 都可以做到,新的class寫法隻是讓對象原型的寫法更加清晰、更像面向對象程式設計的文法而已。

5.2 class建立對象

修改上面的代碼,用class建立對象

<script type="text/javascript">
    class Animal {
        // 類似于python中的__init__方法
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }

        showName() {
            console.log(this.name);
        }
    }
    var d = new Animal("dog", 3);
    d.showName();
</script>           
ES6文法

上面代碼定義了一個“類”,可以看到裡面有一個constructor方法,這就是構造方法,而this關鍵字則代表執行個體對象。

也就是說,ES5 的構造函數Animal,對應 ES6 的Animal類的構造方法。

Animal類除了構造方法,還定義了一個showName方法。注意,定義"類"的方法的時候,前面不需要加上function這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗号分隔,加了會報錯。

上面代碼表示,類本身就指向了類的構造函數。

使用的時候,也是直接對類使用new指令,跟構造函數的用法完全一緻。

5.3 constructor方法

constructor方法是類的預設方法,通過new指令生成對象執行個體時,自動調用該方法。

一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被預設添加。

class Animal {
}

// 等同于
class Animal {
  constructor() {}
}           

上面代碼中,定義了一個空的類Point,JavaScript 引擎會自動為它添加一個空的constructor方法。