通俗易懂理解ES6 - 變量聲明命令及其塊級做用域

引言

萬丈高樓平地起,欲練此功,必先打好基本功: )。瀏覽器

ES6已經融入到了咱們的平常開發當中,甚至一些ES7的特性也已經在廣泛使用,但因爲瀏覽器的支持問題,ES6的代碼在大多數狀況下,仍是須要編譯成ES5才能正常地在各瀏覽器上正常運行。
ES6支持6中形式的變量命名方式:varletconstfunctionclassfunction,本文主要討論的是varletconstclassfunction會在以後專門討論。app

變量聲明

在ES5裏面,咱們要聲明一個變量,會經過三種命令方式來實現,var隱式聲明functionide

var a = 'test';     //var定義一個變量a,並賦值字符串'test';
b = 'test2';        //隱式聲明,在堆變量b賦值字符串'test2'前,未以任何方式聲明變量b,此時b爲全局變量
function f() {
    cosole.log('haha');
}


隱式聲明聲明變量是一個很很差的行爲,隱式聲明的變量相似於經過var定義一個變量,且變量的做用域直接指向window對象,這會致使變量容易被錯誤引用或修改函數

function f() {
    var fn = function() {
        (function() {
            b = '隱式聲明的變量b';
        })();
    }
    fn();
}
f();
b;          //'隱式聲明的變量b'
window.b;   //'隱式聲明的變量b'


ES6-變量聲明的let和const

而在ES6中,變量聲明增長了letconst兩種形式,先看如下例子:ui

console.log(a);     //無報錯,輸出undefined
var a = 'test';
var a = 'var again';

經過var聲明的變量存在變量提高一說,同一變量名在相同做用域下能重複定義,上述代碼執行時會以如下情形執行:先定義一個變量var a,console.log(a),a = 'test';code

console.log(son);     //報錯 Uncaught ReferenceError: son is not defined
let son = 'James Bond';

let聲明變量不存在變量提高,所以在son被定義前使用son的話,會報錯對象

const name;            //報錯 Uncaught SyntaxError: Missing initializer in const declaration
let education;              //正常執行
console.log(education);     //undefined

const name1 = 'human';
name1 = 'cat';         //報錯 Uncaught TypeError: Assignment to constant variable.

經過const定義的nameobj是一個常量,在聲明時必須對其進行賦值,且賦值後不能進行二次賦值,而let聲明的變量在聲明時可不賦值,後面再賦值,此時默認值爲undefined遞歸

const obj = {};
obj;                //{}
obj.a = '1';
obj;                //{a:1}

const 定義的obj在常量中存儲的是obj對象在裏指向的地址,該指向地址不可改變,而在地址裏面存放的數據不被約束,是可變的。若但願禁止變動obj及其內部屬性,Object提供了freeze方法,以下函數能更好的遞歸凍結Object對象ip

const freezeObj = (obj) => {
    Object.freeze(obj);
    Object.keys(obj).forEach((item) => {
        if(type of(item) === 'object') freezeObj(item);
    });
}


let fruit = 'orange';
var fruit = 'apple';    //報錯 Uncaught SyntaxError: Identifier 'fruit' has already been declared

經過let定義的變量在同一做用域下不容許被重複定義,不然會報錯,const也是如此作用域

var test = 'test',
    test1 = 'test1';
{
    test = 'new test';      //Uncaught ReferenceError: test is not defined
    test1 = 'new test1';    //Uncaught ReferenceError: test1 is not defined
    let test;
    const test1 = 'new test1';
}

經過let、const聲明的變量會存在 __暫時性死區__,即:在聲明該變量的做用域內,變量已經被綁定在該做用域之中,忽略外界存在的同名變量,在該做用域下,該變量在變量聲明以前都不能被使用。


變量聲明結論

var:

  1. 存在變量提高,可重複屢次聲明同名變量。

