91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javascript如何定義閉包

發布時間:2020-09-09 09:33:24 來源:億速云 閱讀:165 作者:小新 欄目:web開發

這篇文章主要介紹javascript如何定義閉包,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

前言

閉包 永遠都是前端開發者繞不過去的一個坎,不管你喜歡與否,在工作和面試中,都會遇到。每個人對閉包的理解都不盡相同,這里筆者談談自身對閉包的理解。(如果與您的理解有出入,請以您自己為準  )

如何定義閉包

在給出定義之前,不妨看看別人是如何定義閉包的:

函數對象可以通過作用域鏈相互關聯起來,函數體內部的變量都可以保存在函數作用域內,這種特性在計算機科學文獻中稱為“閉包” -- JavaScript權威指南(第六版)

閉包是指有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。 -- JavaScript高級程序設計(第三版)

當函數可以記住并訪問所在的詞法作用域時,就產生了閉包,即使函數是在當前詞法作用域之外執行。 -- 你不知道的JavaScript(上卷)

雖然上面的幾段話描述起來并不一樣,但是您細細品味后還是能找出一些共同點。其中最重要的是不同作用域之間的聯系。當然了,您可以直接引用上面的定義(畢竟上面幾個定義還是比較權威的),這里筆者比較喜歡最后一段的定義,同時力推《你不知道的JavaScript(上卷)》這本書,值得反復細讀。

閉包涉及哪些知識點

光給出定義是遠遠不夠的,還必須探討內部涉及了哪些知識點。下面是筆者認為有用到的知識點。

作用域與作用域鏈

嗯,其實筆者知道你們都想到了這點(不會吧,不會有人沒想到這點吧)。既然大家都了解作用域。這里就簡單描述一下,過一下場即可。

作用域:根據名稱查找變量的一套規則。分為三種類型:全局作用域;函數作用域;塊作用域。

需要注意的是塊作用域,ES6新增的規范。 在花括號{}里面使用let,const定義的變量,都會綁定到該作用范圍內,花括號以外的地方無法訪問。注意:在花括號開始 到 let變量聲明之前,存在暫時性死區(該點不在本文討論范圍)。

作用域鏈:當不同的作用域 (混~淆~在~一~起~ 呸,不小心出戲了) 圈套在一起時,就形成了作用域鏈。注意的是,查找方向是從內到外的。

為什么作用域的查找方向是從內到外的呢?這是個很有趣的問題。個人覺得是跟js執行函數的入棧方式決定的(感覺有點偏題了,有興趣的小伙伴可以去查一下資料)。

詞法作用域

函數之所以 可以訪問另一個函數作用域的變量(或者說記住當前的作用域并在當前以外的地方訪問)的關鍵點就是詞法作用域在起作用。這一點很重要,但不是所有人都知道這個知識點,這里簡單探討一下。

在編程界中,存在兩種作用域工作模式,一種是被大多數編程語言所采用的詞法作用域;另一種就是與其相反的動態作用域(這個不在本文的討論范圍)。

詞法作用域: 變量和塊的作用域 在 您編寫代碼的階段 就已經確定好了,不會隨著調用的對象或者地方的不同而改變(感覺跟this相反)。

要不,舉個栗子看看吧:

let a = 1;function fn(){    let a = 2;    function fn2(){        console.log(a);
    } return fn2;
}let fn3 = fn();
fn3();復制代碼

從上面的定義可以知道,fn是一個閉包函數,fn3拿到了fn2的指針地址,當fn3執行的時候,其實是執行fn2,而里面的a變量,根據作用域鏈的查找規則,找到的是fn作用域內的變量a,所以最終的輸出是2,不是1。(可以看下圖)

題外話,如何欺騙詞法作用域?

雖然詞法作用域是靜態的,但依然有辦法可以欺騙它,達到動態的效果。

第一種方法是使用eval. eval可以把字符串解析成一個腳本來運行,由于在詞法分析階段,無法預測eval運行的腳本,所以不會對其進行優化分析。

第二種方法是with. with通常被當作重復引用同一個對象中的多個屬性的快捷方式,可以不需要重復引用對象本身。with本身比較難掌握,使用不當容易出現意外情況(如下例子),不推薦使用 -.-

function Fn(obj){    with(obj){
        a = 2;
    }
}var o1 = {    a:1}var o2 = {    b:1}

Fn(o1);console.log(o1.a); //2Fn(o2);console.log(o2.a); //undefined;console.log(a); //2 a被泄漏到全局里面去了// 這是with的一個副作用, 如果當前詞法作用域沒有該屬性,會在全局創建一個復制代碼

閉包能干啥?

閉包的使用場景可多了,平時使用的插件或者框架,基本上都有閉包的身影,可能您沒留意過罷了。下面筆者列舉一些比較常見的場景。

  1. 模擬私有變量和方法,進一步來說可以是模擬模塊化;目前常用的AMD,CommonJS等模塊規范,都是利用閉包的思想;

  2. 柯里化函數或者偏函數;利用閉包可以把參數分成多次傳參。如下面代碼:

// 柯里化函數function currying(fn){    var allArgs = [];    function bindCurry(){        var args = [].slice.call(arguments);
        allArgs = allArgs.concat(args);        return bindCurry;
    }
    bindCurry.toString = function(){        return fn.apply(null, allArgs);
    };    return bindCurry;
}復制代碼
  1. 實現防抖或者節流函數;

  2. 實現緩存結果(記憶化)的輔助函數:

// 該方法適合緩存結果不易改變的函數const memorize = fn => {    let memorized = false;    let result = undefined;    return (...args) => {        if (memorized) {            return result;
        } else {
            result = fn.apply(null,args); 
            memorized = true;
            fn = undefined;            return result;
        }
    };
};復制代碼

如何區分閉包?

說了那么多,我怎么知道自己寫的代碼是不是閉包呢?先不說新手,有些代碼的確隱藏的深,老鳥不仔細看也可能發現不了。 那有沒有方法可以幫助我們區分一個函數是不是閉包呢?答案是肯定的,要學會善于利用周邊的工具資源,比如瀏覽器。

打開常用的瀏覽器(chrome或者其他),在要驗證的代碼中打上debugger斷點,然后看控制臺,在scope里面的Closure(閉包)里面是否有該函數(如下圖)。

閉包真的會導致內存泄漏?

答案是有可能。內存泄漏的原因在于垃圾回收(GC)無法釋放變量的內存,導致運行一段時候后,可用內存越來越少,最終出現內存泄漏的情況。常見的內存泄漏場景有4種:全局變量;閉包引用;DOM事件綁定;不合理使用緩存。其中,閉包導致內存泄漏都是比較隱蔽的,用肉眼查看代碼判斷是比較難,我們可用借助chrome瀏覽器的Memory標簽欄工具來調試。由于篇幅問題,不展開說明了,有興趣自己去了解一下如何使用。

以上是javascript如何定義閉包的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

马鞍山市| 新闻| 云和县| 阿巴嘎旗| 宣汉县| 砚山县| 阿合奇县| 兴海县| 广汉市| 衡山县| 逊克县| 蒲城县| 德格县| 石景山区| 什邡市| 曲沃县| 凤山县| 大港区| 南陵县| 沾化县| 长武县| 加查县| 温泉县| 林甸县| 绥芬河市| 菏泽市| 舒兰市| 长丰县| 海安县| 西藏| 荃湾区| 南部县| 胶州市| 阿荣旗| 措美县| 平潭县| 金塔县| 常德市| 佳木斯市| 札达县| 司法|