Max—— 前端攻城狮 's Blog

A Simple pure blog generated by jekyll

Js特性之一 —— 链式作用域

<< Back

Javascript的链式作用域

对于JavaScript新手来说作用域(scope)是最令人困惑的部分,没有之一。

先来看一段英文定义:

Scope is the set of variables, objects, and functions you have access to.

JavaScript has two scopes: global and local. A variable that is declared outside a function definition is a global variable, and its value is accessible and modifiable throughout your program. A variable that is declared inside a function definition is local. It is created and destroyed every time the function is executed, and it cannot be accessed by any code outside the function.

Functions defined inside other functions, known as nested functions, have access to their parent function's scope.

JavaScript的(scope)如此复杂的原因是它看上去非常像C系语言的成员。在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。

而Javascript压根没有块级作用域,而是函数作用域。

所谓函数作用域就是说:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

    var name="global";  
if(true){  
    var name="local";  
    console.log(name)    // local
}  
console.log(name);       // local
    

都输出是“local",如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。

变量作用域

变量的声明分两种情况;

  • 函数内显示var声明变量 , 作用域为当前函数体内,叫做局部变量;
  • 函数内声明变量(非var方式),作用域为全局,叫做全局变量;

那么,这两种有什么区别呢?

  • 当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除;
  • 不使用var声明一个变量时,创建的这个属性是可配置的,可以通过delete运算符删除;

    var unconfig = "变量一";
delete unconfig;
console.log(unconfig)     // 变量一
	

非显示声明变量

    config = "变量二";
delete config;
console.log(config)     // Uncaught ReferenceError: config is not defined 
console.log(typeof config)   //undefined
    

会发现全局变量被删除了,打印语句会报错,打印类型操作符显示是undefined类型。

Js作用域链

来看ECMA-262的英文原文:Scope chain is exactly this list of all (parent) variable objects for the inner contexts. This chain is used for variables lookup.

翻译过来大致意思是:作用域链就是在所有内部环境(包括全局环境)内查找变量的链式表。(注:它是一个链表结构)

用var关键字声明一个变量时,就是为该变量所在的对象添加了一个属性。

由于js的变量都是对象的属性,而该对象可能又是其它对象的属性,而所有的对象都是window对象的属性,所以这些对象的关系可以看作是一条链。

链头:就是变量所处的对象,链尾:就是window对象

    function global() { 
    var a; 
    function local() { 
        var b; 
    } 
}
    

js中函数也是对象,所以变量a所在的对象是global,global又在window对象中,所以a的作用域链如下:

global(链头)-- window(链尾)

那么b所以在的对象即local,local又包含在global中,global又在window对象,所以b的作用域链如下:

local(链头)-- global -- window(链尾)

来看下面这个例子;

var a = 3;
var b = 8;
function fn1(){
    var a = 5;
    console.log(a); 
    console.log(b);
}
console.log(a)       // 3
fn1();               // 5 、 8
    
var a = 3;
var b = 8;
function fn1(){
    var a = 5;
    var b;
    console.log(a); 
    console.log(b);
}
console.log(a)       // 3
fn1();               // 5 、 undefined
    

函数fn1里面的变量a的赋值为5覆盖了外面的赋值3(但是不会改变全局变量a的值);所以函数fn1里面打印出来的a的值是5;函数fn1外面打印出来a的值还是3;接着执行函数fn1,函数fn1里面没有声明变量b更没有给变量b赋值,所以会沿着作用域链向函数外的全局范围查找变量b的值,所以会打印出来得值为8,但是下面第二个例子在函数fn1里面声明了变量b却没有初始化(给变量b赋值),这个时候因为在函数里面能够找到变量b,所以不会再沿着作用域链向链尾查找,但是此时变量b由于没有赋值,所以会打印出undefined;

函数作用域问题

函数内(函数内的函数除外)定义的变量在整个函数内部都有效

    function loop() { 
    var t = 0; 
    //在for的条件里定义变量i,这个变量i的作用域链对象是这个函数 
    //因此在整个的函数里它是有效的 
    for (var i = 0; i < 5; i++) { 
        t += i; 
    } 
    console.log(i);    // 5
    console.log(t);    // 10 = 1+2+3+4
} 
loop();
    

释义;在函数loop里面的for循环里面定义的变量i,它的作用域对象是这个函数,所以经过for循环语句之后打印出来的结果是5,而此时t累加之后结果等于10。

参考js作用域问题一步步透彻理解

发表于: 12 May 2014