2018-12-20-周末學習

ES6的深入認識(1)

let&const

首先列出我對于let和cons起初粗淺的認識

  • let ≈ var,const只是定義一個不可變常量
  • 二者均不支持提升

學習總結(jié)后的一些認識:
(1) var在全局創(chuàng)建的變量,全局各處均可以訪問到,let則不是,如果在代碼塊中創(chuàng)建,則只能在代碼塊中訪問。

驗證:
第一點認識起初不是很明朗,于是進行了如下的驗證:

var arr01 = [],
        arr02 = [];

    for(let i = 0; i < 5; i++) {
      arr01.push(function() {
        console.log('i in arr01', i);
      });
    }

    for(var i = 0; i < 5; i++) {
      arr02.push(function() {
        console.log('i in arr02', i);
      });
    }

    arr01.forEach(item => item());
    arr02.forEach(item => item());

結(jié)果

image.png

結(jié)論:很明顯,var創(chuàng)建的i一輪又一輪被重新賦值,到最后我們執(zhí)行打印函數(shù)的時候,全局的i只等于最后循環(huán)結(jié)束時被賦予的值了,前面的值均已經(jīng)被覆蓋掉,let創(chuàng)建的i則健全的保留了每一次創(chuàng)建時被賦予的新值。

(2) var創(chuàng)建的變量,在循環(huán)中始終保持只有一個變量被循環(huán)覆蓋式賦值,而let在循環(huán)中每一次都會創(chuàng)建一個新的變量,賦予新值(js的內(nèi)部引擎會記住上一輪循環(huán)的值,初始化本輪的變量i時,就在上一輪循環(huán)的基礎(chǔ)上進行計算。)

(3)暫時性死區(qū)
在代碼塊中,如果我們在沒有用let或者const去聲明一個變量之前就使用了該變量,那么就會報引用錯誤。換一句話說,也就是在進入當前作用域時,需要使用的變量就已經(jīng)存在,但是不可獲取,只有在遇到聲明該變量的這一行代碼出現(xiàn)之后,才可以正常的使用。

(4)塊級作用域存在的意義
內(nèi)層變量可能會覆蓋外層變量
先解讀一段代碼

var tmp = new Date();
    function f() {
      console.log(tmp);
      if (false) {
        var tmp = 'hello world';
      }
    }

    f(); // undefined

起初并不明白為什么會返回Undefined,我最開始認為應當返回new Date(),但是后來發(fā)現(xiàn)var是存在變量提升的,所以上述代碼應當解讀為:

var tmp = new Date();
    function f() {
      var tmp = undefined;
      console.log(tmp);
      if (false) {
        tmp = 'hello world';
      }
    }
    f(); // undefined

用來計數(shù)的循環(huán)變量泄露為全局變量
同樣先讀一段代碼

    var s = 'hello';
    for (var i = 0; i < s.length; i++) {
      console.log(s[i]);
    }
    console.log(i); // 5

在假設(shè)不考慮性能的前提下,如果我有兩個以上的循環(huán),

for() {
    //...
}
for() {
    //...
}
for() {
    //...
}
//...

那么我一個頁面要設(shè)置多個計數(shù)的循環(huán)變量。但是如果換做是let,那么情況就發(fā)生變化了。

var s = 'hello';

    for (let i = 0; i < s.length; i++) {
      console.log(s[i]);
    }

    for(let i = 5; i < 10; i++) {
      console.log('第二個循環(huán)', i);
    }

    for(let i = 10; i < 15; i++) {
      console.log('第三個循環(huán)', i);
    }
    console.log(i); // 5

結(jié)果

image.png

我不用再考慮計數(shù)變量會泄露到全局了。

(5)ES6的塊級作用域

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

起初看這一段代碼的時候,會有一些疑問,前面的學習告訴我let是不能重復對一個變量進行聲明的,這一段代碼必然會報錯,但是運行之后沒有報錯甚至打印了5,這就說明函數(shù)中不僅有函數(shù)自己的局部作用域,還有if代碼塊中的塊級作用域。
此時,就不得不聯(lián)想到es5中常年為了躲避變量泄露二使用到的IIFE(Immediately-invoked-function-expression),哇,很方便了。

// before
(function(){
var temp = 'hello';
})();

// after
{
    let temp = ...;
    ...
}

