閉包已經成爲近乎神話的概念,它很是重要又難以掌握,並且還難以定義。本文就從閉包的定義說開去編程
閉包(closure),是指函數變量能夠保存在函數做用域內,所以看起來是函數將變量「包裹」了起來閉包
那這樣說來,包含變量的函數就是閉包模塊化
//按照古老定義,包含變量n的函數foo就是閉包 function foo() { var n = 0; } console.log(n)//Uncaught ReferenceError: n is not defined
閉包是指能夠訪問其所在做用域的函數函數
那這樣說來,須要經過做用域鏈查找變量的函數就是閉包ui
//按照定義一的說法,須要經過做用域鏈在全局環境中查找變量n的函數foo()就是閉包 var n = 0; function foo() { console.log(n)//0 } foo();
閉包是指有權訪問另外一個函數做用域中的變量的函數spa
那這樣說來,訪問上層函數的做用域的內層函數就是閉包code
//按照定義二的說法,嵌套在foo函數裏的bar函數就是閉包 function foo(){ var a = 2; function bar(){ console.log(a); // 2 } bar(); } foo();
閉包是指在函數聲明時的做用域之外的地方被調用的函數blog
在函數聲明時的做用域之外的地方調用函數,須要經過將該函數做爲返回值或者做爲參數被傳遞接口
【1】返回值作用域
//按照定義三的說法,在foo()函數的做用域中聲明,在全局環境的做用域中被調用的bar()函數是閉包 function foo(){ var a = 2; function bar(){ console.log(a); //2 } return bar; } foo()();
能夠簡寫爲以下表示:
function foo(){ var a = 2; return function(){ console.log(a);//2 } } foo()();
【2】參數
//按照定義三的說法,在foo()函數的做用域中聲明,在bar()函數的做用域中被調用的baz()函數是閉包 function foo(){ var a = 2; function baz(){ console.log(a); //2 } bar(baz); } function bar(fn){ fn(); }
所以,不管經過何種手段,只要將內部函數傳遞到所在的詞法做用域之外,它都會持有對原始做用域的引用,不管在何處執行這個函數都會使用閉包
IIFE是否是閉包呢?
foo()函數在全局做用域定義,也在全局做用域被當即調用,若是按照定義一的說法來講,它是閉包。若是按照定義二和定義三的說法,它又不是閉包
var a = 2; (function foo(){ console.log(a);//2 })();
還有一個更重要的緣由是,在requireJS出現以前,實現模塊化編程主要經過IIFE,而在IIFE中常見的操做就是經過window.fn = fn來暴露接口,而這個fn就是閉包,而IIFE只是一個包含閉包的函數調用
(function(){ var a = 0; function fn(){ console.log(a); } window.fn = fn; })() fn();
閉包定義之因此混亂,我以爲與經典書籍的不一樣解讀有關。經典定義是犀牛書的原話,定義二是高程的原話
但,概括起來就是關於一個函數要成爲一個閉包到底須要滿意幾個條件
嚴格來講,閉包須要知足三個條件:【1】訪問所在做用域;【2】函數嵌套;【3】在所在做用域外被調用
有些人以爲只知足條件1就能夠,因此IIFE是閉包;有些人以爲知足條件1和2才能夠,因此被嵌套的函數纔是閉包;有些人以爲3個條件都知足才能夠,因此在做用域之外的地方被調用的函數纔是閉包
問題是,誰是權威呢?