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

 更新时间:2010年12月16日 22:27:06   作者:  
这个问题其实之前困扰了我很久。如今终于想明白了,特来分享,如果有错误的地方,请帮忙指正,我会随时回来修正滴。
一、迷思!由一段代码引发的疑惑
请看如下代码:
复制代码 代码如下:

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

相关文章

  • 简单实现js轮播图效果

    简单实现js轮播图效果

    这篇文章主要教大家如何简单实现js轮播图效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • JavaScript弹出对话框的三种方式

    JavaScript弹出对话框的三种方式

    本文主要介绍了javascript中的三种弹出对话框,分别是alert()方法,confirm()方法,prompt()方法,对javascript弹出对话框相关知识感兴趣的朋友一起学习吧
    2016-03-03
  • 在JavaScript中获取请求的URL参数

    在JavaScript中获取请求的URL参数

    在ASP.NET后台代码中,对于这样的URL请求地址:http://www.abc.com?id=001,我们可以通过Request.QueryString["id"]的方法很容易的获取到URL中请求的参数的值,但是要在前台js代码中获取请求的参数的值,应该怎么做呢?
    2010-12-12
  • ES6新特性之类(Class)和继承(Extends)相关概念与用法分析

    ES6新特性之类(Class)和继承(Extends)相关概念与用法分析

    这篇文章主要介绍了ES6新特性之类(Class)和继承(Extends)相关概念与用法,结合实例形式较为详细的分析了ES6中类(Class)和继承(Extends)的基本概念、语法、使用方法与注意事项,需要的朋友可以参考下
    2017-05-05
  • Bootstrap CSS布局之按钮

    Bootstrap CSS布局之按钮

    这篇文章主要介为大家详细绍了Bootstrap CSS布局之按钮的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • js监听输入框值的即时变化onpropertychange、oninput

    js监听输入框值的即时变化onpropertychange、oninput

    很多情况下我们都会即时监听输入框值的变化,以便作出即时动作去引导浏览者增强网站的用户体验感。
    2011-07-07
  • JS实现的新浪微博大厅文字内容滚动效果代码

    JS实现的新浪微博大厅文字内容滚动效果代码

    这篇文章主要介绍了JS实现的新浪微博大厅文字内容滚动效果代码,可实现页面图文元素定时滚动的效果,涉及JavaScript时间函数定时改变页面元素的相关技巧,需要的朋友可以参考下
    2015-11-11
  • 原生js实现autocomplete插件

    原生js实现autocomplete插件

    这篇文章主要介绍了原生js实现autocomplete插件的相关资料,需要的朋友可以参考下
    2016-04-04
  • 67 个节约开发时间的前端开发者的工具、库和资源

    67 个节约开发时间的前端开发者的工具、库和资源

    在本文中,我不会去谈 React、Angular、Vue 等等这些大的前端框架,也不会谈 Atom、VS code、Sublime 等等这些已经很出名的代码编辑器,我只是想简单的分享一套我认为有助于提升开发者工作流的工具集
    2017-09-09
  • javascript设计模式之中介者模式学习笔记

    javascript设计模式之中介者模式学习笔记

    这篇文章主要为大家详细介绍了javascript设计模式之中介者模式学习笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02

最新评论