本来写在帖子里了,但是感觉应该存个底,放这吧,呵呵。
论坛帖子地址:
http://www.iteye.com/topic/842542
这个问题其实之前困扰了我很久。如今终于想明白了,特来分享,如果有错误的地方,请帮忙指正,我会随时回来修正滴。
一、迷思!由一段代码引发的疑惑
请看如下代码:
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中应该都写了吧。但是啃英文文档。。。还是算了。。自己推断了。哈哈莫见笑莫见笑
分享到:
相关推荐
一、迷思!由一段代码引发的疑惑 请看如下代码: 代码如下: for... 如果JavaScript中用var声明的变量可视为局部变量,那么能访问到这个变量的作用域就是这个变量的局部作用域。如上例,在console.log行处,依然有j、k
在JavaScript编程中,判断一个对象是否存在是常见的需求,特别是在处理可能未被初始化或者可能存在与否的变量时。本文将深入探讨几种简洁的实例来推断JavaScript对象是否存在的方法,并解释其工作原理。 首先,我们...
TypeScript 中的变量作用域分为局部作用域和全局作用域。在函数内部声明的变量是局部作用域,只在该函数内有效;在函数外部或模块级别的声明是全局作用域,可以在整个文件或模块中访问。此外,TypeScript 还支持...
【标题解析】:“03 - CSS Variables.rar” 这个标题暗示了我们即将...在实际的教程中,可能还会涉及更多细节,如变量的默认值、作用域规则、变量与CSS变量变量的区别,以及如何在CSS-in-JS库或框架中使用CSS变量等。
下面,我们将深入探讨如何利用JavaScript设计HTML,包括JavaScript的基本概念、语法特点、变量管理、类型转换、作用域规则以及复合类型的应用。 #### JavaScript:一种脚本语言的力量 首先,澄清一个常见的误解:...
变量在声明时无需指定类型,JavaScript会根据赋值自动推断类型,这就是所谓的动态类型。 接下来是运算符。JS中的运算符包括算术运算符(如+,-,*,/,%),比较运算符(如==,===,<,>,,>=),逻辑运算符(如&&...
在JavaScript中,闭包是一个非常重要的概念,它允许一个函数访问并操作其外部作用域中的变量,即使这个函数在其外部作用域之外被调用。简单来说,闭包就是能够读取其他函数内部变量的函数。在JavaScript中,只有函数...
JavaScript中的变量通过`var`、`let`或`const`关键字声明,其中`let`和`const`是ES6新增的,提供块级作用域。 **1.3 函数** 函数在JavaScript中扮演着重要角色,它们是第一类对象,可以作为参数传递,也可以作为...
函数是JavaScript的核心组成部分,书中的章节会深入讨论函数的定义、调用、作用域以及闭包等概念。同时,也会介绍箭头函数这一ES6的新特性,使得函数的写法更为简洁。此外,函数式编程的思想和实践在JavaScript中也...
2. **函数与作用域**:理解函数的定义、调用方式,以及函数内部的变量作用域(局部作用域和全局作用域)。 3. **原型与继承**:JavaScript的面向对象特性基于原型链,理解如何通过原型实现继承。 4. **闭包**:闭包...
【标签】虽然没有明确的标签,但我们可以通过文件名推断出主要涉及的知识点,如JavaScript基础、类型转换、作用域链、递归、数组排序等。 【文件名称列表详解】 1. **简单原型链.jpg** 和 **原型链.jpg**: 这两个...
这意味着变量声明在函数内,其作用域限定在该函数内,而变量声明在函数外则具有全局作用域。 在现代JavaScript编程实践中,ECMAScript规范定义了JavaScript语言的核心特性。每一年,ECMAScript规范都会发布一个新...
- JavaScript有函数作用域,变量在函数内部定义,外部无法访问。 - 闭包是函数可以访问其自身作用域、包含它的函数作用域以及全局作用域的一种特性,常用于创建私有变量。 6. **函数原型与原型链** - 所有函数都...
- **惰性作用域**:变量的作用域依赖于其定义的位置,而不是函数调用的位置,这是JavaScript的一个特性。 - **η-约简**:这是一个重要的概念,在JavaScript中,函数可以作为第一类公民进行传递和操作,这导致了η-...
此外,JavaScript还有闭包(Closure)的概念,它允许函数访问并操作其外部作用域的变量,即使在其定义的作用域之外。 描述中的“跑马灯”和“自动滚屏阅读式公告栏”是JavaScript实现动态效果的实例。这些可以通过...
1. **基础语法**:书中首先介绍了JavaScript的基础语法,包括变量、数据类型(如基本类型和引用类型)、运算符、流程控制语句(如if、switch、for、while)、函数以及作用域等概念。 2. **对象与原型**:JavaScript...
var声明的变量具有函数作用域,let和const声明的变量具有块级作用域。在使用变量之前,需要先声明变量。此外,TypeScript还引入了ES6中的解构赋值功能。 6. 接口和类 TypeScript支持接口和类。接口可以用来定义一...