前提:JavaScript中閉包無處不在,你只須要可以識別並擁有它。閉包是基於詞法做用域書寫代碼時天然產生的結果。閉包
簡單的來講詞法做用域就是定義在詞法階段的做用域,換就話說,詞法做用域是由你在寫代碼時將變量和塊做用域寫在哪裏來決定的app
function foo(a){ var b = a*2; function bar(c){ console.log(a,b,c); } bar (b*3); } foo(2);
在這個例子中包含了三個逐級嵌套的做用域異步
關於詞法做用域咱們就現講這麼多,接下來仍是回到咱們的正文,做用域閉包函數
function foo(){ var a=2; function bar(){//bar()的詞法做用域可以訪問foo()的內部做用域 console.log(a); } return bar;//將bar()函數當作一個值類型進行傳遞 } var baz =foo(); baz(2);
foo()內部做用域依然存在,沒有被回收。bar()依然持有該做用域的引用。這個引用就叫閉包工具
function foo(){ var a=2; function baz(){ console.log(a); } bar(baz); } function bar(fn){ fn(); } foo(); //把內部函數baz傳遞給bar, // 當調用這個內部函數, // 他涵蓋的foo()內部做用域的閉包就能夠觀察到了,由於它可以訪問a
var fn; function foo(){ var a =2; function baz(){ console.log(a); }; fn = baz; } function bar(){ fn(); } foo(); bar();
function wait(message){ setTimeout(function timer(){ console.log(message) },1000); }; wait("hello world");
在引擎內部,內置的工具函數setTimeout()持有對一個參數的引用,引擎會調用這個函數,在這個例子中就是內部的timer函數,而詞法做用域就在這個過程當中保持完整。這就是閉包。code
for(var i=0;i<=5;i++){ setTimeout(function timer() { console.log(i); }, i*1000); } //你們猜猜結果會是啥?
正常狀況下會分別輸出數字1~5,但實際會輸出五次6。ip
代碼中有什麼缺陷致使它的行爲通語義所暗示的不一致呢?
咱們須要更多的做用域,特別是在循環的過程當中每一個迭代都要一個閉包做用域,所以想到了當即執行函數作用域
for( var i=0;i<=5;i++){ (function(){ setTimeout(function timer() { console.log(i); }, i*1000); })(); }
這樣子爲何還不行呢?咱們顯然擁有了更過的詞法做用域。
每一個延遲函數都會講IIFE在每次迭代中建立的做用域封閉起來。get
for( var i=1;i<=5;i++){ (function(){ var j =i; setTimeout(function timer() { console.log(j); }, j*1000); })(); }
for(let i =1;i<=5;i++){ setTimeout(function timer() { console.log(i); }, i*1000); }
let欺騙此法做用域,每次在迭代都去建立一個新的做用域,而後執行完後被銷燬,這樣每一個迭代都有本身的做用域就能夠達到咱們的預期效果,輸出1~5。it
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.doAnother(); foo.doSomething();
這個模式JavaScript中被稱爲模塊,保護私有屬性,只提供公共方法。
大多數模塊依賴加載器/管理器本質上都是將這種模塊定義封裝進一個友好的API。
var MyModules = (function Manager(){ var modules = {}; function define(name,deps,impl){ for(var i=0;i<deps.length;i++){ deps[i] = module[deps[i]]; } modules[name] = impl.apply(impl,deps); } function get(name){ return modules[name]; }; return{ define: define, get: get }; })(); MyModules.define("bar",[],function(){ function hello(who){ return "Let me introduce:"+ who; } return { hello: hello }; }); MyModules.define("foo",["bar"],function(bar){ var hungry = "hippo"; function awesome(){ console.log(bar.hello(hungry).toUpperCase()); } return { awesome: awesome }; }); var bar = MyModules.get("bar"); var foo = MyModules.get("foo"); // console.log(bar.hello("hippo")); foo.awesome();