- let与var
在js中声明一个变量除了一个var 还有一个let的声明。对于var 在前面的作用域中已经讲过,这次主要说下二者的区别:
在MDN上有这样的一个demo:
var list = document.getElementById("list"); for (var i = 1; i <= 5; i++) { var item = document.createElement("LI"); item.appendChild(document.createTextNode("Item " + i)); let j = i; item.onclick = function (ev) { console.log("Item " + j + " is clicked."); }; list.appendChild(item); }
上面这段代码的意图是创建5个li,点击不同的li能够打印出当前li的序号。如果不用let,而改用var的话,将总是打印出 Item 5 is Clicked,因为 j 是函数级变量,5个内部函数都指向了同一个 j ,而 j 最后一次赋值是5。
对于上面的结果如果不使用let的话,也是有办法破的。方法就是用一个闭包把j的作用域给包起来。修改如下:
<script> var list = document.getElementById("list"); for (var i = 1; i <= 5; i++) { var item = document.createElement("LI"); item.appendChild(document.createTextNode("Item " + i)); var j = i; item.onclick = (function(j){ return function () { console.log("Item " + j + " is clicked."); } })(j); list.appendChild(item); } </script>
上面的是采用的闭包的方式在绑定的时候已经把j的值已经传递给对应的click事件了,所以能够实现相同的结果。如此很是精妙!但是对于程序的易性来说是一个不小的考验。
下面开始说说let为什么能够实现如此的结果:
let
允许把变量的作用域限制在块级域中。与 var
不同处是:var 申明变量要么是全局的,要么是函数级的,而无法是块级的。再看几个官方的demo.
在程序或者函数的顶层,let 的表现就象 var 一样:
function varTest() { var x = 31; if (true) { var x = 71; // same variable! console.log(x); // 71 } console.log(x); // 71 } function letTest() { let x = 31; if (true) { let x = 71; // different variable console.log(x); // 71 } console.log(x); // 31 }
上面的结果说明了let只在{}内使用。说到这里对开篇的那个问题就容易解释了。
用了let后,j 变成块级域(也就是花括号中的块,每进入一次花括号就生成了一个块级域),所以 5 个内部函数指向了不同的 j 。如果两个定义都在最外层则效果是一样,如下:
var x = ‘global‘; let y = ‘global‘; console.log(this.x); console.log(this.y);
- let 的暂存死区与错误
在同一个函数或同一个作用域中用let重复定义一个变量将引起 TypeError.
if (x) { let foo; let foo; // TypeError thrown. }
然而,在变量声明之前引用这个变量会导致一个 ReferenceError的结果, 因为let变量 在"暂存死区" (从块的开始到声明这段).
function do_something() { console.log(foo); // ReferenceError let foo = 2; }
在 switch 申明中你可能会遇到这样的错误,因为一个switch只有一个作用块.
switch (x) { case 0: let foo; break; case 1: let foo; // TypeError for redeclaration. break; }
循环体中是可以引用在for申明时用let定义的变量,尽管let不是出现在大括号之间.
var i=0; for ( let i=i ; i < 10 ; i++ ) { console.log(i); }
let
块
提供了一种在块的范围内获取变量的值,而不会影响块外面名字相同的变量的值的方法。
var x = 5; var y = 0; let (x = x+10, y = 12) { console.log(x+y); // 27 } console.log(x + y); // 5
总结一下,对于let来说在某些地方确实有其长处的地方,同样也弥补了的js中块作用域的空白。