相信很多人只知道閉包這個詞但是具體是怎麼回事就不太清楚了,最近在群里有很多小伙伴討論這個問題但還是矇矓矓的趕腳。索性就寫了這篇文章來幫助大家一起理解閉包。 變數作用域 閉包其實想明白了很簡單,但是在理解閉包之前,我們先溫習一下作用域的概念不多說 直接上代碼來的直接 全局變數 局部變數 我們大家都知道 ...
相信很多人只知道閉包這個詞但是具體是怎麼回事就不太清楚了,最近在群里有很多小伙伴討論這個問題但還是矇矓矓的趕腳。索性就寫了這篇文章來幫助大家一起理解閉包。
變數作用域
閉包其實想明白了很簡單,但是在理解閉包之前,我們先溫習一下作用域的概念不多說 直接上代碼來的直接
全局變數
var a = 1; function f1(){ a++; console.log(a); } f1();//2 f1();//3
局部變數
function f1(){ var a = 1; a++; console.log(a); } f1();//2 f1();//2
我們大家都知道在函數內部定義的變數(使用
var
)為局部變數,在函數內部可以訪問函數外部的變數和函數,但是在函數外部就訪問不了函數內部的變數和函數了
閉包就是能夠讓我們在函數外部能訪問到函數內部的變數和函數下麵我們看一個史上最簡單的一個閉包,真的不能再簡單,你相信我
function f1(){
var str ='我在函數裡面,外面的壞人找不到我!!哈哈';
var aa = function(){
alert(str);
}
return aa;
}
var b = f1();
b();
在這個例子中f1中的函數aa
就是一個閉包,什麼 aa
不是一個函數麽?那閉包也是一個函數咯? 對,閉包說白了 就是一個函數,只不過這個函數比較特殊而已,特殊到什麼地方,從例子不難發現 aa
是聲明在函數f1
內部的,同時又把這個函數aa
當成返回值給return 出來了。然後 我們執行 var b = f1()
就相當於 var b = aa
但是 aa
是在函數內部定義的 通常情況下外部是無法訪問的,這個時候我們就把這個函數aa
當成函數的返回值給拋出去,這樣在外層就能訪問到 aa
了 由於 aa
又是在函數f1
內部聲明的 所以 我們變相的可以訪問到函數f1
中的變數。這就是閉包最基本的作用
同時它還有很多好處:
- 希望一個變數長期駐扎在記憶體中
- 避免全局變數的污染
- 私有成員的存在
閉包的特性:
1.函數嵌套函數
2.函數內部可以引用外部的參數和變數
3.參數和變數不會被垃圾回收機制回收
簡單的相信大家都能看懂,但是遇見覆雜的就矇蔽的,特別是和對象在一塊
就拿今天群里討論的一個例子來說吧
var sister = '大桃花';
var obj = {
sister:'大妹噠',
sis:function(){
var sister = '小妹噠';
return function(){
console.log(sister);
console.log(this.sister);
};
}
};
function place(){
var sister = '大福晉';
var girl = obj.sis();
girl();
}
place();
在這個例子中 糊弄人的就是 sister
在不同的作用域中都有定義,大兄弟莫慌張!我們一起來揭開它這神秘的面紗,看看到底是大妹噠還是小妹噠,place
調用 place
內部聲明瞭一個 sister
然後緊接著 又定義一個 girl = obj.sis()
;obj的sis方法 的返回值是一個函數 也就是說 girl
最後被賦值為一個函數,先不要管這個函數是在哪,反正就是一個函數,然後緊接著girl
被調用,再被調用之前 你要明白下麵的這些東西函數聲明的時候會創建一個作用域鏈 這個鏈條上面都有神馬東西
this
- 函數內部的變數 --->此函數聲明時所在的作用域中的變數 --->....--->
window
(瀏覽器中的最頂層) - 函數參數(
arguments
)
這3項中其中後兩項是在函數聲明的時候就被確定下來了,也就是說 函數的作用域鏈條是在函數聲明的時候就確定了,而非是函數調用的時候,然而this
在函數聲明是時不確定,而是在函數被調用的時候所確定的,至於怎麼確定那就要看函數被調用時所在的對象,函數被調用無非就2中形式:
- 直接調用(比如
getName()
) - 通過對象.函數的形式調用(比如
obj.getName()
)
這兩種方式調用 第一種的調用方式中 函數中的this
是永遠指向 window
的(這裡拋開 call
和apply
);而第二種調用方式 函數中(這裡通常稱作方法)的this 是指向點前面的obj
下麵我們再看上面的例子
先說 console.log(sister)
由於girl是一個函數的引用 ,而這個函數是在 obj.sis 函數中聲明的 只不過是一個匿名函數而已,由於這個匿名函數內部和匿名函數的參數中都沒有 sister
這個變數, 所以順著作用域鏈往上查找 找到 obj.sis
函數的作用中聲明瞭 sister
這個變數 找到之後就不再去往上查找(js中的很多機制都是這中情況 比如 原型集成中對象屬性的查找規則也類似這樣)這個時候 控制台就會輸出 obj.sis
中定義的 sister
變數的值。
再來看 console.log(this.sister)
girl
這個函數被調用時的環境 是 place
函數內部 而 place
是在整個window
作用域下的 所以 這裡的this
是指向 window
的 因而 控制台輸出結果 會是 window
下的 sister
再看下麵的例子
var a = 'window';
var sys = {
a:'sys',
getA:function(){
alert(this.a);
},
getAA:function(){
return function(){
alert(this.a);
}
}
}
sys.getA();//sys
sys.getAA()();//window
這個例子中sys.getA()
不用說 大家都能理解,第二個 可以這麼來看 先看sys.getAA()
這個結果其實是一個函數你可以把他當成一個函數的名字 a
,變換一下寫法 就是 sys.getAA()()
等價於:
var a = sys.getAA();
a();
由於 這是函數的直接調用 所以 這裡的this
指向 window
最後結果 就是 'window'.
就先說這麼多吧 思路可能有點混亂,如果你有什麼好的建議或者意見,歡迎指正!