简述JS作用域、作用域链和闭包

作用域

定义:一个变量的作用域是程序源代码中定义这个变量的区域。
全局变量拥有全局作用域,局部变量只有局部作用域。

块级作用域: 在ES6 let变量声明出来之前,JS是没有块级作用域的概念的,函数内部定义的变量才是局部变量,具体见下面的代码

var a=1;
for(var i=0;i<10;i++){
    var b= f(a);
}
function f(x){
    return x;
}

console.log(a);//1
console.log(b);//1
console.log(x);//not defined

由于没有块级作用域,for循环结束后b也不会被销毁,是全局变量,而x由于是在函数内部,函数执行完即被销毁,只在函数内部可见,因此访问不到。

作用域链

本质: 一个指向变量对象的指针列表。这些变量对象都能被当前代码(全局代码或函数)访问。
变量对象: 作用域链始终拥有一个全局对象,包含所有的全局变量和函数。当函数被调用时会创建一个执行环境,使用arguments和其他命名参数值来初始化函数的活动对象(作为变量对象),如下面的代码

var a=1;
var x=2;
function f(x){
    return x;
}

console.log(f(a));// 1

当函数f被执行时活动对象为arguments:[ 1 ] x: 1,全局对象包含a x f及其他全局变量和函数。
此时的f(a)的作用域链由f自身的活动对象和全局对象组成,当需要查找变量x时,先从活动对象开始查找,如果找不到则查找全局对象。

当函数被创建时,若函数是全局函数,则创建一个预先包含全局对象的作用域链,若函数是内部函数,则将外部函数的作用域链作为初始作用域链。还是看代码:

var a=2;
function f(){
    var b=a;
    f2=function(){
        return b;
    }
}

f();
console.log(f2());//2

首先看执行f(a)时f的作用域链:活动对象(arguments b)-> 全局对象 (a f f2),
执行f(a)时同时也创建了函数f2,此时f2的作用域链为: f的活动对象(arguments b)-> 全局对象 (a f f2),
执行f2(b)时创建了f2的活动对象(arguments)并将其推入到作用域链的顶端,此时f2的作用域链为: f2的活动对象(arguments)->f的活动对象(arguments b)-> 全局对象 (a f f2),
当查找b时,由于本地活动对象中没有,因此向上层查找,在f的活动对象中找到了b变量。

闭包

理解了作用域链之后,其实闭包就比较好理解了。
闭包指有权访问另一个函数作用域中变量的函数。

创建闭包的常见方式就是在一个函数内部创建另外一个函数,仍以前面的函数为例:

var a=2;
function f(){
    var b=a;
    f2=function(){
        return b;
    }
}

f();
console.log(f2());//2

我们在f中创建了一个函数f2,使得f2能够访问f中的变量,此时f2就是闭包。

猜你喜欢

转载自blog.csdn.net/u012075670/article/details/55657305