let:

  1. 不存在變量提高,必須先定義再使用,不然報錯;
  2. 同一做用域下不可重複定義同名變量,不然報錯;
  3. 在代碼塊內,經過let聲明的變量,儘管代碼塊外層有同名變量,代碼塊內部在該變量聲明前都不能使用該變量,不然報錯。

const:

  1. 不存在變量提高,必須先定義再使用,不然報錯;
  2. 同一做用域下不可重複定義同名變量,不然報錯;
  3. 建立變量的同時必須對其賦值,且賦值後不能直接經過=直接替換整個值不然報錯;
  4. 在代碼塊內,經過const聲明的變量,儘管代碼塊外層有同名變量,代碼塊內部在該變量聲明前都不能使用該變量,不然報錯。

letconst的出現,很好地把ES5中var定義變量的Bug給埋了,解決了定義同名變量致使變量間相互污染的問題,保證了同一塊級做用域下,變量名的惟一性。同時const定義常量能更直觀地明白常量的意義及其不可修改性。


塊級做用域

ES6新增的變量聲明命令存在塊級做用域

什麼是塊級做用域?

{
    var a = 'test';
    let b = 'test1';
}
console.log(a);     //test
console.log(b);     //Uncaught ReferenceError: b is not defined

這即是塊級做用域最基本的示例,經過letconst聲明的變量僅能在其代碼塊內被使用,該代碼塊內部便是一個塊級做用域

塊級做用域有什麼用?

var a = 'test';
function f(){
    console.log(a);
    if(true) {
        var a = 'test1';
    }
    console.log(a);
}
f();        //undefinded   test1

這個例子輸出的結果是undefined,緣由在於,在f()中,不論if語句判斷是否經過,if內部的var a都被變量提高,且變量a均經過var命令聲明,內部變量a覆蓋了外部變量a;

換個寫法再來一遍

let a = 'test';
function f(){
    console.log(a);
    if(true) {
        let a = 'test1';
    }
    console.log(a);
}
f();        //test   test

比對一下兩段代碼的執行狀況:
看看該代碼的實際執行狀況:

var a                               let a
a = 'test'                          a = 'test'
function f                          function f
f() = {}                            f() = {}
f()                                 f()
var a                               console.log(a);
console.log(a)                      if(true)
if(true)                            let a        //此處的a是另外一個a,能夠理解爲_a,且_a的做用範圍僅在if語句內
a = 'test1'                         a = 'test1';    //相似於_a = 'test1'
console.log(a)                      console.log(a)  //相似於console.log(_a)

從上面的比對能夠看出,經過let聲明的變量a,不會被變量提高,且具備塊級做用域,不會影響到上層做用域的a變量

再來一個示例:

for(var i=0;i<10;i++){
    console.log('for裏面的i:'+i);          // 一、二、3......10
}
console.log(i);            //10

這裏定義的循環計數變量i,本來只是想在循環內使用,但因爲使用了var聲明,所以不存在塊級做用域,致使for循環外也能獲取到了i的值。

ES6中規定,函數自己的做用域在其所在的塊級做用域當中。

function fn() {
    console.log('outside console');
}
(function() {
    if(false) {
        function fn() {
            console.log('inside console');
        }
    }
    fn();
}());

上述代碼中,fn()執行結果會報錯fn is not a function,由於在fn執行的環境中存在function fn的聲明,聲明被提早,致使fn被提早定義,但沒有被賦值爲function。

ES6的函數自執行代碼變得精簡。

//從
(function() {
    console.log('test');
})();

//變爲

{
    console.log('test');
}

塊級做用域結論

經過使用let const,讓變量存在了塊級做用域,很好地劃分變量間的做用範圍,避免了以往同名變量相互污染問題;外層變量沒法直接讀取內層變量,對內層變量具備更好的保密性,內外層代碼獨立執行,相互間不影響;精簡了函數自執行代碼

以上。

文章觀點內容若有錯誤歡迎指出交流,相互進步

相關文章
相關標籤/搜索