作用域和閉包和this

前言

面試問(wèn)題:

  • 說(shuō)一下對(duì)變量提升的理解
  • 說(shuō)明this的幾種不同的使用場(chǎng)景
  • 創(chuàng)建10個(gè)<a>標(biāo)簽,點(diǎn)擊的時(shí)候彈出來(lái)對(duì)應(yīng)的序號(hào)
  • 如何理解作用域
  • 實(shí)際開(kāi)發(fā)中閉包的應(yīng)用

涉及到的知識(shí)點(diǎn):

  • 執(zhí)行上下文
  • this
  • 作用域
  • 作用域鏈
  • 閉包

執(zhí)行上下文

執(zhí)行上下文主要有兩種情況:

  • 全局代碼: 一段<script>標(biāo)簽里获高,有一個(gè)全局的執(zhí)行上下文兼都。所做的事情是:變量定義、函數(shù)聲明
  • 函數(shù)代碼:每個(gè)函數(shù)里有一個(gè)上下文。所做的事情是:變量定義、函數(shù)聲明、this码党、arguments

PS:注意“函數(shù)聲明”和“函數(shù)表達(dá)式”的區(qū)別。

全局執(zhí)行上下文

在執(zhí)行全局代碼前將window確定為全局執(zhí)行上下文斥黑。

(1)對(duì)全局?jǐn)?shù)據(jù)進(jìn)行預(yù)處理:(并沒(méi)有賦值)

  • var定義的全局變量==>undefined, 添加為window的屬性
  • function聲明的全局函數(shù)==>賦值(fun), 添加為window的方法
  • this==>賦值(window)

(2)開(kāi)始執(zhí)行全局代碼


20180311_1100.png

函數(shù)執(zhí)行上下文

在調(diào)用函數(shù), 準(zhǔn)備執(zhí)行函數(shù)體之前, 創(chuàng)建對(duì)應(yīng)的函數(shù)執(zhí)行上下文對(duì)象(虛擬的, 存在于棧中)揖盘。

(1)對(duì)局部數(shù)據(jù)進(jìn)行預(yù)處理:

  • 形參變量==>賦值(實(shí)參)==>添加為執(zhí)行上下文的屬性
  • arguments==>賦值(實(shí)參列表), 添加為執(zhí)行上下文的屬性
  • var定義的局部變量==>undefined, 添加為執(zhí)行上下文的屬性
  • function聲明的函數(shù) ==>賦值(fun), 添加為執(zhí)行上下文的方法
  • this==>賦值(調(diào)用函數(shù)的對(duì)象)

(2)開(kāi)始執(zhí)行函數(shù)體代碼

執(zhí)行上下文棧

  • 1.在全局代碼執(zhí)行前, JS引擎就會(huì)創(chuàng)建一個(gè)棧來(lái)存儲(chǔ)管理所有的執(zhí)行上下文對(duì)象
  • 2.在全局執(zhí)行上下文(window)確定后, 將其添加到棧中(壓棧)
  • 3.在函數(shù)執(zhí)行上下文創(chuàng)建后, 將其添加到棧中(壓棧)
  • 4.在當(dāng)前函數(shù)執(zhí)行完后,將棧頂?shù)膶?duì)象移除(出棧)
  • 5.當(dāng)所有的代碼執(zhí)行完后, 棧中只剩下window

this

this指的是,調(diào)用函數(shù)的那個(gè)對(duì)象锌奴。this永遠(yuǎn)指向函數(shù)運(yùn)行時(shí)所在的對(duì)象兽狭。

解析器在調(diào)用函數(shù)每次都會(huì)向函數(shù)內(nèi)部傳遞進(jìn)一個(gè)隱含的參數(shù),這個(gè)隱含的參數(shù)就是this鹿蜀。

根據(jù)函數(shù)的調(diào)用方式的不同箕慧,this會(huì)指向不同的對(duì)象:【重要】

  • 1.以函數(shù)的形式調(diào)用時(shí),this永遠(yuǎn)都是window茴恰。比如fun();相當(dāng)于window.fun();
  • 2.以方法的形式調(diào)用時(shí)颠焦,this是調(diào)用方法的那個(gè)對(duì)象
  • 3.以構(gòu)造函數(shù)的形式調(diào)用時(shí),this是新創(chuàng)建的那個(gè)對(duì)象
  • 4.使用call和apply調(diào)用時(shí)往枣,this是指定的那個(gè)對(duì)象