(6)塊級作用域與函數(shù)聲明
在學習過程中,碰到這么一段代碼,說在es5環(huán)境下運行和es6環(huán)境下運行是不一樣的,前者環(huán)境下會執(zhí)行內(nèi)部函數(shù),后者則會視執(zhí)行環(huán)境的表現(xiàn)而定。

function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重復聲明一次函數(shù)f
    function f() { console.log('I am inside!'); }
  }

  f();
}());

果然,在Chrome中執(zhí)行結(jié)果如下:


image.png

這么一看,與理論上es6的塊級作用域表現(xiàn)不符,我們期待的是會打印出外層函數(shù)的outside,但是這里的表現(xiàn)說明,我們的函數(shù)沒有做到es5的提升,即

function f() { console.log('I am outside!'); }

(function () {
  // 重復聲明一次函數(shù)f
    function f() { console.log('I am inside!'); }
  }
  if (false) {
    

  f();
}());

那具體是怎么回事呢,在查閱資料之后發(fā)現(xiàn),es6的附錄中規(guī)定了瀏覽器的表現(xiàn)可以不遵守理論上的規(guī)定,它可以有自己的表現(xiàn)方式,如下:

  • 允許在塊級作用域內(nèi)聲明函數(shù)。
  • 函數(shù)聲明類似于var,即會提升到全局作用域或函數(shù)作用域的頭部。
  • 同時,函數(shù)聲明還會提升到所在的塊級作用域的頭部。

那么上述代碼的解讀就變成了:

function f() { console.log('I am outside!'); }

    (function () {
      var f = undefined;
      if (false) {
        // 重復聲明一次函數(shù)f
        function f() { console.log('I am inside!'); }
      }

      f();
    }());

另外,還有一個需要注意的地方。ES6 的塊級作用域允許聲明函數(shù)的規(guī)則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。

(7)const保證不變的并非是值
以前的學習只會粗淺的認為const會保證一個值不變,但是直到我遇到const設(shè)置對象為常量。

const foo = {};
foo.name = 'foo';
console.log('foo', foo);
foo.name = 'boo';
console.log('foo', foo);

foo = {};

結(jié)果

image.png

原來const只能保證設(shè)置的值指向的內(nèi)存指針是不變的,而這個內(nèi)存地址縮儲存的值為多少const并不關(guān)心,所以上述代碼中name屬性如何變又或是foo的對象屬性增或減與const無關(guān),因為foo所在的內(nèi)存地址始終沒有變過。

結(jié)論:使用const去聲明一個不變的對象時需要格外的謹慎

那么,我們真的想要一個始終都不會變的對象,又該怎么辦呢?Object有一個方法叫做freeze

const foo = Object.freeze({});
    foo.name = 'foo';
    console.log('foo', foo);
    foo.name = 'boo';
    console.log('foo', foo);

    foo = {};

結(jié)果

image.png

嚴格模式下:


image.png

可是,這就結(jié)束了嗎?我們以前復制對象的時候回去考慮深淺拷貝的問題,那么這里修改的時候,會不會也考慮到層級深淺的問題呢?于是就出現(xiàn)了以下的代碼(函數(shù)聲明或是表達式都可以)

var freezeComplete = (obj) => {
      Object.freeze(obj);
      Object.keys(obj).forEach( (key, i) => {
        if ( typeof obj[key] === 'object' ) {
          freezeComplete( obj[key] );
        }
      });
      return obj;
    };
    const foo = freezeComplete({
      person: {},
      otherKeys: 'otherKeys'
    });

    foo.person.name = 'foo';
    foo.person.height = '1.8';
    console.log('foo', foo);
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • let 命令 塊級作用域 const 命令 頂層對象的屬性 global 對象 let 命令 基本用法 ES6 新...
    嘉奇呦_nice閱讀 1,695評論 0 2
  • let 和 const 命令 let 命令 塊級作用域 const 命令 頂層對象的屬性 gl...
    安小明閱讀 1,043評論 0 0
  • let 命令 塊級作用域 const 命令 頂層對象的屬性 global 對象 let 命令 基本用法 ES6 新...
    卞卞村長L閱讀 683評論 0 0
  • 特別說明,為便于查閱,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 628評論 0 0
  • 幾天前,漫威又一部巨作《毒液:致命守護者》正式上映。我作為漫威的忠實粉絲,一到周末就拉著好友走進了電影院。進去的那...
    愛德CHY閱讀 563評論 0 5

友情鏈接更多精彩內(nèi)容