關於JS中閉包的問題

来源:https://www.cnblogs.com/yuanzhangya/archive/2018/12/04/10065533.html
-Advertisement-
Play Games

一直以來,我都以為我已經懂了JavaScript中 閉包 的概念,直到有一次小伙伴突然問我這個概念的時候,我才發現我根本不知道該怎來麽跟他來講述這個概念。 那時候我就知道我是自我欺騙,打腫臉充胖子了。 所以,花了點時間去專門瞭解了一下,今天專門記錄一下自己所理解的閉包。 一. 概念 閉包,簡單來講, ...


一直以來,我都以為我已經懂了JavaScript中閉包的概念,直到有一次小伙伴突然問我這個概念的時候,我才發現我根本不知道該怎來麽跟他來講述這個概念。

那時候我就知道我是自我欺騙,打腫臉充胖子了。

所以,花了點時間去專門瞭解了一下,今天專門記錄一下自己所理解的閉包。

一. 概念

閉包,簡單來講,就是定義在函數內部的函數,使用閉包,可以讓你有權訪問另一個函數作用域內的變數。

所以,想要瞭解閉包的前提是,你首先要知道在JS中變數作用域的問題。

創建閉包的常見方式就是在函數內部去創建另一個函數:

function fun() {
    var variable = 'Hello World';
    function inner() {
        console.log(variable);
    }
    return inner;
}

var outer = fun();
outer(); // Hello World



在這個例子中,我們想在外部用到fun()中定義的variable的值,但是因為變數作用域的問題,我們不可能直接取到。

所以我們採取了變通的方法:在fun()函數內部又創建了一個函數inner(),這時fun()內部的variable對於inner()來說是可見的,既然inner()可以取到fun()中的變數,那麼我們將inner()返回,就可以用到fun()中定義的variable了。

閉包在此處,就是鏈接函數內部和外部的一個橋梁。

在這裡提一句:如果inner()內部存在新設置的變數,對於fun()函數來說是不可見的,此處涉及到JS中作用鏈的問題,理解作用鏈對於徹底理解閉包的問題很有幫助,可以參考JavaScript高級程式設計(第四章)去瞭解一下作用鏈。

其實閉包的定義也就這麼簡單,對於那些過於抽象的定義,置之不理即可,不用強迫自己去理解那些比較晦澀難懂的專業定義,記住自己最終的目的並不是為了咬文嚼字,實用才是根本。

最後借用知乎上一個回答來形象的描述一下閉包的概念:



二. 閉包的用處

我總結的閉包主要用處:

  1. 讓外部可以讀取函數內部的變數。
  2. 可以封裝對象的私有屬性和私有方法。

第一點用處就是在說閉包概念時候所舉的例子。

下麵說下第二點用處:可以封裝對象的私有屬性和私有方法。

function Worker(name) {
    var _salary;
    function setSalary(value) {
        _salary = value;
    }
    function getSalary() {
        return _salary;
    }
    
    return {
        name: name,
        setSalary; setSalary;
        getSalary: getSalary;
    }
}

var cxk = Worker('cXK');
cxk.setSalare(100);
cxk.getSalary(); // 100



在上面的代碼中,通過閉包,_salary變成了cxk的私有變數。

三. 需要註意的地方

第一點需要註意的地方是關於使用閉包時記憶體的問題,因為閉包會攜帶包含它的函數的作用域,因此會比其他的函數占用更多的記憶體,濫用閉包會造成網頁的性能問題,所以對於閉包,建議只在絕對必要時在考慮使用。

對於閉包中垃圾回收的詳細測試,參考js閉包測試/司徒正美

第二點需要註意的就是在創建閉包時可能會常犯的錯誤:在迴圈中的閉包創建問題。

function createArray() {
  var result = [];
  for (var i = 0; i < 10; i++) {
    result[i] = function () {
      return i;
    }
  }
  return result;
}

var arr = createArray();
arr[1](); // 10
arr[2](); // 10



可以看到,跟我們預期達到的結果不一樣,每一個位置上的函數都返回了10。

這是因為每一個result[i]上都保存著createArray()函數的活動對象(參考JS中的作用鏈),而給result[i]進行賦值時,'function(){return i}'沒有執行,所以最後在arr[1]運行時,返回的i其實都是同一個值,即最後生成的i,值為10。

可以做出如下修改。

修改一:在閉包里再添加一個閉包函數,同時立即執行。

function createArray() {
  var result = [];
  for (var i = 0; i < 10; i++) {
    result[i] = function (num) {
      return function () {
        return num;
      }
    }(i)
  }
  return result;
}

var arr = createArray();
arr[1]();



修改二:修改varlet

function createArray() {
  var result = [];
  for (let i = 0; i < 10; i++) {
    result[i] = function () {
      return i;
    }
  }
  return result;
}

var arr = createArray();
arr[1]();



以上就是我對閉包的比較淺顯的認知,如果有不對的地方,希望能夠指正,以免我誤人子弟,謝謝。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼是Aurelia? Aurelia 是一個新的開源的,基於web標準的mvvm框架,是一個現代化的js模塊的集合。 Aurelia提供了豐富的plugin,例如國際化,驗證,模態框,UI可視化等。 其強大的binding模塊和template模塊,能夠幫助你更專註於你的業務邏輯,寫出清晰高效的代 ...
  • 使用的是Node.js作為後端 統一下單: appid:這裡的appid是調起微信支付的appid mch_id:商戶號,需要註意的是商戶號要與appid對應 nonce_str:Math.random().toString(36).substr(2)這是我的隨機字元串的生成演算法 sign:這裡的簽 ...
  • ①為什麼要使用原型:為了實現繼承。 ②利用constructor屬性可以讓實例化對象輕鬆訪問原型,實現實例化對象對原型對象的修改,但是原型對象是全局對象,一般不能隨意修改原型對象的成員。該屬性多用於調試。 ③原型是構造函數的屬性,原型是實例化對象的原型對象。 ④實例化對象如何訪問原型對象: func ...
  • html : 1、相當於沒有穿衣服的人,一套瀏覽器認識的規則, 2、開發者: 學習html規則 開發後臺程式: -寫html文件(充當模板) -資料庫獲取數據,然後替換到html文件的指定位置(web框架) 3、本地測試 -找到文件路徑,直接用瀏覽器打開 -用pycharm打開測試 4、編寫html ...
  • 類數組對象:arguments總所周知,js是一門相當靈活的語言。當我們在js中在調用一個函數的時候,我們經常會給這個函數傳遞一些參數,js把傳入到這個函數的全部參數存儲在一個叫做arguments的東西裡面,那麼這到底是什麼東西?在js中萬物皆對象,甚至數組字元串函數都是對象。所以這個叫做argu ...
  • js代碼: ...
  • /* * 中間就可以進行封裝操作 * mui就代表mui,owner就代表window的app屬性,就是一個傳值 */ (function(mui,owner) { /** * 獲取當前狀態 **/ owner.getState = function() { var stateText = plus ...
  • 本文由QQ音樂前端團隊發表 前段時間做了一個非常有意思的模擬終端的展示頁:http://ursb.me/terminal/(沒有做移動端適配,請在PC端訪問),這個頁面非常有意思,它可以作為個人博客系統或者給 Linux 初學者學習終端命令,現分享給大家~ 開源地址:airingursb/termi ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...