需要特別提醒的是:this的指向在函數(shù)定義時(shí)無(wú)法確認(rèn)伐庭,只有函數(shù)執(zhí)行時(shí)才能確定。

this的幾種場(chǎng)景:

  • 1分冈、作為構(gòu)造函數(shù)執(zhí)行

例如:

    function Foo(name) {
        //this = {};
        this.name = name;
        //return this;
    }

    var foo = new Foo();
  • 2圾另、作為對(duì)象的屬性執(zhí)行
    var obj = {
        name: 'A',
        printName: function () {
            console.log(this.name);
        }
    }

    obj.printName();

  • 3、作為普通函數(shù)執(zhí)行
    function fn() {
        console.log(this); //this === window
    }

    fn();
  • 4丈秩、call apply bind

作用域

作用域指一個(gè)變量的作用范圍盯捌。它是靜態(tài)的(相對(duì)于上下文對(duì)象), 在編寫(xiě)代碼時(shí)就確定了淳衙。

作用:隔離變量蘑秽,不同作用域下同名變量不會(huì)有沖突。

作用域的分類(lèi):

  • 全局作用域
  • 函數(shù)作用域
  • 沒(méi)有塊作用域(ES6有了)
if (true) {
    var name = 'smyhvae';
}
console.log(name);

上方代碼中箫攀,并不會(huì)報(bào)錯(cuò)肠牲,因?yàn)椋弘m然 name 是在塊里面定義的,但是 name 是全局變量靴跛。

全局作用域

直接編寫(xiě)在script標(biāo)簽中的JS代碼缀雳,都在全局作用域。

在全局作用域中:

  • 在全局作用域中有一個(gè)全局對(duì)象window梢睛,它代表的是一個(gè)瀏覽器的窗口肥印,它由瀏覽器創(chuàng)建我們可以直接使用识椰。

  • 創(chuàng)建的變量都會(huì)作為window對(duì)象的屬性保存。

  • 創(chuàng)建的函數(shù)都會(huì)作為window對(duì)象的方法保存深碱。

全局作用域中的變量都是全局變量腹鹉,在頁(yè)面的任意的部分都可以訪(fǎng)問(wèn)到。

變量的聲明提前:(變量提升)

使用var關(guān)鍵字聲明的變量( 比如 var a = 1)敷硅,會(huì)在所有的代碼執(zhí)行之前被聲明(但是不會(huì)賦值)功咒,但是如果聲明變量時(shí)不是用var關(guān)鍵字(比如直接寫(xiě)a = 1),則變量不會(huì)被聲明提前绞蹦。

舉例1:

    console.log(a);
    var a = 123;

打印結(jié)果:undefined

舉例2:

    console.log(a);
    a = 123;   //此時(shí)a相當(dāng)于window.a

程序會(huì)報(bào)錯(cuò):


函數(shù)的聲明提前:

  • 使用函數(shù)聲明的形式創(chuàng)建的函數(shù)function foo(){}力奋,會(huì)被聲明提前

也就是說(shuō)幽七,它會(huì)在所有的代碼執(zhí)行之前就被創(chuàng)建景殷,所以我們可以在函數(shù)聲明之前,調(diào)用函數(shù)澡屡。

  • 使用函數(shù)表達(dá)式創(chuàng)建的函數(shù)var foo = function(){}滨彻,不會(huì)被聲明提前,所以不能在聲明前調(diào)用挪蹭。

很好理解亭饵,因?yàn)榇藭r(shí)foo被聲明了,且為undefined梁厉,并沒(méi)有給其賦值function(){}辜羊。

所以說(shuō),下面的例子词顾,會(huì)報(bào)錯(cuò):


函數(shù)作用域

調(diào)用函數(shù)時(shí)創(chuàng)建函數(shù)作用域八秃,函數(shù)執(zhí)行完畢以后,函數(shù)作用域銷(xiāo)毀肉盹。

