浏览 2781 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-12-15
一、迷思!由一段代码引发的疑惑 请看如下代码: for(var i=0;i<3;i++) { console.log(j+","+k); for(var j=0;j<3;j++) { var k = j+1; } } console.log(i); 输出结果: undefined,undefined 3,3 3,3 3 如果你是搞c、java等语言的,可能你会不解,为何j、k这种局部变量可以被作用域外的代码访问呢? 如果JavaScript中用var声明的变量可视为局部变量,那么能访问到这个变量的作用域就是这个变量的局部作用域。如上例,在console.log行处,依然有j、k的作用域,而循环外,依然有i的作用域。说到这里,也许我可以武断的说,JavaScript没有真正意义的局部作用域。真的吗?非也! 二、如何获得真正的局部作用域呢?一个写法引起了我的注意 大家也许看过JQuery的源码或者Ext的源码,也许会对下面的写法有点熟悉。 var a = 3,b=4; var exports = (function() { var a = 1,b=2; return {a:a,b:b}; })(); console.log(""+a+","+b); console.log(exports.a+","+exports.b); 输出结果: 3,4 1,2 很神奇的发现(其实也不神奇,大家都知道啦)函数内部是有独立作用域的,即函数内部var声明的变量,仅在函数内部可以使用。所以各框架各大师都这么写,防止自身局部变量与外界变量(外层局部变量与全局变量)冲突。 至此,我收回第一条里的武断推断,修改一下: JavaScript以函数为界,每个函数内部拥有一个局部作用域;任何其他的块(包括普通代码块,for循环、if、while等代码块)不存在局部作用域,使用var声明的变量可以直接穿过这些代码块,可以被外部代码访问到。 三、何时报错,何时undefined?var的声明机制 看代码: console.log(a) 输出结果: ReferenceError: a is not defined console.log(a); var a = 3; 输出结果: undefined var exports = (function() { var a = 1,b=2; return {a:a,b:b}; })(); console.log(a); 输出结果: ReferenceError: a is not defined 猜想结论: 每次JavaScript引擎执行代码时,会先扫描作用域中的所有代码(作用域中的function内部的代码不会扫描),并将所有var声明的变量记录下来,在代码执行到赋值之前,这些变量的值为undefined。此后如果访问变量时,先访问局部变量,如果没有这个局部变量就访问上一层的局部变量(如为闭包,上一层为闭包创建环境),直到访问到完全局变量。如果都没有这个变量,那么抛出异常。 四、题外话:闭包+异步,变量值错乱!如何确保异步情况下局部变量当前值的传递? 还是代码说话: for(var i=0;i<3;i++) { setTimeout(function() { console.log(i); },1); } 输出结果: 3 3 3 为何?因为在闭包异步执行的时候,i始终访问的是外层作用域的i,由于异步了,所以在执行闭包的时候循环已经结束了,i已经为3了,故每一次打印出来的都是3。 那如何解决这个问题呢?我们需要把i转换成局部变量。 嗯,有人会有这种写法: for(var i=0;i<3;i++) { var j = i; setTimeout(function() { console.log(j); },1); } 输出结果: 2 2 2 为何? 其实之前已经解释过了,其实j和i的作用域是一样的。都是外层局部变量,在异步情况下循环执行完成的时候j为2(比i少一次i++); 那该怎么办呢?(请想象某广告,(⊙v⊙))。 大家知道,函数中的参数也算函数的局部变量。那么这里有一个办法,可以将局部变量转换为函数的实参,这样就达到值传递的效果了。 for(var i=0;i<3;i++) { setTimeout(( function(j){ return function() { console.log(j); } })(i) ,1); } 输出 0 1 2 其实说了这么多,代码写出来大家就差不多明白了吧,用这种匿名函数的方式去除了异步情况下变量变化的问题,不过此为本贴的题外话了。 总结: 额。不写了,我懒,哪天抽空补上。嘿嘿。 其实这些结论RFC中应该都写了吧。但是啃英文文档。。。还是算了。。自己推断了。哈哈莫见笑莫见笑 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-12-15
强人啊,你几乎将与变量相关的处理流程逆向出来了,去做开荒/预研什么的肯定很拿手……
JS的规范是ECMAScript,这里有我对ECMAScript中变量处理流程部分章节的理解:http://www.iteye.com/topic/812668#1758994 |
|
返回顶楼 | |
发表时间:2010-12-16
1楼眼光确实犀利。。。我确实对开荒比较感兴趣,呵呵。可惜现实项目中不给我机会开荒,只好自己给自己找目标开荒啦 ~~
另外http://www.iteye.com/topic/812668#1758994写的挺透彻啊,我觉得我该看看ECMAScript了,也许对JS理解的瓶颈就在这了,毕竟通过经验理解上来的东西多少可能还是有一点误差的~ |
|
返回顶楼 | |
发表时间:2010-12-23
解决了我的疑问
|
|
返回顶楼 | |