闭包

闭包是作用域概念的扩展。通过闭包,函数可以访问存在函数被创建的作用域中的变量。如果这显得令人困惑,别担心:闭包一般最适合通过例子来理解。

如同作用域部分所示,函数可以访问变化的变量值。定义在循环中的函数也存在同样的行为 - 即使在函数定义之后,它依然能观察到变量的值发生了改变,导致每一个函数都引用了保存在变量中的最后值。

// Each function executed within the loop will reference
// the last value stored in i (5).
// This won"t behave as we want it to - every 100 milliseconds, 5 will alert
for ( var i = 0; i < 5; i++ ) {
    setTimeout(function() {
        alert( i );
    }, i * 100 );
}

闭包可以用来防止这种情况,通过给每一次迭代创建一个独特的作用域 - 在其作用域内保存变量的每一个独特值。

// Using a closure to create a new private scope
// fix: “close” the value of i inside createFunction, so it won"t change
var createFunction = function( i ) {
    return function() {
        alert( i );
    };
};

for ( var i = 0; i < 5; i++ ) {
    setTimeout( createFunction( i ), i * 100 );
}

闭包也可以用来解决 this 关键字的问题,它是每个作用域的唯一值:

// Using a closure to access inner and outer object instances simultaneously.
var outerObj = {
    myName: "outer",
    outerFunction: function() {

        // Provide a reference to outerObj through innerFunction"s closure
        var self = this;
        var innerObj = {
            myName: "inner",
            innerFunction: function() {
                console.log( self.myName, this.myName ); // "outer inner"
            }
        };

        innerObj.innerFunction();

        console.log( this.myName ); // "outer"
    }
};

outerObj.outerFunction();

Function.bind

当处理回调函数时,闭包也是特别有用的。但是,通常更好的做法是使用Function.bind,它可以避免任何作用域遍历相关的过度开销。

Function.bind 被用来创建一个新函数。当新函数被调用时,函数会在 .bind()方法中提供的 this 上下文中执行,并使用一系列 .bind() 方法中提供的参数与函数调用时提供的任何参数。

由于 .bind() 是在 ECMAScript 5 中添加的,它可能不会得到所有浏览器的支持,当决定是否使用它时,这是值得注意的一点。不过,我们可以使用 MDN 提供的兼容代码来使 .bind() 正常工作。

// Shim from MDN
if (!Function.prototype.bind) {

    Function.prototype.bind = function( oThis ) {

        if (typeof this !== "function") {
            // closest thing possible to the ECMAScript 5 internal
            // IsCallable function
            throw new TypeError( "Function.prototype.bind - what is trying to be bound is not callable" );
        }

        var fSlice = Array.prototype.slice,
            aArgs = fSlice.call( arguments, 1 ),
            fToBind = this,
            fNOP = function() {},
            fBound = function() {
                return fToBind.apply( this instanceof fNOP
                    ? this
                    : oThis || window,
                    aArgs.concat( fSlice.call( arguments ) ) );
            };

        fNOP.prototype = this.prototype;

        fBound.prototype = new fNOP();

        return fBound;
    };
}

.bind() 最简单的用途之一是创建一个使用特定 this 值的函数,而无关该函数是如何调用的。一个开发者常常出现的错误是试图从对象中提取一个方法,在随后调用该方法时期望使用原始对象作为 this 值。这时可以通过创建一个函数绑定原始对象来解决类似问题,如下所示:

// Let"s manipulate "this" with a basic example.
var user = "johnsmith";
var module = {
    getUser: function() {
        return this.user;
    },
    user: "janedoe"
};

// module.getUser() is called where "module" is "this"
// and "module.user" is returned.

// janedoe
module.getUser();

// let"s now store a reference in the global version of "this"
var getUser = module.getUser;

// getUser() called, "this" is global, "user" is returned

// johnsmith
getUser();

// store a ref with "module" bound as "this"
var boundGetUser = getUser.bind( module );

// boundGetUser() called, "module" is "this" again, "module.user" returned.

// janedoe
boundGetUser();
文章导航