目錄
1. 學習JavaScript的契機
2.'噁心'的閉包
3.閉包的樣子到底是什麼樣的
4.閉包與方法堆疊(對原理不感興趣可以略過)
5.闭包有什么用
首頁 web前端 js教程 你必須了解的JavaScript閉包

你必須了解的JavaScript閉包

Jan 21, 2022 pm 05:58 PM
html javascript 前端

這篇文章為大家帶來了關於JavaScript閉包的學習筆記,其中包括了閉包與方法堆疊以及閉包的作用,希望對大家有幫助。

你必須了解的JavaScript閉包

從定義上來講,它是一個腳本語言,而且是一個相對容易學習的腳本語言。不需要太多的專業知識,你也能夠在某種程度上使用js(JavaScript的簡寫)程式碼。

當然如果你已經學習了一前端知識,你應該能理解這個工具的作用,這是一個非常方便的顯示頁面元素之間間距的工具。你看,你只是進行了一些簡單的瀏覽器操作,甚至你無法理解上述程式碼的內容,但你剛剛確確實實的嵌入了一段js程式碼在你所在的頁面中(顯然它是無害的,請放心使用)感謝up主CodingStartup起碼課的影片【有了它,把網頁做到跟設計圖一樣】以及up主ArcRain在影片下方的回應

這篇學習筆記的目的是記錄我自己對於js學習路程中的一些感悟和體會,以及一些我自己認為的小技巧,而不是為了教學,所以其中的部分內容的原理我並不會給出答案,有可能是我沒法準確的描述,有可能是我還沒搞懂,本人程度相當有限,如果文字中有錯誤的部分歡迎大家指摘。

1. 學習JavaScript的契機

正式學習JavaScript是在訓練班,沒錯我是從訓練班出來的,並不是科班出身,可以說是非常的草根了。我學習的時候ES6標準還並未普及,變數命名還在用非常傳統的var,學習的第一段程式碼是經典的console.log('Hello,world!'),當然它是在控制台上列印出來的。

當然,在培訓機構中的JavaScript內容講的是非常的淺顯,只有最為基礎的變數定義與命名,function聲明,回呼函數,ajax以及最為基礎的dom操作。顯然這些內容對於工作完全不夠用的。

對於js學習的'進修'機會來自我的工作,在工作中我第一次知道了node這個東西,也了解到即便是js也是可以做後台的(我是做的JAVA訓練),也開始逐漸接觸了一些ES6的標準。當然這些都是後話,最開始我接觸到最大的障礙是這貨。

2.'噁心'的閉包

啊,對我只有那麼一丁丁點基礎的我,完全無法理解我們公司自己封裝的jsonp代碼,它是長這個樣子的。

  var jsonp = (function(){
        var JSONP;
       return function(url){
           if (JSONP) {
             document.getElementsByTagName("head")[0].removeChild(JSONP);
          }
         JSONP = document.createElement("script");
          JSONP.type = "text/javascript";
          JSONP.src = url;
          document.getElementsByTagName("head")[0].appendChild(JSONP);
       }
     }())
登入後複製

當然,現在瀏覽器上已經無法透過控制台直接使用這個方法了,為了防止XSS攻擊瀏覽器已經禁止這樣注入程式碼了,但是在伺服器上還是可以用的,當然,這些都不是重點。

重點是這裡

    if (JSONP) {
       //dosome
 }
登入後複製

如果你跟我當初一樣,不知道什麼叫閉包或對閉包一知半解,那麼,對於這裡你應該也會產生疑問,思路大約是這樣的

第2行定義了JSONP但是沒有賦值,現在JSONP值為null,第三行返回了一個方法,第四行檢測JSONP值是否為空,如果不為空則做了一些事情,好了,後面可以不用看了,這個if白寫了,它百分之百進不去!

你看嘛,前面也沒有賦值,然後直接判斷,那它明明就是null。但是實際使用的時候你會發現,這個地方第一次呼叫確實不會進入這個分支,但只要你呼叫了第二次,,它就百分之百會進入這個分支。

// 这个是一个可以在控制台输出的闭包版本,你可以自己试一下
var closedhull = (function() {
    let name = null; // 这里直接赋值为null
    return function(msg){
        if(name) {
            console.log('name:', name)
            return name += msg;
        }
        return name = msg;
    }
}())
closedhull('我是第一句。') //我是第一句。
closedhull('我是第二句。') //我是第一句。我是第二句。
登入後複製

上面這個範例運行後,無論是從console.log()亦或是傳回值上都不難看出,確實進入了if(name)的分支,這個就是閉包的表現。這裡給一下閉包的定義

閉包就是能夠讀取其他函數內部變數的函數。

3.閉包的樣子到底是什麼樣的

好了,看過閉包是個啥了,先不說會不會用,至少,算是見過了,閉包有個顯著的特徵return function(){}

