`
superobin
  • 浏览: 9558 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

js中变量作用域的推断

阅读更多
本来写在帖子里了,但是感觉应该存个底,放这吧,呵呵。
论坛帖子地址: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中应该都写了吧。但是啃英文文档。。。还是算了。。自己推断了。哈哈莫见笑莫见笑
分享到:
评论

相关推荐

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

    一、迷思!由一段代码引发的疑惑 请看如下代码: 代码如下: for... 如果JavaScript中用var声明的变量可视为局部变量,那么能访问到这个变量的作用域就是这个变量的局部作用域。如上例,在console.log行处,依然有j、k

    如何推断Javascript对象是否存在的简洁实例_.docx

    在JavaScript编程中,判断一个对象是否存在是常见的需求,特别是在处理可能未被初始化或者可能存在与否的变量时。本文将深入探讨几种简洁的实例来推断JavaScript对象是否存在的方法,并解释其工作原理。 首先,我们...

    TypeScript 变量声明

    TypeScript 中的变量作用域分为局部作用域和全局作用域。在函数内部声明的变量是局部作用域,只在该函数内有效;在函数外部或模块级别的声明是全局作用域,可以在整个文件或模块中访问。此外,TypeScript 还支持...

    03 - CSS Variables.rar

    【标题解析】:“03 - CSS Variables.rar” 这个标题暗示了我们即将...在实际的教程中,可能还会涉及更多细节,如变量的默认值、作用域规则、变量与CSS变量变量的区别,以及如何在CSS-in-JS库或框架中使用CSS变量等。

    使用javascript 设计html

    下面,我们将深入探讨如何利用JavaScript设计HTML,包括JavaScript的基本概念、语法特点、变量管理、类型转换、作用域规则以及复合类型的应用。 #### JavaScript:一种脚本语言的力量 首先,澄清一个常见的误解:...

    JavaScript基础笔记

    变量在声明时无需指定类型,JavaScript会根据赋值自动推断类型,这就是所谓的动态类型。 接下来是运算符。JS中的运算符包括算术运算符(如+,-,*,/,%),比较运算符(如==,===,&lt;,&gt;,,&gt;=),逻辑运算符(如&&...

    JS有关JS方面的内容

    在JavaScript中,闭包是一个非常重要的概念,它允许一个函数访问并操作其外部作用域中的变量,即使这个函数在其外部作用域之外被调用。简单来说,闭包就是能够读取其他函数内部变量的函数。在JavaScript中,只有函数...

    JavaScript学习笔记

    JavaScript中的变量通过`var`、`let`或`const`关键字声明,其中`let`和`const`是ES6新增的,提供块级作用域。 **1.3 函数** 函数在JavaScript中扮演着重要角色,它们是第一类对象,可以作为参数传递,也可以作为...

    Javascript征途

    函数是JavaScript的核心组成部分,书中的章节会深入讨论函数的定义、调用、作用域以及闭包等概念。同时,也会介绍箭头函数这一ES6的新特性,使得函数的写法更为简洁。此外,函数式编程的思想和实践在JavaScript中也...

    喵版Yunzai-V3.zip

    2. **函数与作用域**:理解函数的定义、调用方式,以及函数内部的变量作用域(局部作用域和全局作用域)。 3. **原型与继承**:JavaScript的面向对象特性基于原型链,理解如何通过原型实现继承。 4. **闭包**:闭包...

    teacher-tmp.zip

    【标签】虽然没有明确的标签,但我们可以通过文件名推断出主要涉及的知识点,如JavaScript基础、类型转换、作用域链、递归、数组排序等。 【文件名称列表详解】 1. **简单原型链.jpg** 和 **原型链.jpg**: 这两个...

    JavaScript Demystified (2014)

    这意味着变量声明在函数内,其作用域限定在该函数内,而变量声明在函数外则具有全局作用域。 在现代JavaScript编程实践中,ECMAScript规范定义了JavaScript语言的核心特性。每一年,ECMAScript规范都会发布一个新...

    JavaScript函数手册

    - JavaScript有函数作用域,变量在函数内部定义,外部无法访问。 - 闭包是函数可以访问其自身作用域、包含它的函数作用域以及全局作用域的一种特性,常用于创建私有变量。 6. **函数原型与原型链** - 所有函数都...

    JavaScript 10分钟速成 (js-in-ten-minutes)

    - **惰性作用域**:变量的作用域依赖于其定义的位置,而不是函数调用的位置,这是JavaScript的一个特性。 - **η-约简**:这是一个重要的概念,在JavaScript中,函数可以作为第一类公民进行传递和操作,这导致了η-...

    JavaScript小技巧全集 JavaScript教程 JavaScript源代码集

    此外,JavaScript还有闭包(Closure)的概念,它允许函数访问并操作其外部作用域的变量,即使在其定义的作用域之外。 描述中的“跑马灯”和“自动滚屏阅读式公告栏”是JavaScript实现动态效果的实例。这些可以通过...

    JS高程红宝书(第三版)

    1. **基础语法**:书中首先介绍了JavaScript的基础语法,包括变量、数据类型(如基本类型和引用类型)、运算符、流程控制语句(如if、switch、for、while)、函数以及作用域等概念。 2. **对象与原型**:JavaScript...

    Getting started with TypeScript with intro Angular

    var声明的变量具有函数作用域,let和const声明的变量具有块级作用域。在使用变量之前,需要先声明变量。此外,TypeScript还引入了ES6中的解构赋值功能。 6. 接口和类 TypeScript支持接口和类。接口可以用来定义一...

Global site tag (gtag.js) - Google Analytics