闭包的总结(干货1)

闭包:函数能够记住所在词法做用域,就产生了闭包,即便函数在当前词法做用域以外执行 ,闭包无处不在。。。请忽略这句话 ,看完在说javascript

function foo() {
        var a = 1;
        function bibao() {
            console.log(a);
        }
        return bibao;
    }
    var baz = foo();
    baz();

bibao()能访问foo的内部做用域,而后bibao()自己做为值传递给baz,在foo执行以后,注意 baz=foo(),这里foo()会先运行,而后将返回值赋值给baz,而后运行baz(),实际是经过不一样的标识符引用内部的函数bibao();bibao()能够正常的执行,实现了bibao()在本身定义时的词法做用域之外执行。foo执行完成以后,一般期待foo()整个内部空间被销毁,被垃圾回收器回收空间,可是,拜baz()所赐,baz能访问foo()做用域,使得该做用域一直纯在。java

这个例子中,bibao对做用域的引用就叫闭包闭包

 

再来看一个传递函数是间接的:函数

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

同上面例子,不过这里外部函数运行,实现了全局变量fn对bibao()的引用,bibao()能访问foo内部词法做用域,在运行fn()的时候 ,就能实现了闭包spa

再好比:code

function foo() {
var a = 2;
function baz() {
console.log( a ); // 2
}
bar( baz );
}
function bar(fn) {
fn(); // 闭包
}

运行foo()实现baz的引用绑定到bar做用域 ,bar是全局函数对象

总结:blog

不管经过何种手段将内部函数传递到所在的词法做用域之外,它都会持有对原始定义做用
域的引用,不管在何处执行这个函数都会使用闭包。
接口

在好比咱们常常写的函数:事件

function wait(message) {
        setTimeout( function timer() {
            console.log( message );
        }, 1000 );
    }
    wait( "Hello, closure!" );

这也是闭包

将一个内部函数(名为timer)传递给setTimeout(..)。timer 具备涵盖wait(..) 做用域的闭包,所以还保有对变量message 的引用。
wait(..) 执行1000 毫秒后,它的内部做用域并不会消失,timer 函数依然保有wait(..)做用域的闭包(也就是引用)。

function setupBot(name, selector) {
$( selector ).click( function activator() {
console.log( "Activating: " + name );
} );
}
setupBot( "Closure Bot 1", "#bot_1" );
setupBot( "Closure Bot 2", "#bot_2" );

同理:事件函数也是闭包,本质上只要用到回调函数的都是使用闭包


在好比:

for (var i=1; i<=5; i++) {
        setTimeout( function timer() {
            console.log( i );   //结果是输出5次6
        }, i*1000 );
     }   

显而易见的,延迟函数的回调会在循环结束时才执行,为何会捕捉不到?

它们被封闭在一个共享的全局做用域中,所以实际上只有一个i

解决办法:延迟函数的回调重复定义五次,彻底不使用循环,用匿名函数当即执行产生5个数做用域包裹对应变量i

for (var i=1; i<=5; i++) {
(function() {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
})();
}

固然,你也能够直接用ES新特性:

for (let i=1; i<=5; i++) {
        setTimeout( function timer() {
            console.log( i );
        }, i*1000 );
    }

延迟函数的回调能够将新的做用域封闭在每一个迭代内部,每一个迭代中都会含有一个具备正确值的变量供咱们访问

在好比:模块的例子,一个函数返回一个公用的对象接口

function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

同理,调用CoolModule();实现了内部函数引用传递给外部词法做用域 ,foo就能访问CoolModule内部

1. 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会建立一个新的模块
实例)。
2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,并
且能够访问或者修改私有的状态。

实现单例模式

var foo = (function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
})();

 

大量代码例子出自,原谅我剽窃,实在是让人豁然开朗

《Javascript 权威指南》

《你不知道的javascript》

相关文章
相关标签/搜索