每調(diào)用一次函數(shù)就會(huì)創(chuàng)建一個(gè)新的函數(shù)作用域昔驱,他們之間是互相獨(dú)立的。

在函數(shù)作用域中可以訪(fǎng)問(wèn)到全局作用域的變量上忍,在全局作用域中無(wú)法訪(fǎng)問(wèn)到函數(shù)作用域的變量骤肛。

在函數(shù)中要訪(fǎng)問(wèn)全局變量可以使用window對(duì)象。(比如說(shuō)窍蓝,全局作用域和函數(shù)作用域都定義了變量a腋颠,如果想訪(fǎng)問(wèn)全局變量,可以使用window.a

提醒1:

在函數(shù)作用域也有聲明提前的特性:

  • 使用var關(guān)鍵字聲明的變量吓笙,是在函數(shù)作用域內(nèi)有效淑玫,而且會(huì)在函數(shù)中所有的代碼執(zhí)行之前被聲明
  • 函數(shù)聲明也會(huì)在函數(shù)中所有的代碼執(zhí)行之前執(zhí)行

因此,在函數(shù)中,沒(méi)有var聲明的變量都會(huì)成為全局變量絮蒿,而且并不會(huì)提前聲明尊搬。

舉例1:

        var a = 1;

        function foo() {
            console.log(a);
            a = 2;     // 此處的a相當(dāng)于window.a
        }

        foo();
        console.log(a);   //打印結(jié)果是2

上方代碼中,foo()的打印結(jié)果是1土涝。如果去掉第一行代碼毁嗦,打印結(jié)果是Uncaught ReferenceError: a is not defined

提醒2:定義形參就相當(dāng)于在函數(shù)作用域中聲明了變量。

        function fun6(e) {
            console.log(e);
        }

        fun6();  //打印結(jié)果為 undefined
        fun6(123);//打印結(jié)果為123

作用域與執(zhí)行上下文的區(qū)別

區(qū)別1:

  • 全局作用域之外回铛,每個(gè)函數(shù)都會(huì)創(chuàng)建自己的作用域狗准,作用域在函數(shù)定義時(shí)就已經(jīng)確定了。而不是在函數(shù)調(diào)用時(shí)
  • 全局執(zhí)行上下文環(huán)境是在全局作用域確定之后, js代碼馬上執(zhí)行之前創(chuàng)建
  • 函數(shù)執(zhí)行上下文是在調(diào)用函數(shù)時(shí), 函數(shù)體代碼執(zhí)行之前創(chuàng)建

區(qū)別2:

  • 作用域是靜態(tài)的, 只要函數(shù)定義好了就一直存在, 且不會(huì)再變化
  • 執(zhí)行上下文是動(dòng)態(tài)的, 調(diào)用函數(shù)時(shí)創(chuàng)建, 函數(shù)調(diào)用結(jié)束時(shí)就會(huì)自動(dòng)釋放

聯(lián)系:

  • 執(zhí)行上下文(對(duì)象)是從屬于所在的作用域
  • 全局上下文環(huán)境==>全局作用域
  • 函數(shù)上下文環(huán)境==>對(duì)應(yīng)的函數(shù)使用域

作用域鏈

當(dāng)在函數(shù)作用域操作一個(gè)變量時(shí)茵肃,它會(huì)先在自身作用域中尋找腔长,如果有就直接使用(就近原則)。如果沒(méi)有則向上一級(jí)作用域中尋找验残,直到找到全局作用域捞附;如果全局作用域中依然沒(méi)有找到,則會(huì)報(bào)錯(cuò)ReferenceError您没。

外部函數(shù)定義的變量可以被內(nèi)部函數(shù)所使用鸟召,反之則不行。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        //只要是函數(shù)就可以創(chuàng)造作用域
        //函數(shù)中又可以再創(chuàng)建函數(shù)
        //函數(shù)內(nèi)部的作用域可以訪(fǎng)問(wèn)函數(shù)外部的作用域
        //如果有多個(gè)函數(shù)嵌套氨鹏,那么就會(huì)構(gòu)成一個(gè)鏈?zhǔn)皆L(fǎng)問(wèn)結(jié)構(gòu)欧募,這就是作用域鏈

        //f1--->全局
        function f1(){
            //f2--->f1--->全局
            function f2(){
                //f3---->f2--->f1--->全局
                function f3(){
                }
                //f4--->f2--->f1---->全局
                function f4(){
                }
            }
            //f5--->f1---->全局
            function f5(){
            }
        }

    </script>
</head>
<body>

</body>
</html>

理解:

  • 多個(gè)上下級(jí)關(guān)系的作用域形成的鏈, 它的方向是從下向上的(從內(nèi)到外)
  • 查找變量時(shí)就是沿著作用域鏈來(lái)查找的

查找一個(gè)變量的查找規(guī)則:

    var a = 1

    function fn1() {
      var b = 2

      function fn2() {
        var c = 3
        console.log(c)
        console.log(b)
        console.log(a)
        console.log(d)
      }
      fn2()
    }
    fn1()
  • 在當(dāng)前作用域下的執(zhí)行上下文中查找對(duì)應(yīng)的屬性, 如果有直接返回, 否則進(jìn)入2
  • 在上一級(jí)作用域的執(zhí)行上下文中查找對(duì)應(yīng)的屬性, 如果有直接返回, 否則進(jìn)入3
  • 再次執(zhí)行2的相同操作, 直到全局作用域, 如果還找不到就拋出找不到的異常

閉包

閉包就是能夠讀取其他函數(shù)內(nèi)部數(shù)據(jù)(變量/函數(shù))的函數(shù)。

只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量仆抵,因此可以把閉包簡(jiǎn)單理解成"定義在一個(gè)函數(shù)內(nèi)部的函數(shù)"跟继。

上面這兩句話(huà),是阮一峰的文章里的镣丑,你不一定能理解舔糖,來(lái)看下面的講解和舉例。

如何產(chǎn)生閉包

當(dāng)一個(gè)嵌套的內(nèi)部(子)函數(shù)引用了嵌套的外部(父)函數(shù)的變量或函數(shù)時(shí), 就產(chǎn)生了閉包莺匠。

閉包到底是什么?

使用chrome調(diào)試查看

  • 理解一: 閉包是嵌套的內(nèi)部函數(shù)(絕大部分人)
  • 理解二: 包含被引用變量 or 函數(shù)的對(duì)象(極少數(shù)人)

注意: 閉包存在于嵌套的內(nèi)部函數(shù)中金吗。

產(chǎn)生閉包的條件

  • 1.函數(shù)嵌套
  • 2.內(nèi)部函數(shù)引用了外部函數(shù)的數(shù)據(jù)(變量/函數(shù))。

來(lái)看看條件2:

    function fn1() {
        function fn2() {

        }

        return fn2;
    }

    fn1();

上面的代碼不會(huì)產(chǎn)生閉包趣竣,因?yàn)閮?nèi)部函數(shù)fn2并沒(méi)有引用外部函數(shù)fn1的變量摇庙。

