作用域与闭包

一、变量提升

变量和函数在内的所有声明都会在任何代码被执行前首先被处理。

举个例子:

当你看到 var a = 2; 时,可能会认为这是一个声明,但实际上 Javascript 会将其看成两个声明:var a ; 和 a = 2;并且在不同阶段执行。var a 是在编译阶段进行的,而 a = 2 会被留在原地等待执行阶段。

这个过程就好像变量和函数声明从它们在代码中出现的位置被“移动”到了最上面,这个过程就叫做变量提升。

foo();

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

foo()可以正常执行,控制台打印出undefined。这段代码相当于下面这种形式:

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

foo();

重点:

1.对于函数,函数声明会被提升,而函数表达式不会被提升。

foo(); //TypeError

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

2、重复声明后面的会覆盖前面的。

foo(); // 3

function foo(){
    console.log(1);
}
var foo = function(){
    console.log(2)
}

function foo(){
    console.log(3)
}

 二、闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

function foo(){
    var a = 2;
    function bar(){
        console.log(a);
    }
    return bar;
}

var baz = foo();
baz(); //2

这里通过调用 baz 来调用 foo 内部的 bar , bar 在自己定义的词法作用域以外的地方执行,在 foo 执行之后,通常会期待 foo 的整个内部作用域被销毁,因为引擎的垃圾回收器会释放不再使用的内存空间。看上去 foo 不再被使用,所以很自然的考虑到对其进行回收,然而闭包就是阻止这样的事情发生,事实上内部作用域依然存在,没有被回收,因为 bar 依然在使用该作用域。

bar 拥有 涵盖 foo 内部作用域的闭包,使得该作用域能够一直存活,以供 bar 在之后任何时间进行引用。

bar 依然持有对该作用域的引用,而这个引用就叫作 闭包。

bar 在定义时的词法作用域以外的地方被调用,闭包使得函数可以继续访问定义时的作用域。

原文地址:https://www.cnblogs.com/PeriHe/p/8539054.html