effective javascript 第二章

来源:互联网 发布:易通网络平台 编辑:程序博客网 时间:2024/05/23 01:19

一、变量声明提升

JavaScript不支持块级作用域,即变量定义的作用域并不是离其最近的封闭语句或代码块,而是包含他们的函数。

理解JavaScript变量声明行为的一个好办法是把变量声明看做由两部分组成,即声明和赋值。JavaScript隐式地提升声明部分到封闭函数顶部,而将赋值留在原地。

变量声明提升也可能导致变量重声明的混淆。在同一函数中多次声明相同的变量是合法的。例如写多个循环时:

function trimSection(header,body,footer){for(var i =0,n = header.length;i<n;i++){header[i] = header[i].trim();}for(var i =0,n = body.length;i<n;i++){body[i] = body[i].trim();}for(var i =0,n = footer.length;i<n;i++){footer[i] = footer[i].trim();}}
trimSections函数好像声明了6个局部变量(3个变量i,3个变量n),但经过变量声明的提升后其实只声明了2个,相当于:
        for(var i =0,n = body.length;i<n;i++){body[i] = body[i].trim();}for(var i =0,n = footer.length;i<n;i++){footer[i] = footer[i].trim();}
因此我们需要通过手动提升变量将所有的var声明放置在函数的顶部,避免歧义。

JavaScript没有块级作用域的一个例外是其异常处理。try...catch语句将捕获的异常绑定到一个变量,该变量的作用域只是catch语句块:

function test(){var x = "var",result = [];result.push(x);try{throw "exception";}catch(x){x = "catch";}result.push(x);return result;}test();//["var","var"]
二、使用立即调用的函数表达式创建局部作用域
以下程序为何会输出undefined?

function wrapElements(a){var result = [],i,n;for(i = 0,n = a.length;i<n;i++){result[i] = function(){return a[i];};}return result;}var wrapped = wrapElements([10,20,30,40,50]);var f = wrapped[0];f();//undefined
函数在运行时进入一个作用域,JavaScript会为每一个绑定到该作用域的变量在内存中分配一个“槽”(slot)。wrapElements函数绑定了三个局部变量:result、i和n。因此,当它被调用时,函数会为这三个变量分配“槽”。在循环的每次迭代中,循环体都会为嵌套函数分配一个闭包。该函数存储的不是嵌套函数创建时变量i的值,而是变量i的引用。由于每次函数创建后变量i的值都发生了变化,因此内部函数最终看到的是变量i最后的值。值得注意的是,闭包存储的是其外部变量的引用而不是值。

所以,所有由wrapElements函数创建的闭包都引用在循环之前创建的变量i的同一个共享“槽”。由于每次循环迭代都递增变量i知道运行到数组结束,因此,这时候其实当我们调用其中任何一个闭包,它都会查找数组的索引5并返回undefined值。

解决的办法是通过创建一个嵌套函数并立即调用它来强制创建一个局部作用域。

function wrapElements(a){var result = [],i,n;for(i = 0,n = a.length;i<n;i++){(function(){var j = i;result[i] = function(){return a[j];};})();}return result;}
但是,使用这种方式创建局部作用域要小心,代码块中不能包含跳出块的break语句和continue语句。因为在函数外使用break或continue是不合法的。如果代码块中引用了this或特别的arguments变量,它们的含义将会被改变。




原创粉丝点击