PS:還有一個(gè)條件是外部函數(shù)被調(diào)用,內(nèi)部函數(shù)被聲明期贫。比如:

    function fn1() {
        var a = 2
        var b = 'abc'

        function fn2() { //fn2內(nèi)部函數(shù)被提前聲明跟匆,就會(huì)產(chǎn)生閉包(不用調(diào)用內(nèi)部函數(shù))
            console.log(a)
        }

    }

    fn1();

    function fn3() {
        var a = 3
        var fun4 = function () {  //fun4采用的是“函數(shù)表達(dá)式”創(chuàng)建的函數(shù)异袄,此時(shí)內(nèi)部函數(shù)的聲明并沒(méi)有提前
            console.log(a)
        }
    }

    fn3();

常見(jiàn)的閉包

    1. 將一個(gè)函數(shù)作為另一個(gè)函數(shù)的返回值
    1. 將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用通砍。

閉包1:將一個(gè)函數(shù)作為另一個(gè)函數(shù)的返回值

    function fn1() {
      var a = 2

      function fn2() {
        a++
        console.log(a)
      }
      return fn2
    }

    var f = fn1();   //執(zhí)行外部函數(shù)fn1,返回的是內(nèi)部函數(shù)fn2
    f() // 3       //執(zhí)行fn2
    f() // 4       //再次執(zhí)行fn2