不是!

它的顯著特徵是在function內的function!

觀察以下方法

/*第一个案例*/
function test1(){
    // a应该在方法运行结束后销毁
    let a = 1;
    return {
        add: function(){
            return ++a;
        }
    }
}
let a = test1();
a.add()//2
a.add()//3
/*第二个案例*/
(function(){
    // b应该在方法运行结束后销毁
    let b = 1,
        timer = setInterval(()=>{
        console.log(++b)
    }, 2000)
    setTimeout(()=>{
        clearInterval(timer)
    }, 10000)
})()// 2 3 4 5 6
/*第三个案例*/
function showMaker(obj){
    // obj应该在方法运行结束后销毁
    return function(){
        console.log(JSON.stringify(obj))
    }
}
let shower = showMaker({a:1})
// 显然这里你还能看到他
shower(); // {"a":1}
/*第四个案例*/
let outObj = (function(){
    let c = 'hello',
        obj = {};
    Object.defineProperty(obj, 'out', {
        get(){
            return c;
        },
        set(v){
            c = v;
        }
    });
    return obj
})()
outObj.out // 可以读取并设置c的值
登入後複製

這四個都是閉包,他們都具備方法中的方法這一特性。

4.閉包與方法堆疊(對原理不感興趣可以略過)

閉包的定義,1. 可以在變數的作用域外存取該變數。 2. 透過某種手段延長一個局部變數的生命週期。 3. 讓一個局部變數的存活時間超過它的時間循環執行時間

3中由於涉及到了事件循環概念,之後涉及到時會去講的,這裡主要討論前兩種方式的定義。

一下內容如果你知道方法堆疊是個啥了就可以跳過了

局部作用域:在ES6之前,一般指一个方法内部(从参数列表开始,到方法体的括号结束为止),ES6中增加let关键字后,在使用let的情况下是指在一个{}中的范围内(显然,你不能在隐式的{}中使用let,编译器会禁止你做出这种行为的,因为没有{}就没有块级作用域),咱们这里为了简化讨论内容,暂且不把let的块级作用域算作闭包的范畴(其实应该算,不过意义不大,毕竟,你可以在外层块声明它。天啊,JS的命名还没拥挤到需要在一个方法内再去防止污染的程度。)

局部变量:区别于全局变量,全局变量会在某些时候被意外额创造和使用,这令人非常的...恼火和无助。局部变量就是在局部作用域下使用变量声明关键字声明出来的变量,应该很好理解。

局部变量的生命周期:好了,你在一个局部作用域中通过关键字(var const let等)声明了一个变量,然后给它赋值,这个局部变量在这个局部作用域中冒险就开始了,它会被使用,被重新赋值(除了傲娇的const小姐外),被调用(如果它是个方法),这个局部变量的本质是一个真实的值,区别在于如果它是个对象(对象,数组,方法都是对象)那么,它其实本质是一个地址的指针。如果它一个基础类型,那么它就是那个真实的值。它之所以存活是因为它有个住所。内存。

局部作用域与内存:每当出现一个局部作用域,一个方法栈就被申请了出来,在这个方法栈大概长这样子

|  data5 |
|  data4 |
|  data3 |
|  data2 |
|__data1_|
登入後複製

当然,它是能够套娃的,长这个样子

|  | d2 |  |
|  |_d1_|  |
|  data3   |
|  data2   |
|__data1___|
登入後複製

如果上面的东西是在太过于抽象,那么,我可以用实际案例展示一下

function stack1(){
    var data1,
        data2,
        data3,
        data4,
        data5
}
function stack2(){
    var data1,
        data2,
        data3;
    function stackInner(){
        var d1,
            d2;
    }
}
登入後複製

如果方法栈能够直观的感受的话,大约就是这个样子,咱们重点来分析stack2的这种情况,同时写一点实际内容进去

function stack2(){
    var data1 = '1',
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = '4',
            d2 = {y: '5'};
    }
    stackInner()
}
stack2()
登入後複製

显然其中data1,data3,d1持有的是基本类型(string),data2,d2持有的是引用类型(object),反应到图上

运行时的方法栈的样子

            |------>{y: '5'}
            |    |->{x: '2'}
    |  | d2-|   || |
    |  |_d1='4'_|| |
    |  data3='3' | |
    |  data2 ----| |
    |__data1='1'___|
登入後複製

画有点抽象...就这样吧。具体对象在哪呢?他们在一个叫堆的地方,不是这次的重点,还是先看方法栈内的这些变量,运行结束后,按照先进后出的原则,把栈内的局部变量一个一个的销毁,同时堆里的两个对象,由于引用被销毁,没了继续存在的意义,等待被垃圾回收。

接下来咱们要做两件事情:

  • d1不再等于4了,而是引用data1

  • return stackInner 而不是直接调用

