摘要:內(nèi)部的稱為內(nèi)部函數(shù)或閉包函數(shù)。過度使用閉包會導(dǎo)致性能下降。,閉包函數(shù)分為定義時,和運行時。循環(huán)會先運行完畢,此時,閉包函數(shù)并沒有運行。閉包只能取得外部函數(shù)中的最后一個值。事件綁定種的匿名函數(shù)也是閉包函數(shù)。而對象中的閉包函數(shù),指向。
閉包概念解釋:
閉包(也叫詞法閉包或者函數(shù)閉包)。
在一個函數(shù)parent內(nèi)聲明另一個函數(shù)child,形成了嵌套。函數(shù)child使用了函數(shù)parent的參數(shù)或變量,那么就形成了閉包。
閉包(closure)是可以訪問外部函數(shù)作用域中的變量或參數(shù)的函數(shù)。
此時,包裹的函數(shù)稱為外部函數(shù)。內(nèi)部的稱為內(nèi)部函數(shù)或閉包函數(shù)。(zyx456自定義:或稱為父函數(shù)和子函數(shù))。
閉包wiki
JS采用詞法作用域(lexical scoping),函數(shù)的執(zhí)行依賴于函數(shù)作用域,這個作用域是在函數(shù)定義時決定的,而不是函數(shù)調(diào)用時決定的。
詞法作用域:詞法作用域也叫靜態(tài)作用域,是指作用域在詞法解析階段就已經(jīng)確定了,不會改變。
動態(tài)作用域:是指作用域在運行時才能確定。
參看下面的例子,引自楊志的回答
var foo=1; function static(){ alert(foo); } !function(){ var foo=2; static(); }(); 在js中,會彈出1而非2,因為static的scope在創(chuàng)建時,記錄的foo是1。 如果js是動態(tài)作用域,那么他應(yīng)該彈出2
zyx456:識別閉包,在詞法分析階段已經(jīng)確定了。
當外部函數(shù)運行的時候,一個閉包就形成了,他由內(nèi)部函數(shù)的代碼以及任何內(nèi)部函數(shù)中指向外部函數(shù)局部變量的引用組成。
注意事項01,閉包函數(shù)作用域中,使用的外部函數(shù)變量不會被立刻銷毀回收,所以會占用更多的內(nèi)存。過度使用閉包會導(dǎo)致性能下降。建議在非常有必要的時候才使用閉包。
02,同一個閉包函數(shù),所訪問的外部函數(shù)的變量是同一個變量。
03,如果把閉包函數(shù),賦值給不同的變量,那么不同的變量指向的是不同的閉包函數(shù),所使用的外部函數(shù)變量是不同的。
04,閉包函數(shù)分為定義時,和運行時。只有運行時,才會訪問外部函數(shù)的變量。
05,在for循環(huán)的閉包函數(shù),只有在運行時,才在作用域中尋找變量。for循環(huán)會先運行完畢,此時,閉包函數(shù)并沒有運行。
06,如果在for循環(huán)中,使用閉包的自執(zhí)行函數(shù)。那么閉包會使用for循環(huán)的變量i(0-*,假設(shè)i從0開始)。
07,一個函數(shù)里,可以有多個閉包。
匿名自執(zhí)行函數(shù),可以封裝私有變量。不會污染全局作用域。匿名函數(shù)中定義的任何變量,都會在執(zhí)行結(jié)束時被銷毀。
eval+with(僅了解)在評論中賀師俊還提到,eval 和 with可以產(chǎn)生動態(tài)作用域的效果:
比如 with(o) { console.log(x) } 這個x實際有可能是 o.x 。所以這就不是靜態(tài)(詞法)作用域了。
var x = 0; void function (code) { eval(code); console.log(x) }("var x=1")
不過注意eval在strict模式下被限制了,不再能形成動態(tài)作用域了。
為什么閉包函數(shù)可以訪問外部函數(shù)的變量?因為閉包函數(shù)的作用域鏈包含了外部函數(shù)的作用域。
如何創(chuàng)建閉包?在一個函數(shù)類內(nèi)創(chuàng)建另外一個函數(shù)。內(nèi)部函數(shù)使用了外部函數(shù)的變量,就形成了閉包。
普通函數(shù)的內(nèi)部函數(shù)是閉包函數(shù)么?zyx456:不是。
函數(shù)第一次被調(diào)用時,會發(fā)生什么?當函數(shù)第一次被調(diào)用時,會創(chuàng)建一個執(zhí)行環(huán)境(execution context)和相應(yīng)的作用域鏈,并把作用域鏈賦值給一個內(nèi)部屬性(即[[Scope]])。
然后,使用this、arguments和其他參數(shù)來初始化函數(shù)的活動對象(activation object)。
在作用域鏈中,內(nèi)部函數(shù)的活動對象處于第一位,外部函數(shù)的活動對象始終處于第二位,外部函數(shù)的外部函數(shù)的活動對象處于第三位,……直至作為作用域鏈終點的全局執(zhí)行環(huán)境。
在函數(shù)執(zhí)行過程中,讀取和寫入變量的值,都需要在作用域鏈中查找變量。
每次調(diào)用JS函數(shù),會為之創(chuàng)建一個新的對象來保存所有的局部變量(函數(shù)定義的變量,函數(shù)參數(shù)。),把這個對象添加到作用域鏈中。函數(shù)體內(nèi)部的變量都保存在函數(shù)作用域內(nèi)。
我們將作用域鏈看做一個對象列表,而不是一個棧。
(zyx456:棧是一種線性表,僅允許在表的一端進行插入和刪除操作。)
當函數(shù)返回的時候,就從作用域鏈中將這個綁定變量的對象刪除。
如果這個函數(shù)不存在嵌套的函數(shù),也沒有其他引用指向這個綁定變量的對象,它就會被當做垃圾回收掉。
(zyx456:這個操作由瀏覽器自動完成)。
如果這個函數(shù)有嵌套的函數(shù),每個嵌套的函數(shù)都各自對應(yīng)一個作用域鏈。
這時:
內(nèi)部函數(shù),被作為返回值返回,或存儲在某處屬性中,就是會有一個外部引用指向這個它,那么它就不會被當做垃圾回收,并且它所使用外部變量所在的對象也不會被當做垃圾回收。只有內(nèi)部函數(shù)被銷毀后,外部函數(shù)的活動對象才會被銷毀。
在函數(shù)中訪問一個變量時,就會從作用域鏈中查找變量。
一般來講,當函數(shù)執(zhí)行完畢后,局部活動對象就會被銷毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的環(huán)境對象)。
但是,閉包的情況又有所不同。閉包函數(shù)的作用域鏈上有外部函數(shù)的作用域鏈。所以閉包函數(shù)可以訪問外部函數(shù)的變量。
閉包函數(shù)必須返回(return)么,return這個閉包函數(shù)?zyx456:不必要返回,只要使用外部函數(shù)的變量即可。
代碼:
function fn1() { var a = 1; function fn2() { console.log(a); } fn2(); } fn1();如果用不同的變量引用函數(shù)中的閉包函數(shù),那么是不同的閉包變量。
簡單的例子:
function outter(){ var x = 0; return function(){ return x++; } } var a = outter(); console.log(a()); console.log(a()); var b = outter(); console.log(b()); console.log(b());
運行結(jié)果為:
0
1
0
1
可以創(chuàng)建私有變量。
因為只有閉包函數(shù)可以訪問外部函數(shù)的變量。
因為在閉包內(nèi)部保持了對外部活動對象的訪問,但外部的變量卻無法直接訪問內(nèi)部,避免了全局污染;
function setMoyu(){ var name = "moyu"; return function(newValue){ name=newValue; console.log(name); } } var setValue = setMoyu(); setValue("world");//world /*zyx456:這時name是私有屬性了,只能通過閉包函數(shù)設(shè)置它*/閉包的缺點?
可能導(dǎo)致內(nèi)存占用過多,因為閉包攜帶了自身的函數(shù)作用域。
閉包只能取得外部函數(shù)中的最后一個值。
作用域:變量聲明如果不使用 var 關(guān)鍵字,那么它就是一個全局變量,即便它在函數(shù)內(nèi)定義。
變量生命周期
全局變量的作用域是全局性的,即在整個JS程序中,全局變量處處都在。
而在函數(shù)內(nèi)部聲明的變量,只在函數(shù)內(nèi)部起作用。
這些變量是局部變量,作用域是局部性的;
函數(shù)的參數(shù)也是局部性的,只在函數(shù)內(nèi)部起作用。
在JS中,所有函數(shù)都能訪問它們上一層的作用域。
例子:
function compare(value1, value2){ if (value1 < value2){ return -1; } else if (value1 > value2){ return 1; } else { return 0; } } var result = compare(5, 10);內(nèi)存泄漏
由于IE 的JS對象和DOM對象使用不同的垃圾收集方式,因此閉包在IE中會導(dǎo)致內(nèi)存泄漏的問題,也就是無法銷毀駐留在內(nèi)存中的元素。
事件綁定種的匿名函數(shù)也是閉包函數(shù)。
如果閉包的作用域鏈中保存著一個HTML元素,那么就意味著該元素將無法被銷毀。
function box() { var oDiv = document.getElementById("oDiv"); //oDiv 用完之后一直駐留在內(nèi)存 oDiv.onclick = function () { alert(oDiv.innerHTML); //這里用oDiv 導(dǎo)致內(nèi)存泄漏 }; } box();
那么在最后應(yīng)該將oDiv 解除引用來避免內(nèi)存泄漏。
function box() { var oDiv = document.getElementById("oDiv"); var text = oDiv.innerHTML; oDiv.onclick = function () { alert(text); }; oDiv = null; //解除引用 }
PS:如果并沒有使用解除引用,那么需要等到瀏覽器關(guān)閉才得以釋放。
閉包和this和arguments
閉包函數(shù)中的this問題對于某個函數(shù)來說,如果函數(shù)在全局環(huán)境中,this指向window。如果在對象中,就指向這個對象。
而對象中的閉包函數(shù),this指向window。因為閉包并不屬于這個對象的屬性或方法。
var user = "The Window"; var obj = { user : "The Object", getUserFunction : function () { return function () { //閉包不屬于obj,里面的this 指向window return this.user; }; } }; alert(obj.getUserFunction()()); //The window //可以強制指向某個對象 alert(obj.getUserFunction().call(obj)); //The Object //也可以從上一個作用域中得到對象 getUserFunction : function () { var that = this; //從對象的方法里得對象 return function () { return that.user; }; }
例子:
var self = this; // 將this保存至一個變量中,以便嵌套的函數(shù)能夠訪問它
綁定arguments的問題與之類似。
arguments并不是一個關(guān)鍵字,但在調(diào)用每個函數(shù)時都會自動聲明它,由于閉包具有自己所綁定的arguments,因此閉包內(nèi)無法直接訪問外部函數(shù)的參數(shù)數(shù)組,除非外部函數(shù)將參數(shù)數(shù)組保存到另外一個變量中:
var outerArguments = arguments; //保存起來以便嵌套的函數(shù)能使用它
在通過call()或apply()改變函數(shù)執(zhí)行環(huán)境的情況下,this就會指向其他對象。
例子:
var scope = "global scope"; // 全局變量 function checkscope() { var scope = "local scope"; // 局部變量 function f() { return scope; } // 在作用域中返回這個值 return f(); } checkscope() // => "local scope"
checkscope()函數(shù)聲明了一個局部變量,并定義了一個函數(shù)f(),函數(shù)f()返回了這個變量的值,最后將函數(shù)f()的執(zhí)行結(jié)果返回。
你應(yīng)當非常清楚為什么調(diào)用checkscope()會返回"local scope"?,F(xiàn)在我們對這段代碼做一點改動。
var scope = "global scope"; // 全局變量 function checkscope() { var scope = "local scope"; // 局部變量 function f() { return scope; } // 在作用域中返回這個值 return f; } checkscope()() // 返回值是什么?//local scope
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106028.html
摘要:和構(gòu)造函數(shù)構(gòu)造函數(shù)可以使用,然后再次創(chuàng)建實例。提供的值被忽略,提供的那些參數(shù)仍然會被前置到構(gòu)造函數(shù)調(diào)用的前面。在這種情況下,指向全局作用域?,F(xiàn)在將作為的方法來調(diào)用,傳入這些實參用于構(gòu)造函數(shù)。 概念 bind() 方法會返回一個新函數(shù)(稱為綁定函數(shù)),綁定函數(shù)與原函數(shù)(使用bind()的函數(shù))具有相同的函數(shù)體,但是綁定函數(shù)有新的this值和參數(shù)。 說白了,bind()就是創(chuàng)建一個有著新t...
摘要:系列目錄復(fù)習資料資料整理個人整理重溫基礎(chǔ)篇重溫基礎(chǔ)對象介紹重溫基礎(chǔ)對象介紹重溫基礎(chǔ)介紹重溫基礎(chǔ)相等性判斷本章節(jié)復(fù)習的是中的關(guān)于閉包,這個小哥哥呀,看看。這里隨著閉包函數(shù)的結(jié)束,執(zhí)行環(huán)境銷毀,變量回收。 本文是 重溫基礎(chǔ) 系列文章的第十九篇。今日感受:將混亂的事情找出之間的聯(lián)系,也是種能力。 系列目錄: 【復(fù)習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎(chǔ)】...
摘要:本文是重溫基礎(chǔ)系列文章的第四篇。系列目錄復(fù)習資料資料整理個人整理重溫基礎(chǔ)語法和數(shù)據(jù)類型重溫基礎(chǔ)流程控制和錯誤處理重溫基礎(chǔ)循環(huán)和迭代本章節(jié)復(fù)習的是中的基礎(chǔ)組件之一,函數(shù),用來復(fù)用特定執(zhí)行邏輯。箭頭函數(shù)不能使用命令,即不能用作函數(shù)。 本文是 重溫基礎(chǔ) 系列文章的第四篇。今日感受:常懷感恩之心,對人對己。 系列目錄: 【復(fù)習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基...
摘要:內(nèi)存泄露內(nèi)存泄露概念在計算機科學中,內(nèi)存泄漏指由于疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存。判斷內(nèi)存泄漏,以字段為準。 本文是 重溫基礎(chǔ) 系列文章的第二十二篇。 今日感受:優(yōu)化學習方法。 系列目錄: 【復(fù)習資料】ES6/ES7/ES8/ES9資料整理(個人整理) 【重溫基礎(chǔ)】1-14篇 【重溫基礎(chǔ)】15.JS對象介紹 【重溫基礎(chǔ)】16.JSON對象介紹 【重溫基礎(chǔ)】1...
摘要:箭頭函數(shù)本文字符數(shù),閱讀時間約分鐘左右。箭頭函數(shù)等于說,只保留了函數(shù)的參數(shù)和返回。箭頭函數(shù)體內(nèi)的,繼承的是外層代碼塊的。所以,不用用箭頭函數(shù)聲明對象的方法。不可以使用命令因此箭頭函數(shù)不能用作函數(shù)。 【01】ES6箭頭函數(shù) 本文字符數(shù)4300+,閱讀時間約8分鐘左右。 【01】箭頭函數(shù) 等于說,只保留了函數(shù)的參數(shù)和返回。省略function和return。 寫法: (形參) => {st...
閱讀 2580·2021-09-06 15:02
閱讀 3213·2021-09-02 10:18
閱讀 2835·2019-08-30 15:44
閱讀 695·2019-08-30 15:43
閱讀 1959·2019-08-30 14:08
閱讀 2767·2019-08-30 13:16
閱讀 1408·2019-08-26 13:52
閱讀 939·2019-08-26 12:21