當(dāng)f()第二次執(zhí)行的時(shí)候,a加1了封孙,也就說(shuō)明了:閉包里的數(shù)據(jù)沒(méi)有消失迹冤,而是保存在了內(nèi)存中。如果沒(méi)有閉包虎忌,代碼執(zhí)行完倒數(shù)第三行后泡徙,變量a就消失了。

上面的代碼中膜蠢,雖然調(diào)用了內(nèi)部函數(shù)兩次堪藐,但是,閉包對(duì)象只創(chuàng)建了一個(gè)挑围。

也就是說(shuō)礁竞,要看閉包對(duì)象創(chuàng)建了一個(gè),就看:外部函數(shù)執(zhí)行了幾次(與內(nèi)部函數(shù)執(zhí)行幾次無(wú)關(guān))杉辙。

閉包2. 將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用

    function showDelay(msg, time) {
      setTimeout(function() {  //這個(gè)function是閉包模捂,因?yàn)槭乔短椎淖雍瘮?shù),而且引用了外部函數(shù)的變量msg
        alert(msg)
      }, time)
    }
    showDelay('atguigu', 2000)

上面的代碼中蜘矢,閉包是里面的funciton狂男,因?yàn)樗乔短椎淖雍瘮?shù),而且引用了外部函數(shù)的變量msg品腹。

閉包的作用

  • 作用1. 使用函數(shù)內(nèi)部的變量在函數(shù)執(zhí)行完后, 仍然存活在內(nèi)存中(延長(zhǎng)了局部變量的生命周期)
  • 作用2. 讓函數(shù)外部可以操作(讀寫(xiě))到函數(shù)內(nèi)部的數(shù)據(jù)(變量/函數(shù))

我們讓然拿這段代碼來(lái)分析:

    function fn1() {
      var a = 2

      function fn2() {
        a++
        console.log(a)
      }
      return fn2;
    }

    var f = fn1();   //執(zhí)行外部函數(shù)fn1岖食,返回的是內(nèi)部函數(shù)fn2
    f() // 3       //執(zhí)行fn2
    f() // 4       //再次執(zhí)行fn2

作用1分析

上方代碼中,外部函數(shù)fn1執(zhí)行完畢后舞吭,變量a并沒(méi)有立即消失县耽,而是保存在內(nèi)存當(dāng)中。

作用2分析:

函數(shù)fn1中的變量a镣典,是在fn1這個(gè)函數(shù)作用域內(nèi)兔毙,因此外部無(wú)法訪(fǎng)問(wèn)。但是通過(guò)閉包兄春,外部就可以操作到變量a澎剥。

達(dá)到的效果是:外界看不到變量a,但可以操作a赶舆。

比如上面達(dá)到的效果是:我看不到變量a哑姚,但是每次執(zhí)行函數(shù)后,讓a加1叙量。當(dāng)然,如果我真想看到a九串,我可以在fn2中將a返回即可绞佩。

回答幾個(gè)問(wèn)題:

  • 問(wèn)題1. 函數(shù)執(zhí)行完后, 函數(shù)內(nèi)部聲明的局部變量是否還存在?

答案:一般是不存在, 存在于閉包中的變量才可能存在寺鸥。

閉包能夠一直存在的根本原因是f,因?yàn)?code>f接收了fn1()品山,這個(gè)是閉包胆建,閉包里有a。注意肘交,此時(shí)笆载,fn2并不存在了,但是里面的對(duì)象(即閉包)依然存在涯呻,因?yàn)橛?code>f接收了凉驻。

  • 問(wèn)題2. 在函數(shù)外部能直接訪(fǎng)問(wèn)函數(shù)內(nèi)部的局部變量嗎?

不能,但我們可以通過(guò)閉包讓外部操作它复罐。

