這篇文章帶給大家的內容是關於帶你進一步理解js閉包(詳細),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
譯者:閉包都被討論爛了,不理解閉包都不好意思說自己會js,但我看到這篇文章還是感覺眼前一亮,也讓我對閉包有了一些新的理解,並且涉及了一些類和原型鏈的知識,這是一篇2012年的文章,稍微有點早,內容也略微基礎,但是很明晰,希望能給讀者帶來新的理解。
閉包(Closure) 是javascript這門語言中有些複雜且充滿誤解的特性。簡言之,閉包是一個對象,這個物件包含一個方法(function)和該方法創建時環境的引用(reference to the enviroment)。為了完全理解閉包,我們還需要理解兩個js中的特性,一個是一級方法(first-class function),另一個是內部方法(inner function)。
一級方法/First-Class Functions
在js中,方法是頭等公民,因為它可以輕易轉換成其他資料型別。例如,一級方法可以即時建構並且賦值給一個變數。也可以傳遞給其他方法,或透過其他方法返回。除了滿足這些標準以外,方法也擁有自己的屬性和方法。
透過下述例子,我們來看一級方法的能力。
var foo = function() { alert("Hello World!"); }; var bar = function(arg) { return arg; }; bar(foo)();譯者註:省略原文對程式碼的文字解釋,這裡體現的是一級方法可以回傳參數,參數可以是另一個一級函數,回傳的結果還可以呼叫。
內部方法/Inner Functions
內部方法或說嵌套方法,是指定義在其他方法內部的方法,每當外部方法被喚起,內部方法的實例就被創建。下面的範例反應內部方法的使用,add方法是外部方法,doAdd是內部方法。
function add(value1, value2) { function doAdd(operand1, operand2) { return operand1 + operand2; } return doAdd(value1, value2); } var foo = add(1, 2); // foo equals 3
這個例子中,一個重要的特性是,內部方法獲取到了外部方法的作用域,這意味著內部方法能夠使用外部方法的變量,參數等。範例中add()的參數value1,value2傳遞給doAdd()的operand1,operand2參數。然而這並沒有必要,因為doAdd可以直接取得value1,value2。所以上面的例子我們還可以這麼寫:
function add(value1, value2) { function doAdd() { return value1 + value2; } return doAdd(); } var foo = add(1, 2); // foo equals 3
建立閉包/Creating Closures
內部方法取得外部方法的作用域,便形成了一個閉包。典型的場景是外部函數將其內部方法傳回,內部方法保持了外部環境的引用,並保存了作用域下的所有變數。
一下範例展示閉包如何建立並使用。
function add(value1) { return function doAdd(value2) { return value1 + value2; }; } var increment = add(1); var foo = increment(2); // foo equals 3
說明:
add傳回了內部方法doAdd,doAdd呼叫了add的參數,閉包建立。
value1是add方法的本機變量,對doAdd來說是非本機變數(非本機變數指變數既不在函數體本身,也不在全域),value2是doAdd的本機變數。
當add(1)被調用,一個閉包被創建並儲存在increment中,在該閉包的引用環境中,value1綁定了1,被綁定的1相當於「封鎖」在這個函數中,這也是「閉包」這個名字的由來。
當increment(2)被調用,進入閉包函數,這意味著攜帶value1為1的doAdd被調用,因此該閉包本質上可以當做如下函數:
function increment(value2) { return 1 + value2; }
何時使用閉包?
閉包可以實現很多功能。例如將回調函數綁定指定參數。我們說兩個讓你的生活和開發變得更簡單的場景。
配合定時器
閉包結合setTimeout和setInterval非常有用,閉包可讓你向回呼函數傳入指定參數,例如下面的例子,每秒鐘在給指定dom插入字串。
nbsp;html> <title>Closures</title> <meta> <script> window.addEventListener("load", function() { window.setInterval(showMessage, 1000, "some message<br />"); }); function showMessage(message) { document.getElementById("message").innerHTML += message; } </script> <span></span>
可惜的是,IE不支援向setInterval的回呼傳參,IE中頁面不會展現「some message」而是「undefined」(無值傳入showMessage()),解決這個問題,可以透過閉包將期望值綁定於回呼函數裡,我們可以改寫如上程式碼:
window.addEventListener("load", function() { var showMessage = getClosure("some message<br>"); window.setInterval(showMessage, 1000); }); function getClosure(message) { function showMessage() { document.getElementById("message").innerHTML += message; } return showMessage; }
2.模擬私有屬性
絕大多數物件導向的程式語言支援物件的私有屬性,然而js不是純正的物件導向的語言,因此也沒有私有屬性的概念。不過,我們可以透過閉包來模擬私有屬性。回想一下,閉包包含了一份其創建環境的引用,這份引用已經不在當前作用域中了,因此這份引用只能在閉包中訪問,這本質上就是私有屬性。
看如下例子(譯者:省略對程式碼的文字描述):
function Person(name) { this._name = name; this.getName = function() { return this._name; }; }
這裡有一個嚴重的問題,因為js不支援私有屬性,所以我們沒辦法阻止別人修改實例的name字段,例如我們建立一個Person實例叫Colin,然後可以將他的名字改成Tom。
var person = new Person("Colin"); person._name = "Tom"; // person.getName() now returns "Tom"
没有人愿意不经同意就被别人改名字,为了阻止这种情况的发生,通过闭包让_name字段变成私有。看如下代码,注意这里的_name是Person构造器的本地变量,而不是对象的属性,闭包形成了,因为外层方法Person对外暴露了一个内部方法getName。
function Person(name) { var _name = name;// 注:区别在这里 this.getName = function() { return _name; }; }
现在,当getName被调用,能够保证返回的是最初传入类构造器的值。我们依然可以为对象添加新的_name属性,但这并不影响闭包getName最初绑定的值,下面的代码证明,_name字段,事实私有。
var person = new Person("Colin"); person._name = "Tom"; // person._name is "Tom" but person.getName() returns "Colin"
什么时候不要用闭包?
正确理解闭包如何工作何时使用非常重要,而理解什么时候不应该用它也同样重要。过度使用闭包会导致脚本执行变慢并消耗额外内存。由于闭包太容易创建了,所以很容易发生你都不知道怎么回事,就已经创建了闭包的情况。本节我们说几种场景要注意避免闭包的产生。
1.循环中
循环中创建出闭包会导致结果异常。下例中,页面上有三个按钮,分别点击弹出不同的话术。然而实际运行,所有的按钮都弹出button4的话术,这是因为,当按钮被点击时,循环已经执行完毕,而循环中的变量i也已经变成了最终值4.
nbsp;html> <title>Closures</title> <meta> <script> window.addEventListener("load", function() { for (var i = 1; i < 4; i++) { var button = document.getElementById("button" + i); button.addEventListener("click", function() { alert("Clicked button " + i); }); } }); </script> <input> <input> <input>
去解决这个问题,必须在循环中去掉闭包(译者:这里的闭包指的是click事件回调函数绑定了外层引用i),我们可以通过调用一个引用新环境的函数来解决。下面的代码中,循环中的变量传递给getHandler函数,getHandler返回一个闭包(译者:这个闭包指的是getHandler返回的内部方法绑定传入的i参数),独立于原来的for循环。
function getHandler(i) { return function handler() { alert("Clicked button " + i); }; } window.addEventListener("load", function() { for (var i = 1; i <p>2.构造函数里的非必要使用<br>类的构造函数里,也是经常会产生闭包的错误使用。我们已经知道如何通过闭包设置类的私有属性,而如果当一个方法不需要调用私有属性,则造成的闭包是浪费的。下面的例子中,Person类增加了sayHello方法,但是它没有使用私有属性。</p><pre class="brush:php;toolbar:false">function Person(name) { var _name = name; this.getName = function() { return _name; }; this.sayHello = function() { alert("Hello!"); }; }
每当Person被实例化,创建sayHello都要消耗时间,想象一下有大量的Person被实例化。更好的实践是将sayHello放入Person的原型链里(prototype),原型链里的方法,会被所有的实例化对象共享,因此节省了为每个实例化对象去创建一个闭包(译者:指sayHello),所以我们有必要做如下修改:
function Person(name) { var _name = name; this.getName = function() { return _name; }; } Person.prototype.sayHello = function() { alert("Hello!"); };
需要记得一些事情
闭包包含了一个方法,以及创建它的代码环境引用
闭包会在外部函数包含内部函数的情况下形成
闭包可以轻松的帮助回调函数传入参数
类的私有属性可以通过闭包模拟
类的构造器中使用闭包不是一个好主意,将它们放到原型链中
以上是帶你進一步理解js閉包(詳細)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 Linux新版
SublimeText3 Linux最新版

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能