关于JavaScript中var声明变量作用域的推断


Posted in Javascript onDecember 16, 2010

一、迷思!由一段代码引发的疑惑
请看如下代码:

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
输出结果:
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中应该都写了吧。但是啃英文文档。。。还是算了。。自己推断了。哈哈莫见笑莫见笑

Javascript 相关文章推荐
JavaScript将相对地址转换为绝对地址示例代码
Jul 19 Javascript
Javascript自定义函数判断网站访问类型是PC还是移动终端
Jan 10 Javascript
用JS在浏览器中创建下载文件
Mar 05 Javascript
原生JS绑定滑轮滚动事件兼容常见浏览器
Jun 30 Javascript
[将免费进行到底]在Amazon的一年免费服务器上安装Node.JS, NPM和OurJS博客
Aug 18 Javascript
jQuery实现为图片添加镜头放大效果的方法
Jun 25 Javascript
jquery制做精致的倒计时特效
Jun 13 Javascript
对Js OOP编程 创建对象的一些全面理解
Jul 26 Javascript
JS获取当前页面名称的简单实例
Aug 19 Javascript
有关JS中的0,null,undefined,[],{},'''''''',false之间的关系
Feb 14 Javascript
详解前端路由实现与react-router使用姿势
Aug 07 Javascript
Angular移动端页面input无法输入的解决方法
Nov 14 Javascript
jquery中动态效果小结
Dec 16 #Javascript
关于jquery append() html时的小问题的解决方法
Dec 16 #Javascript
Javascript学习笔记二 之 变量
Dec 15 #Javascript
Javascript学习笔记一 之 数据类型
Dec 15 #Javascript
iframe 父窗口和子窗口相互的调用方法集锦
Dec 15 #Javascript
jQuery Ajax使用 全解析
Dec 15 #Javascript
JQuery 应用 JQuery.groupTable.js
Dec 15 #Javascript
You might like
PHP 获取客户端真实IP地址多种方法小结
2010/05/15 PHP
使用phpexcel类实现excel导入mysql数据库功能(实例代码)
2016/05/12 PHP
golang与PHP输出excel示例
2016/07/22 PHP
CodeIgniter框架验证码类库文件与用法示例
2017/03/18 PHP
js 返回时间戳所对应的具体时间
2010/07/20 Javascript
Js event事件在IE、FF兼容性问题
2011/01/01 Javascript
基于jQuery实现下拉框
2014/11/24 Javascript
实例分析javascript中的call()和apply()方法
2014/11/28 Javascript
jQuery中show与hide方法用法示例
2016/09/16 Javascript
js style.display=block显示布局错乱问题的解决方法
2016/09/21 Javascript
详解原生js实现offset方法
2017/06/15 Javascript
深入理解Webpack 中路径的配置
2017/06/17 Javascript
React入门教程之Hello World以及环境搭建详解
2017/07/11 Javascript
使用JS编写的随机抽取号码的小程序
2017/08/11 Javascript
vue.js实现插入数值与表达式的方法分析
2018/07/06 Javascript
Vue 路由 过渡动效 数据获取方法
2018/07/31 Javascript
微信小程序基于picker实现级联菜单
2019/02/15 Javascript
mpvue微信小程序多列选择器用法之省份城市选择的实现
2019/03/07 Javascript
JS面向对象编程实现的Tab选项卡案例详解
2020/03/03 Javascript
vue基于better-scroll实现左右联动滑动页面
2020/06/30 Javascript
nuxt.js添加环境变量,区分项目打包环境操作
2020/11/06 Javascript
Vue实现手机号、验证码登录(60s禁用倒计时)
2020/12/19 Vue.js
django 自定义用户user模型的三种方法
2014/11/18 Python
python交互式图形编程实例(二)
2017/11/17 Python
Django用户认证系统 组与权限解析
2019/08/02 Python
django自带serializers序列化返回指定字段的方法
2019/08/21 Python
在Python中预先初始化列表内容和长度的实现
2019/11/28 Python
python实现飞机大战游戏(pygame版)
2020/10/26 Python
pycharm如何实现跨目录调用文件
2020/02/28 Python
美国名牌香水折扣网站:Hottperfume
2021/02/10 全球购物
Android面试题附答案
2014/12/08 面试题
竞选班干部演讲稿600字
2014/08/20 职场文书
党员教师个人对照检查材料(群众路线)
2014/09/26 职场文书
医院护士党的群众路线教育实践活动对照检查材料思想汇报
2014/10/04 职场文书
商业门面租房协议书
2014/11/25 职场文书
python中的装饰器该如何使用
2021/06/18 Python