閉包的生命周期

  1. 產(chǎn)生: 嵌套內(nèi)部函數(shù)fn2被聲明時(shí)就產(chǎn)生了(不是在調(diào)用)
  2. 死亡: 嵌套的內(nèi)部函數(shù)成為垃圾對(duì)象時(shí)沿侈。(比如f = null,就可以讓f成為垃圾對(duì)象市栗。意思是缀拭,此時(shí)f不再引用閉包這個(gè)對(duì)象了)

閉包的應(yīng)用:定義具有特定功能的js模塊

  • 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的),只向外暴露一個(gè)包含n個(gè)方法的對(duì)象或函數(shù)填帽。
  • 模塊的使用者, 只需要通過(guò)模塊暴露的對(duì)象調(diào)用方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能蛛淋。

方式一

(1)myModule.js:(定義一個(gè)模塊,向外暴露多個(gè)函數(shù)篡腌,供外界調(diào)用)

function myModule() {
    //私有數(shù)據(jù)
    var msg = 'Smyhvae Haha'

    //操作私有數(shù)據(jù)的函數(shù)
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase()); //字符串大寫(xiě)
    }

    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase()) //字符串小寫(xiě)
    }

    //通過(guò)【對(duì)象字面量】的形式進(jìn)行包裹褐荷,向外暴露多個(gè)函數(shù)
    return {
        doSomething1: doSomething,
        doOtherthing2: doOtherthing
    }
}

上方代碼中,外界可以通過(guò)doSomething1和doOtherthing2來(lái)操作里面的數(shù)據(jù)嘹悼,但不讓外界看到叛甫。

(2)index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>05_閉包的應(yīng)用_自定義JS模塊</title>
</head>
<body>
<!--
閉包的應(yīng)用 : 定義JS模塊
  * 具有特定功能的js文件
  * 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的)
  * 【重要】只向外暴露一個(gè)包含n個(gè)方法的對(duì)象或函數(shù)
  * 模塊的使用者, 只需要通過(guò)模塊暴露的對(duì)象調(diào)用方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能
-->
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
    var module = myModule();
    module.doSomething1();
    module.doOtherthing2();
</script>
</body>
</html>

方式二

同樣是實(shí)現(xiàn)方式一種的功能,這里我們采取另外一種方式杨伙。

(1)myModule2.js:(是一個(gè)立即執(zhí)行的匿名函數(shù))

(function () {
    //私有數(shù)據(jù)
    var msg = 'Smyhvae Haha'

    //操作私有數(shù)據(jù)的函數(shù)
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase())
    }

    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase())
    }

    //外部函數(shù)是即使運(yùn)行的匿名函數(shù)其监,我們可以把兩個(gè)方法直接傳給window對(duì)象
    window.myModule = {
        doSomething1: doSomething,
        doOtherthing2: doOtherthing
    }
})()

(2)index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>05_閉包的應(yīng)用_自定義JS模塊2</title>
</head>
<body>
<!--
閉包的應(yīng)用2 : 定義JS模塊
  * 具有特定功能的js文件
  * 將所有的數(shù)據(jù)和功能都封裝在一個(gè)函數(shù)內(nèi)部(私有的)
  * 只向外暴露一個(gè)包信n個(gè)方法的對(duì)象或函數(shù)
  * 模塊的使用者, 只需要通過(guò)模塊暴露的對(duì)象調(diào)用方法來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能
-->

<!--引入myModule文件-->
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
    myModule.doSomething1()
    myModule.doOtherthing2()
</script>
</body>
</html>

上方兩個(gè)文件中,我們?cè)?code>myModule2.js里直接把兩個(gè)方法直接傳遞給window對(duì)象了限匣。于是抖苦,在index.html中引入這個(gè)js文件后,會(huì)立即執(zhí)行里面的匿名函數(shù)米死。在index.html中把myModule直接拿來(lái)用即可锌历。

總結(jié):

當(dāng)然,方式一和方式二對(duì)比后峦筒,我們更建議采用方式二究西,因?yàn)楹芊奖恪?/p>

但無(wú)論如何,兩種方式都采用了閉包物喷。

閉包的缺點(diǎn)及解決

