關(guān)于閉包的3要點
1 js允許引用函數(shù)外部的變量
2 函數(shù)能夠引用外部函數(shù)赤屋,即使函數(shù)已經(jīng)返回,一個函數(shù)能夠引用任何作用域內(nèi)的變量抗斤,包括參數(shù)和函數(shù)外部的變量
function sandwichMaker(ingredient) {
return function(filling) {
console.log(ingredient + " and " + filling);
};
}
var make = sandwichMaker("apple");
make("butter"); // "appele and butter"
// 內(nèi)部返回的匿名函數(shù)可以訪問sandwichMaker中參數(shù)蹈垢,即使sandwichMaker已經(jīng)return了
// javascript中function是firs-class citizen, 意味著函數(shù)可以作為參數(shù)或者返回值
3 閉包能夠更新外部變量的值吗讶,實際上閉包是通過引用的方式來與外部變量建立聯(lián)系燎猛,而不是拷貝外部變量的值
function box() {
var val = undefined;
return {
get: function() { return val; },
set: function(newVal) { val = newVal;},
type: function() { typeof val; }
};
}
var b = box();
b.type(); // undefined
b.set("hello");
b.get(); // "hello"
b.type(); // "string"
變量提升(Hoisting)
ES5之前,javascript是沒有塊級作用域的关翎,所有的變量都是通過<b><i>var</i></b> 來聲明扛门,ES6引入<b><i>let</i></b> 關(guān)鍵字,并引入塊級作用域的概念.
1.隱式提升變量聲明位置纵寝,在var聲明處賦值
function f() { function f() {
// ... var x; // 提升到此處聲明
// ... // ...
{ {
var x = 15; =>實際上 x = 15 // 此處賦值
// .... // ...
} }
} }
使用函數(shù)表達(dá)式時注意论寨,只有變量提升
hello();
function hello() { // 函數(shù)statement
console.log('hello')
}
great();
var great = function() { // 函數(shù)表達(dá)式
console.log('great')
}
// 運(yùn)行結(jié)果
‘hello’
Reference Error
報錯的原因在于星立, var great 中 只有變量 'great' 提升,而匿名函數(shù)并沒有提升
let塊級作用域葬凳, 變量不再隱式提升
常見陷阱
function wrapElements(a) {
var result = [];
for (var i = 0, n < a.length; i < n; i++) {
resule[i] = function() { return a[i]; };
}
return result;
}
var wrapped = wrapElements([10, 20, 30]);
wrapped[0](); // ?
看起來沒什么問題绰垂,應(yīng)該返回10, 但是實際上返回undefined火焰,函數(shù)wrapElements綁定3個局部變量<b>i, n, result</b>,在調(diào)用wrapElements函數(shù)時在內(nèi)存中為每一個變量分配一個"槽點"(slot)用來存儲變量所賦的值劲装,每次循環(huán)時,內(nèi)部匿名函數(shù)形成一個<b>閉包</b>昌简,引用<b>i</b>占业,我們期望的是 i 的值每次循環(huán)匿名函數(shù)都能夠存儲 i 的值,實際上i是引用值纯赎,每次循環(huán)之后都會改變,內(nèi)部函數(shù)最終存儲的是i最后的值谦疾,關(guān)鍵點在于: <b>Closure store their outer variables by reference, not by value</b>.
更改方法:
// 1.使用IIEF模擬塊級作用域,立即執(zhí)行每一次內(nèi)部函數(shù)的調(diào)用
function wrapElements(a) {
var result = [];
for (var i = 0, n < a.length; i < n; i++) {
(function(j) {
resule[i] = function() { return a[j]; };
})(i)
}
return result;
}
// 2.使用ES6 let 關(guān)鍵詞創(chuàng)建塊級作用域
function wrapElements(a) {
var result = [];
for (let i = 0, n < a.length; i < n; i++) {
resule[i] = function() { return a[i]; };
}
return result;
}