读<你不晓得的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);
我们都晓得个中的a
和b
的作用域查找(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
。然则我在chrome
和firefox
中试了一下,会根据想要的输出1
。而在IE11
中,却输出的是2
。
所以,很明显,chrome
和firefox
对这类状况作了优化,然则照样有浏览器的差异,所以只管不要这么声明函数。防止不必要的bug
涌现。
渣滓网络
当运用闭包的时刻,会形成一部分的内存不会举行开释。这是闭包的优点,也是闭包的害处。优点就是我们能够在外部运用这个变量,而害处就是有些不须要的变量也不会被开释。
一个简朴的闭包:
function Foo(){
var a = [....];
// .....和a做些互动
var b = 0;
return function(){
return b++;
}
}
这里在闭包中,我们实在只须要保留b
的内存就好,而a
是不须要的。根据抱负状况,a
在Foo
完毕后就能够渣滓回收了(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
这个关键词,也是块状作用域,也是不提拔。