缺點(diǎn):函數(shù)執(zhí)行完后, 函數(shù)內(nèi)的局部變量沒(méi)有釋放卤材,占用內(nèi)存時(shí)間會(huì)變長(zhǎng)遮斥,容易造成內(nèi)存泄露。

解決:能不用閉包就不用商膊,及時(shí)釋放伏伐。比如:

    f = null;  // 讓內(nèi)部函數(shù)成為垃圾對(duì)象 -->回收閉包

總而言之宠进,你需要它晕拆,就是優(yōu)點(diǎn);你不需要它材蹬,就成了缺點(diǎn)实幕。

內(nèi)存溢出和內(nèi)存泄露

內(nèi)存溢出

內(nèi)存溢出:一種程序運(yùn)行出現(xiàn)的錯(cuò)誤。當(dāng)程序運(yùn)行需要的內(nèi)存超過(guò)了剩余的內(nèi)存時(shí), 就出拋出內(nèi)存溢出的錯(cuò)誤堤器。

代碼舉例:

    var obj = {};
    for (var i = 0; i < 10000; i++) {
    obj[i] = new Array(10000000);  //把所有的數(shù)組內(nèi)容都放到obj里保存昆庇,導(dǎo)致obj占用了很大的內(nèi)存空間
    console.log("-----");
    }

內(nèi)存泄漏

內(nèi)存泄漏占用的內(nèi)存沒(méi)有及時(shí)釋放。

注意闸溃,內(nèi)存泄露的次數(shù)積累多了整吆,就容易導(dǎo)致內(nèi)存溢出。

常見(jiàn)的內(nèi)存泄露

  • 1.意外的全局變量
  • 2.沒(méi)有及時(shí)清理的計(jì)時(shí)器或回調(diào)函數(shù)
  • 3.閉包

情況1舉例:

    // 意外的全局變量
    function fn() {
        a = new Array(10000000);
        console.log(a);
    }

    fn();

情況2舉例:

    // 沒(méi)有及時(shí)清理的計(jì)時(shí)器或回調(diào)函數(shù)
    var intervalId = setInterval(function () { //啟動(dòng)循環(huán)定時(shí)器后不清理
        console.log('----')
    }, 1000)

    // clearInterval(intervalId);  //清理定時(shí)器

情況3舉例:

<script type="text/javascript">
  function fn1() {
    var a = 4;
    function fn2() {
      console.log(++a)
    }
    return fn2
  }
  var f = fn1()
  f()

  // f = null //讓內(nèi)部函數(shù)成為垃圾對(duì)象-->回收閉包
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辉川,一起剝皮案震驚了整個(gè)濱河市表蝙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乓旗,老刑警劉巖府蛇,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異屿愚,居然都是意外死亡汇跨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)妆距,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)穷遂,“玉大人,你說(shuō)我怎么就攤上這事娱据∪洌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵吸耿,是天一觀(guān)的道長(zhǎng)祠锣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)咽安,這世上最難降的妖魔是什么伴网? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮妆棒,結(jié)果婚禮上澡腾,老公的妹妹穿的比我還像新娘吻谋。我一直安慰自己,他們只是感情好崖堤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布捷犹。 她就那樣靜靜地躺著,像睡著了一般澜公。 火紅的嫁衣襯著肌膚如雪姆另。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天坟乾,我揣著相機(jī)與錄音迹辐,去河邊找鬼。 笑死甚侣,一個(gè)胖子當(dāng)著我的面吹牛明吩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播殷费,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼印荔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了详羡?” 一聲冷哼從身側(cè)響起仍律,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎殷绍,沒(méi)想到半個(gè)月后染苛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡主到,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年茶行,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片登钥。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畔师,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出牧牢,到底是詐尸還是另有隱情看锉,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布塔鳍,位于F島的核電站伯铣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏轮纫。R本人自食惡果不足惜腔寡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掌唾。 院中可真熱鬧放前,春花似錦忿磅、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至似扔,卻和暖如春吨些,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虫几。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工锤灿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挽拔,地道東北人辆脸。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像螃诅,于是被迫代替她去往敵國(guó)和親啡氢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容