理解JavaScript之作用域、作用域链和闭包

作用域

作用域是指程序源代码中定义变量的区域。决定这个变量的生命周期及其可见性。

作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。

JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。

PS:通过var创建的变量只有函数作用域,而通过let和const创建的变量既有函数作用域,也有块作用域。

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar(); // 1

执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置(词法作用域),查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。

作用域的类型

全局作用域:代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域。

函数作用域:在固定的代码片段才能被访问

作用域有上下级关系,上下级关系的确定取决于函数是在哪个作用域下创建的,如上,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

作用域链

当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面)执行上下文的变量对象中查找。一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。

函数的作用域在函数定义的时候就决定了。

var a = 10;
function fun(){
    var b  20;
    function foo(){
        console.log(a + b);
    }
}
var x = fun(),
    b = 5;

x.foo(); //30

上面的代码中,fun()赋值给x,在执行a.foo()时,foo()向上查找a和b,由于词法作用域的作用,foo()并不会访问到b=5,即得到打印30。

而foo()是一个嵌套在fun()中的函数。在x.foo()函数执行中,可以访问到变量a和b。此时,foo()就是一个闭包。

闭包就是内部函数,可以通过该一个在函数内部或者{}块里面定义一个函数来创建闭包。

闭包

内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。

特点:

1. 让外部访问函数内部变量成为可能;

2. 局部变量会常驻在内存中;

3. 可以避免使用全局变量,防止全局变量污染;

4. 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

应用场景:设计私有的方法和变量。

缺陷:常驻内存会增大内存使用量,并且使用不当很容易造成内存泄露。

看一道闭包面试题:

function fun(n,o){
    console.log(o);
    return {
        fun: function(m){
            return fun(m,n);
        }
    }
}

var a = fun(0); //fun(0):fun(m,n)中,m=0,n=undefined;fun(n,o)中,n=0,o=undefined,
//打印undefined,退出执行栈,n=0被返回给a
a.fun(1); //fun(1):fun(m,n)中m=1,n=0,fun(n,o)中,n=1,o=0,打印0,退栈
a.fun(2); //fun(2):fun(m,n)中m=1,n=0,fun(n,o)中,n=1,o=0,打印0,退栈
a.fun(3); //fun(3):fun(m,n)中m=1,n=0,fun(n,o)中,n=1,o=0,打印0,退栈

var b = fun(0).fun(1).fun(2).fun(3);
//fun(0):同上,打印undefined
//fun(1):由于链式调用的关系,fun(0)并未被清除,
//则fun(m,n)中m=1,n=0,fun(n,o)中,n=1,o=0,打印0
//fun(2):fun(m,n)中m=2,n=1,fun(n,o)中,n=2,o=1,打印1
//fun(3):fun(m,n)中m=3,n=2,fun(n,o)中,n=3,o=2,打印2
//执行完成全部调用后,按fun(3),fun(2),fun(1),fun(0)退栈

var c = fun(0).fun(1);
//fun(0):同上,打印undefined
//fun(1):fun(m,n)中m=1,n=0,fun(n,o)中,n=1,o=0,打印0,退栈fun(1),fun(0),n=1被返回给c
c.fun(2);
//fun(2):fun(m,n)中m=2,n=1,fun(n,o)中,n=2,o=1,打印1,退栈
c.fun(3);
//fun(3):fun(m,n)中m=3,n=1,fun(n,o)中,n=3,o=1,打印1,退栈

//最终结果
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1

如有不对之处,欢迎指正或留言讨论。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值