`

理解 Javascript 中变量的作用域

 
阅读更多

本文由 范斌 创作

原文http://segmentfault.com/a/1190000002960647

Javascript 这门语言与其他的大部分语言相比,有很多特殊性,这是很多人喜欢它或者讨厌它的原因。其中变量的作用域问题,对很多初学者来说就是一个又一个「坑」。

变量的作用域在编程技能中算是一个基本概念,而在 Javascript 中,这一基本概念往往挑战者初学者的常识。

基本的变量作用域

先上例子:

var scope = 'global';
function checkScope(){
    var scope = 'local';
    console.log(scope); // local
}
checkScope();
console.log(scope); // global

上面的例子中,声明了全局变量 scope 和函数体内的局部变量 scope。在函数体内部,局部变量的优先级比通明的全局变量要高,如果一个局部变量的名字与一个全局变量相同,那么,在声明局部变量的函数体范围内,局部变量将覆盖同名的全局变量。

下面再看一个例子:

scope = 'global';
function checkScope(){
    scope = 'local'; 
    console.log(scope); // local
    myScope = 'local';
    console.log(myScope); // local
}
checkScope();
console.log(scope); // local
console.log(myScope); // local

对于初学者来说,可能会有两个疑问:为什么在函数体外,scope 的值也变成了 local ?为什么在函数体外可以访问 myScope 变量?

这两个问题都源于一个特性。在全局作用域中声明变量可以省略 var 关键字,但是如果在函数体内声明变量时不使用 var 关键字,就会发生上面的现象。首先,函数体内的第一行语句,把全局变量中的 scope 变量的值改变了。而在声明 myScope 变量时,由于没有使用 var 关键字,Javascript 就会在全局范围内声明这个变量。因此,在声明局部变量时使用 var 关键字是个很好的习惯。

在 Javascript 中,没有「块级作用域」一说

在 C 或者 Java 等语言中,iffor 等语句块内可以包含自己的局部变量,这些变量的作用域是这些语句的语句块,而在 Javascript 中,不存在「块级作用域」的说法。

看下面的例子:

function checkScope(obj){
    var i = 0;
    if (typeof obj == 'object') {
        var j = 0;
        for (var k = 0; k < 10; k++) {
            console.log(k);
        }
        console.log(k);
    }
    console.log(j);
}
checkScope(new Object());

在上面的例子中,每一条控制台输出语句都能输出正确的值,这是因为,由于 Javascript 中不存在块级作用域,因此,函数中声明的所有变量,无论是在哪里声明的,在整个函数中它们都是有定义的

如果要更加强调上文中 函数中声明的所有变量,无论是在哪里声明的,在整个函数中它们都是有定义的 这句话,那么还可以在后面跟一句话:函数中声明的所有变量,无论是在哪里声明的,在整个函数中它们都是有定义的,即使是在声明之前。对于这句话,有个经典的困扰初学者的「坑」。

var a = 2;
function test(){
    console.log(a);
    var a = 10;
}
test();

上面的例子中,控制台输出变量 a 的值为 undefined,既不是全局变量 a 的值 2,也不是局部变量 a 的值 10。首先,局部变量在整个函数体内都是有定义的,因此,局部变量 a 会在函数体内覆盖全局变量 a,而在函数体内,var 语句之前,它是不会被初始化的。如果要读取一个未被初始化的变量,将会得到一个默认值 undefined

所以,上面示例中的代码与下面的代码时等价的:

var a = 2;
function test(){
    var a;
    console.log(a);
    a = 10;
}
test();

可见,把所有的函数声明集合起来放在函数的开头是个良好的习惯

变量的真相

可能很多人已经注意到,在 Javascript 当中,一个变量与一个对象的一个属性,有很多相似的地方,实际上,它们并没有什么本质区别。在 Javascript 中,任何变量都是某个特定对象的属性

全局变量都是全局对象的属性。在 Javascript 解释器开始运行且没有执行 Javascript 代码之前,会有一个「全局对象」被创建,然后 Javascript 解释器会给它与定义一些属性,这些属性就是我们在 Javascript 代码中可以直接使用的内置的变量和方法。之后,每当我们定义一个全局变量,实际上是给全局对象定义了一个属性。

在客户端的 Javascript 当中,这个全局变量就是 Window 对象,它有一个指向自己的属性 window ,这就是我们常用的全局变量。

对于函数的局部变量,则是在函数开始执行时,会有一个对应的「调用对象」被创建,函数的局部变量都作为它的属性而存储。这样可以防止局部变量覆盖全局变量。

作用域链

如果要深入理解 Javascript 中变量的作用域,那就必须拿出「作用域链」这个终极武器。

首先要理解的一个名词就是「执行环境」,每当 Javascript 执行时,都会有一个对应的执行环境被创建,这个执行环境中很重要的一部分就是函数的调用对象(前面说过,调用对象是用来存储相应函数的局部变量的对象), 每一个 Javascript 方法都是在自己独有的执行环境中运行的。简而言之,函数的执行环境包含了调用对象,调用对象的属性就是函数的局部变量,每个函数就是在这样的执行环境中执 行,而在函数之外的代码,也在一个执行环境中,这个执行环境包含了全局变量。

在 Javascript 的执行环境中,还有一个与之对应的「作用域链」,它是一个由对象组成的列表或链。

当 Javascript 代码需要查询一个变量 x 的时候,会有一个被称为「变量名解析」的过程。它会首先检查作用域链的第一个对象,如果这个对象包含名为 x 的属性,那么就采用这个属性的值,否则,会继续检查第二个对象,依此类推。当检查到最后一个对象的时候仍然没有相应的属性,则这个变量会被认定为是「未定义」的。

在全局的 Javascript 执行环境中,作用域链中只包含一个对象,就是全局对象。而在函数的执行环境中,则同时包含函数的调用对象。由于 Javascript 的函数是可以嵌套的,因此每个函数执行环境的作用域链可能包含不同数目个对象,一个非嵌套的函数的执行环境中,作用域链包含了这个函数的调用对象和全局对 象,而在嵌套的函数的执行环境中,作用域链包含了嵌套的每一层函数的调用对象以及全局变量。

我们可以用一个图来直观地解释作用域链和变量名解析的过程:

分享到:
评论

相关推荐

    javascript变量作用域

    了解 JavaScript 变量作用域的规则非常重要,因为它可以帮助我们更好地理解和使用 JavaScript 语言。同时,了解变量作用域的规则也可以帮助我们避免一些常见的错误和问题。 在实际开发中,了解变量作用域的规则可以...

    理解JavaScript变量作用域.pdf

    理解JavaScript变量作用域.pdf 本人还有几十本经典javascript书籍以及无数javascript资料,要的加我qq 568094881,本人网址:www.maoshanhai.com

    基于JavaScript的变量作用域的辨析.pdf

    本文探讨了JavaScript中的变量作用域问题,通过对两种变量的特性从不同角度进行分析和测试,讨论了如何控制变量作用域的有效方法。 变量作用域是JavaScript编程过程中经常遇到的问题之一,对于编程人员来说是一个...

    深入理解变量作用域

    ### 深入理解变量作用域 #### 一、引言 在JavaScript编程语言中,作用域是一个核心概念,它决定了变量的可见性和生命周期。作用域不仅影响着代码的执行效率,更是解决bug的关键所在。本文将从JavaScript权威指南...

    js变量作用域

    在探讨JavaScript的面向对象特性之前,理解变量作用域的概念至关重要。本篇文章旨在通过一系列示例和解释,帮助读者掌握JavaScript中的变量作用域机制。 #### 二、全局变量与作用域划分 ##### 1. 全局变量 在...

    JavaScript程序设计-变量作用域.pdf

    总结,理解JavaScript的变量作用域对于编写高效且无错的代码至关重要。全局变量应谨慎使用,以避免命名冲突和潜在的内存泄漏。局部变量有助于限制作用域,提高代码可读性和性能。掌握变量提升、词法作用域和作用域链...

    JavaScript程序设计变量作用域共4页.pdf.zi

    本文将深入探讨JavaScript中的变量作用域,以及它如何影响代码的运行和组织。 首先,我们来理解什么是变量作用域。在JavaScript中,变量作用域决定了变量在何处可以被访问和识别。主要有三种类型的作用域:全局作用...

    深入理解JavaScript作用域和作用域链

    JavaScript作用域是编程中至关重要的概念,它规定了变量和函数的可见性及生命周期。JavaScript主要有两种作用域:全局作用域和局部作用域。 全局作用域是指在代码的任何位置都可以访问的变量或函数,这通常包括在最...

    javascript执行环境,作用域理解

    在 JavaScript 中,执行环境和作用域是两个非常重要和基本的概念,理解了这两个概念对于 JavaScript 中很多脚本的运行结果就能明白其中的道理了。执行环境是一个概念,一种机制,用来完成 JavaScript 运行时在作用域...

    JavaScript 基础函数_深入剖析变量和作用域

    JavaScript是一种广泛用于网页开发的脚本语言,它的函数和作用域是实现代码逻辑结构和模块...理解JavaScript中的变量作用域非常重要,它有助于避免变量命名冲突和意外的变量修改,从而编写出更加健壮和易于维护的代码。

    理解JavaScript变量作用域更轻松

    JavaScript变量作用域是编程基础中的重要概念,它决定了变量在哪些范围内可以被访问。了解变量作用域可以帮助开发者更好地管理代码中的数据,防止意外的变量冲突,从而提高代码的稳定性和可维护性。 JavaScript中...

    JavaScript的变量作用域深入理解

    在学习JavaScript的变量作用域之前,我们应当明确几点: a、JavaScript的变量作用域是基于其特有的作用域链的。 b、JavaScript没有块级作用域。 c、函数中声明的变量在整个函数中都有定义。 1、JavaScript的作用域链...

    理解JavaScript作用域和作用域链

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript。任何程序...

    深化理解javascript作用域其次篇之词法作用域和动态作用域_.docx

    引擎首先在bar函数的作用域中查找变量a,找不到时,则在foo函数的作用域中查找,最后找到变量a。 词法作用域的优点是可以避免变量的污染和命名冲突。例如: var a = 0; function test() { var a = 1; console....

    深入理解JavaScript作用域共12页.pdf.zip

    JavaScript作用域是编程中至关重要的概念,尤其是在JavaScript这种动态类型的脚本语言中。它规定了变量、函数以及其它标识符的可见性和生命周期,是代码组织和管理的关键元素。本资料"深入理解JavaScript作用域共12...

    一道关于JavaScript变量作用域的面试题_.docx

    JavaScript变量作用域是编程语言中一个非常核心的概念,它决定了变量在何处可被访问以及其生命周期。在这道面试题中,我们看到的是如何在不同作用域下声明和使用变量,特别是与for循环相结合的情况。 首先,我们要...

    深入浅析JavaScript中的作用域和上下文

    JavaScript中,作用域和上下文是理解代码执行逻辑的关键概念。作用域指的是变量和函数的可见性和生命周期,而上下文则关乎`this`关键字的值,它指示了当前代码执行的环境。 **作用域(Scope)** 1. **全局作用域**...

Global site tag (gtag.js) - Google Analytics