关于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 相关文章推荐
不同浏览器的怪癖小结
Jul 11 Javascript
动感效果的TAB选项卡jquery 插件
Jul 09 Javascript
jquery的each方法使用示例分享
Mar 25 Javascript
jQuery简单tab切换效果实现方法
Apr 08 Javascript
Jquery 效果使用详解
Nov 23 Javascript
基于javascript编写简单日历
May 02 Javascript
JS仿京东移动端手指拨动切换轮播图效果
Apr 10 Javascript
基于jQuery封装的分页组件
Jun 26 jQuery
Node.js 使用流实现读写同步边读边写功能
Sep 11 Javascript
详解基于mpvue微信小程序下载远程图片到本地解决思路
May 16 Javascript
JQuery实现简单的复选框树形结构图示例【附源码下载】
Jul 16 jQuery
Angular8 实现table表格表头固定效果
Jan 03 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 fgetcsv 定义和用法(附windows与linux下兼容问题)
2012/05/29 PHP
PHP autoload与spl_autoload自动加载机制的深入理解
2013/06/05 PHP
jQuery+PHP发布的内容进行无刷新分页(Fckeditor)
2015/10/22 PHP
PHP使用数组依次替换字符串中匹配项
2016/01/08 PHP
php 策略模式原理与应用深入理解
2019/09/25 PHP
Js之软键盘实现(js源码)
2007/01/30 Javascript
CSS+Jquery实现页面圆角框方法大全
2009/12/24 Javascript
js 无提示关闭浏览器页面的代码
2010/03/09 Javascript
JavaScript继承方式实例
2010/10/29 Javascript
jQuery Ajax请求状态管理器打包
2012/05/03 Javascript
jquery实现表格奇数偶数行不同样式(有图为证及实现代码)
2013/01/23 Javascript
jquery cookie实现的简单换肤功能适合小网站
2013/08/25 Javascript
浅析Node在构建超媒体API中的作用
2014/07/30 Javascript
浅析javascript 定时器
2014/12/23 Javascript
jQuery常用且重要方法汇总
2015/07/13 Javascript
JavaScript事件类型中焦点、鼠标和滚轮事件详解
2016/01/25 Javascript
Bootstrap基本样式学习笔记之按钮(4)
2016/12/07 Javascript
webstorm+vue初始化项目的方法
2018/10/18 Javascript
基于elementUI实现图片预览组件的示例代码
2019/03/31 Javascript
vue 项目 iOS WKWebView 加载
2019/04/17 Javascript
VUE 实现element upload上传图片到阿里云
2020/08/12 Javascript
关于element-ui表单中限制输入纯数字的解决方式
2020/09/08 Javascript
[01:49]一目了然!DOTA2DotA快捷操作对比第二弹
2014/05/16 DOTA
python动态加载变量示例分享
2014/02/17 Python
在Python中操作字典之setdefault()方法的使用
2015/05/21 Python
Django使用AJAX调用自己写的API接口的方法
2019/03/06 Python
python导包的几种方法(自定义包的生成以及导入详解)
2019/07/15 Python
用python监控服务器的cpu,磁盘空间,内存,超过邮件报警
2021/01/29 Python
AJAX检测用户名是否存在的方法
2021/03/24 Javascript
关于奉献的演讲稿
2014/05/21 职场文书
我们的节日元宵活动方案
2014/08/23 职场文书
2014年外贸业务员工作总结
2014/12/11 职场文书
环卫个人总结
2015/03/03 职场文书
再见,2019我们不负使命;你好,2020我们砥砺前行
2020/01/03 职场文书
Mysql排查分析慢sql之explain实战案例
2022/04/19 MySQL
苹果macOS 13开发者预览版Beta 8发布 正式版10月发布
2022/09/23 数码科技