这样闭包就完成了

function stack2(){
    var data1 = {msg: 'hello'},
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = data1,
            d2 = {y: '5'};
    }
    return stackInner
}
var out = stack2()
登入後複製

这里有一个要点,d2赋值给data1一定是在stackInner中完成的,原因?因为再stackInner方法中d2才被声明出来,如果你在stack2中d1 = data1那么恭喜你,你隐式的声明了一个叫d1的全局变量,而且在stackInner由于变量屏蔽的原因,你也看不到全局上的d2,原本计划的闭包完全泡汤。

变量屏蔽:不同作用域中相同名称的变量就会触发变量屏蔽。

看看栈现在的样子

运行时的方法栈的样子

               |------>{y: '5'}
out<---|       | |----|
    |  |  | d2-| | |  |  |
    |  |--|_d1---|_|  |  |
    |     data3=&#39;3&#39;   |  |
    |     data2(略)   |  |
    |_____data1<------|__|
登入後複製

好了,这个图可以和我们永别了,如果有可能,我后面会用画图工具替代,这么画图实在是太过邪典了。

这里涉及到了方法栈的一个特性,就是变量的穿透性,外部变量可以在内部的任意位置使用,因为再内部执行结束前,外部变量会一直存在。

由于stackInner被外部的out引用,导致这个对象不会随着方法栈的结束而销毁,接下来,最神奇的事情来了,由于stackInner这对象没有销毁,它内部d1依然保有data1所对应数据的引用,d1,d2一定会活下来,因为他们的爸爸stackInner活下来了,data1也以某种形式活了下来。

为什么说是某种形式,因为,本质上来说data1还是被销毁了。没错,只不过,data1所引用的那个对象的地址链接没有被销毁,这个才是本质。栈在调用结束后一定是会销毁的。但是调用本体(方法对象)只要存在,那么内部所引用的链接就不会断。

这个就是闭包的成因和本质。

5.闭包有什么用

OK,我猜测上一个章节估计很多人都直接跳过了,其实,跳过影响也不多,这个部分描述一下结论性的东西,闭包的作用。

它的最大作用就是给你的变量一个命名空间,防止命名冲突。要知道,你的框架,你export的东西,你import进来的东西,在编译的时候都会变成闭包,为的就是减少你变量对全局变量的污染,一个不依赖与import export的模块的代码大概长这个样子

(function(Constr, global){
    let xxx = new Constr(env1, env2, env3)
    global.NameSpace = xxx;
})(function(parm1, parm2, parm3) {
    //dosomeing
    reutrn {
        a: &#39;some1&#39;,
        b: &#39;some2&#39;,
        funcC(){
            //dosome
        },
        funcD(){
            //dosome
        }
    }
}, window)
登入後複製

当然这种封装代码的风格有多种多样的,但是大家都尽量把一套体系的内容都放到一个命名空间下,避免与其他框架产生冲突

相关推荐:javascript学习教程

以上是你必須了解的JavaScript閉包的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

HTML 中的表格邊框 HTML 中的表格邊框 Sep 04, 2024 pm 04:49 PM

HTML 表格邊框指南。在這裡,我們以 HTML 中的表格邊框為例,討論定義表格邊框的多種方法。

HTML 中的巢狀表 HTML 中的巢狀表 Sep 04, 2024 pm 04:49 PM

這是 HTML 中巢狀表的指南。這裡我們討論如何在表中建立表格以及對應的範例。

HTML 左邊距 HTML 左邊距 Sep 04, 2024 pm 04:48 PM

HTML 左邊距指南。在這裡,我們討論 HTML margin-left 的簡要概述及其範例及其程式碼實作。

HTML 表格佈局 HTML 表格佈局 Sep 04, 2024 pm 04:54 PM

HTML 表格佈局指南。在這裡,我們詳細討論 HTML 表格佈局的值以及範例和輸出。

HTML 輸入佔位符 HTML 輸入佔位符 Sep 04, 2024 pm 04:54 PM

HTML 輸入佔位符指南。在這裡,我們討論 HTML 輸入佔位符的範例以及程式碼和輸出。

HTML 有序列表 HTML 有序列表 Sep 04, 2024 pm 04:43 PM

HTML 有序列表指南。在這裡我們也分別討論了 HTML 有序列表和類型的介紹以及它們的範例

HTML onclick 按鈕 HTML onclick 按鈕 Sep 04, 2024 pm 04:49 PM

HTML onclick 按鈕指南。這裡我們分別討論它們的介紹、工作原理、範例以及各個事件中的onclick事件。

在 HTML 中移動文字 在 HTML 中移動文字 Sep 04, 2024 pm 04:45 PM

HTML 中的文字移動指南。在這裡我們討論一下marquee標籤如何使用語法和實作範例。

See all articles