关于let的随想

读<你不晓得的javascript>,想到那里写到那里。。。

块状作用域

提起ES6中的let关键字,第一个主意就是块状作用域。

说到作用域,之前说起的都是全局作用域和函数作用域。当举行作用域查找的时刻,永远是一层一层往上查找,直到找到第一个婚配的标示符时住手。

全局作用域就是全部作用域的最上层,当举行作用域查找的时刻,全局作用域永远是最上一层,假如在全局作用域中依旧没法找到,那末就会抛出referenceError

而说到函数作用域,曾读过一本书上面说到一个词“隐蔽”变量和函数定义,觉得很贴切。把东西隐蔽在函数中,防止污染外部作用域的同时,能够有效地举行模块化治理。

回到块状作用域,除了let,别的一个with关键字也制造了一个块状作用域。

function foo(obj) {
    var c = 6;
    with(obj) {
       a = b;
       c = 5;
       var d = 7;
       let e = 8;
    }
    console.log(c); // 5
    console.log(d); // 7
    console.log(e); // referenceError
}
var obj = { a:1, b:2 };
foo(obj);

我们都晓得个中的ab的作用域查找(LHS, RHS)都是在obj中找到。而假如没法再obj中找到,那末就会向上作用域继承找,这里的c就会找到foo函数中的c。而假如经由过程var这些关键词声明的,那末也就会声明到with地点的作用域中。

关于with,就相当于在表面又包了一层作用域,内里包括供应对象中的一切属性。

终究回到let, let能够将变量绑定到地点的恣意作用域中,通常是{}内部。没了。。。

提拔

var声明变量和函数声明,let不会举行提拔。看看下面的几个例子:

console.log(a); // referenceError
let a = 1;
console.log(b); // undefined;
var b = 1;
foo(); // 3
function foo(){ console.log("3") }
foo(); // type error
var foo = function(){ console.log("4") }
foo(); // referenceError
let foo = function(){ console.log("5") }

说到提拔,提一句:“函数会起首被提拔,然后才是变量”。像之前的书里的一个例子:

bar(); // 3
function bar(){ console.log(1) };

var bar = function(){ console.log(2) };

function bar(){ console.log(3) }

提拔今后看起来就像:

function bar(){ console.log(1) };
function bar(){ console.log(3) };
bar(); // 3
bar = function(){ console.log(2) };

这里须要提一句,var bar这一段因为bar在之前已被声明过了,所以这里var bar会被疏忽,而不是bar设为null

说到提拔,另有一种特别的状况,虽然不发起这么写,然则肯定会有人会踩到雷:

var a = true;
if(a) {
  function foo(){ console.log(1) }
} else {
  function foo(){ console.log(2) }
}
foo();

原书中说这类状况,因为提拔,所以会输出2,而不是我们想要的1。然则我在chromefirefox中试了一下,会根据想要的输出1。而在IE11中,却输出的是2

所以,很明显,chromefirefox对这类状况作了优化,然则照样有浏览器的差异,所以只管不要这么声明函数。防止不必要的bug涌现。

渣滓网络

当运用闭包的时刻,会形成一部分的内存不会举行开释。这是闭包的优点,也是闭包的害处。优点就是我们能够在外部运用这个变量,而害处就是有些不须要的变量也不会被开释。

一个简朴的闭包:

function Foo(){
   var a = [....];
   // .....和a做些互动
   var b = 0;
   return function(){
      return b++;
   }
}

这里在闭包中,我们实在只须要保留b的内存就好,而a是不须要的。根据抱负状况,aFoo完毕后就能够渣滓回收了(GC)。但是引擎有可能会保留着这个东西(取决于详细完成–你不晓得的javascript),这就形成了内存的糟蹋。

那末就能够运用let块状作用域的特征,举行处置惩罚:

function Foo(){
   {
      let a = [....];
      // ....和a做些互动
   }
   var b = 0;
   return function(){
      return b++;
   }
}

这里,因为a的作用域只在{}中,所以当{}完毕后就会开释a的内存,而不是久长占领内存。

轮回

这里只写出一个很有意义的看法。

我们都晓得在轮回顶用let能够处理某个陈词滥调的题目。然则老是改变不过来,为何会处理这个题目。

var a = [];
for(var i = 0; i < 10; i++) {
   a[i] = function(){ console.log(i) };
}
a[3](); // 10
var a = [];
for(let i = 0; i < 10; i++) {
   a[i] = function(){ console.log(i) };
}
a[3](); // 3

所以究竟let做了什么奇异的事变,处理了这个题目?

书中说了一句话:

for轮回头部的let不仅将i绑定到了for轮回的块中,事实上它将其从新绑定到了轮回的每一个迭代中,确保运用上一个轮回迭代完毕时的值从新举行赋值。

然后给了一个等价的例子,一览无余:

var a = [];
{
   let j;
   for(j=0; j<10;j++){
     let i = j; // 每一个迭代从新绑定
     a[i] = function(){ console.log(i) };
   }
}
a[3](); // 3

末了烦琐一句,const这个关键词,也是块状作用域,也是不提拔。

    原文作者:Clark
    原文地址: https://segmentfault.com/a/1190000009021044
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