关于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 Distilled 基础知识与函数
Apr 07 Javascript
js中创建对象的几种方式示例介绍
Jan 26 Javascript
JavaScript生成带有缩进的表格代码
Jun 15 Javascript
JS及PHP代码编写八大排序算法
Jul 12 Javascript
JavaScript遍历Json串浏览器输出的结果不统一问题
Nov 03 Javascript
jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
Dec 22 Javascript
在js代码拼接dom对象到页面上去的模板总结(必看)
Feb 14 Javascript
Vue.js 中的 $watch使用方法
May 25 Javascript
详解vue2.0 使用动态组件实现 Tab 标签页切换效果(vue-cli)
Aug 30 Javascript
用jquery获取select标签中选中的option值及文本的示例
Jan 25 jQuery
vue实现点击关注后及时更新列表功能
Jun 26 Javascript
ES6知识点整理之函数对象参数默认值及其解构应用示例
Apr 17 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服务器实现多session并发运行
2006/10/09 PHP
php 字符过滤类,用于过滤各类用户输入的数据
2009/05/27 PHP
php使用百度翻译api示例分享
2014/01/31 PHP
php文件管理基本功能简单操作
2017/01/16 PHP
Laravel框架实现抢红包功能示例
2019/10/31 PHP
解决extjs在firefox中关闭窗口再打开后iframe中js函数访问不到的问题
2008/11/06 Javascript
JavaScript模板入门介绍
2012/09/26 Javascript
Javascript玩转继承(二)
2014/05/08 Javascript
javascript中with()方法的语法格式及使用
2014/08/04 Javascript
复杂的javascript窗口分帧解析
2016/02/19 Javascript
JS简单实现String转Date的方法
2016/03/02 Javascript
JavaScript知识点总结(五)之Javascript中两个等于号(==)和三个等于号(===)的区别
2016/05/31 Javascript
详解jQuery同步Ajax带来的UI线程阻塞问题及解决办法
2017/08/09 jQuery
js实现canvas保存图片为png格式并下载到本地的方法
2017/08/31 Javascript
vue脚手架搭建过程图解
2018/06/06 Javascript
vue实现的组件兄弟间通信功能示例
2018/12/04 Javascript
Jquery遍历筛选数组的几种方法和遍历解析json对象,Map()方法详解以及数组中查询某值是否存在
2019/01/18 jQuery
vue cli 3.x 项目部署到 github pages的方法
2019/04/17 Javascript
vue实现移动端触屏拖拽功能
2020/08/21 Javascript
[02:12]Dota 2 推出全新英雄—— 电炎绝手
2019/08/23 DOTA
Pyramid Mako模板引入helper对象的步骤方法
2013/11/27 Python
Python正则表达式如何进行字符串替换实例
2016/12/28 Python
Python 多进程并发操作中进程池Pool的实例
2017/11/01 Python
python生成每日报表数据(Excel)并邮件发送的实例
2019/02/03 Python
Python获取时间范围内日期列表和周列表的函数
2019/08/05 Python
虚拟环境及venv和virtualenv的区别说明
2021/02/05 Python
美国球鞋寄卖网站:Stadium Goods
2018/05/09 全球购物
西班牙美妆电商:Perfume’s Club(有中文站)
2018/08/08 全球购物
你所在的项目是如何确定版本号的
2015/12/28 面试题
校运会口号
2014/06/18 职场文书
公司授权委托书范文
2014/08/02 职场文书
500字小学生检讨书
2015/02/19 职场文书
离职证明范本
2015/06/12 职场文书
幼儿园园长新年寄语
2015/08/17 职场文书
人事部:年度述职报告范文
2019/07/12 职场文书
MySQL中存储时间的最佳实践指南
2021/07/01 MySQL