JavaScript温故而知新——做用域链和闭包

1、做用域链

JavaScript是基于词法做用域的语言,当代码在一个环境中执行时,就会建立与之关联的做用域链(scope chain),这个做用域链能够看作是一个对象或者链表,对象中定义了这段代码“做用域”中的全部变量。
当须要查找某一变量的值时(这个过程称做“变量解析”),JavaScript会从链中的第一个对象开始查找,若是找到了则直接使用这个属性的值,查找不到则继续查找链上的下一个对象,以此类推,若是整个做用域链中都没有任一对象包含该属性,则会抛出一个引用错误(ReferenceError)异常。
bash

  • 若是执行环境是函数,则其活动对象最开始只包含一个变量,即arguments对象(这个对象在全局环境是不存在的)
  • 全局执行环境的变量对象始终都是做用域链中的最后一个对象。

能够经过一段代码来理解做用域链:闭包

var color = "blue";
function changeColor() {
    var anotherColor = "red";
    function swapColors() {
        var tempColor = antherColor;
        antherColor = color;
        color = tempColor;
        // 这里能够访问color、anotherColor和tempColor
    }
    // 这里能够访问color和anotherColor,但不能访问tempColor
    swapColors();
}
// 这里只能访问color
changeColor();
复制代码

做用域链图:app

图中矩形表示了三个特定的执行环境:全局环境、 changeColor()的局部环境和 swapColors()的局部环境。其中,内部环境能够经过做用域链访问全部的外部环境,但外部环境不能访问内部环境中的任何变量或函数。

2、闭包

理解了做用域链,咱们再来理解闭包是什么。
咱们都知道在JavaScript中,只有函数内部的子函数可以访问局部变量,那么若是函数外部要访问函数内的局部变量应该怎么作呢?闭包就是用来解决这一问题的。函数

闭包是指有权访问另外一个函数做用域中的变量的函数。post

能够看一下阮一峰老师的例子:ui

function f1() {
    var n = 999;
    nAdd = function() { n += 1 }
    function f2() {
        alert(n);
    }
    return f2;
}

var result = f1();
result(); // 999
nAdd();
result(); // 1000   
复制代码

在这个例子中,result就是闭包f2函数,第一次运行的值是999,说明访问到了函数f1内部的变量。第二次运行的值是1000,这是因为f2被赋给了全局变量result,因为result引用着f2,所以f2不会由于运行结束了就被销毁,它将一直存在于内存当中,f1和它的局部变量n也是如此。this

f1nAdd因为没有使用var进行声明,所以aAdd是一个全局变量,f1执行后将function() { n + 1 }赋给了全局变量nAdd,此时function() { n + 1 }也是一个闭包,一样能够对f1内部的变量进行操做。spa

总结code

  • 闭包能够读取函数内部的变量;
  • 闭包会使得函数内部的变量都被保存在内存中,形成较大的内存开销,所以不要滥用闭包。解决的方法是在退出函数以前将不使用的局部变量置为null

关于this对象

了解完闭包,还有一个须要特别注意的点。就是在闭包中使用this的问题,看下面的代码:cdn

var name = "The Window";
var object = {
    name: "My Object",
    getName: function() {
        return function() {
            return this.name;
        };
    }
};
alert(object.getNameFunc()());  // "The Window"(在非严格模式下)
复制代码

这里要注意两个点:

  • this对象是在运行时基于函数的执行环境绑定的
  • 匿名函数的执行环境具备全局性

所以这里的this指向的是全局环境,因此查找到的是全局的name

那么闭包如何访问到object对象呢,咱们能够改变一下代码:

var name = "The Window";
var object = {
    name: "My Object",
    getName: function() {
        var that = this;
        return function() {
            return that.name;
        };
    }
};
alert(object.getNameFunc()());  // "My Object" 
复制代码

能够看到,将this对象保存在一个闭包可以访问到的变量哩,就可让闭包访问到该对象了。

结尾

系列文章:

相关文章
相关标签/搜索