欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你深入了解JavaScript中的閉包

 更新時間:2023年07月20日 15:11:21   作者:風(fēng)如也  
閉包(closure)是一個函數(shù)以及其捆綁的周邊環(huán)境狀態(tài)的引用的組合,就是讓開發(fā)者可以從內(nèi)部函數(shù)訪問外部函數(shù)的作用域,下面下面小編就來和大家深入聊聊它的使用吧

閉包(closure)是一個函數(shù)以及其捆綁的周邊環(huán)境狀態(tài)(lexical environment,詞法環(huán)境)的引用的組合。換而言之,閉包讓開發(fā)者可以從內(nèi)部函數(shù)訪問外部函數(shù)的作用域。在 JavaScript 中,閉包會隨著函數(shù)的創(chuàng)建而被同時創(chuàng)建。

簡言之:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。

詞法作用域

示例1:

function init() {
  var name = "localName"; // name 是一個被 init 創(chuàng)建的局部變量
  function closureName() { // closureName() 是內(nèi)部函數(shù),一個閉包
    alert(name); // 使用了父函數(shù)中聲明的變量
  }
  closureName();
}
init();

上面詞法作用域的例子描述了分析器如何在函數(shù)嵌套的情況下解析變量名。詞法指的是,詞法作用域根據(jù)源代碼中聲明變量的位置來確定該變量在何處可用。嵌套函數(shù)可訪問聲明于它們外部作用域的變量。

示例2:

function fn() {
  var name = "localName";
  function closureName() {
    alert(name);
  }
  return closureName;
}
var myFn = fn()
myFn();

示例2的運行效果和示例1一樣。不同之處在于內(nèi)部函數(shù)closureName()在執(zhí)行前,從外部函數(shù)返回。

JavaScript中的函數(shù)會形成閉包。閉包是由函數(shù)以及聲明該函數(shù)的詞法環(huán)境組合而成的。該環(huán)境包含了這個閉包創(chuàng)建時作用域內(nèi)的任何局部變量。示例2中,myFn是執(zhí)行fn時創(chuàng)建的closureName函數(shù)實例的引用。closureName的實例維持了一個對它的詞法環(huán)境(變量name存在其中)的引用。

所以,當(dāng)myFn被調(diào)用時,變量name仍然可以使用。

示例3:

function fn(x) {
  return function(y) {
    return x + y;
  }
}
var addOne = fn(1)
var addTwo = fn(2)
console.log(addOne(3)) // 4
console.log(addTwo(3)) // 5

示例3中,我們可以把fn看作一個函數(shù)工廠,它創(chuàng)建了將指定的值和他的參數(shù)相加求和的函數(shù)。我們使用這個函數(shù)工廠創(chuàng)建了兩個新函數(shù)。一個將其參數(shù)和1求和,一個將其參數(shù)和2求和。

addOneaddTwo都是閉包,他們共享相同的函數(shù)定義,但是保存了不同的詞法環(huán)境。在addOne的詞法環(huán)境中,x是1。addTwo中,x是2。

使用閉包場景

閉包允許將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)聯(lián)起來。類似于面向?qū)ο缶幊?。比如對象允許我們將某些數(shù)據(jù)(如對象的屬性)與一個或多個方法相關(guān)聯(lián)。

  • 當(dāng)使用只有一個方法的對象的地方,都可以使用閉包。
  • 在web中,我們大部分寫的javascript代碼都是基于事件的----定義某種行為,然后將其綁定到用戶觸發(fā)的事件之上。(比如按鈕的點擊事件)。這個代碼通常稱為回調(diào):為響應(yīng)事件而執(zhí)行的函數(shù)。

比如我們想在頁面上添加調(diào)整字體大小的按鈕,size1size2就是閉包。

示例4:

<button id="btn1">size12</button>
<button id="btn2">size24</button>
<p>hello</p>
<script>
    function setFontSize(num) {
        return function() {
            return num + 'px';
        }
    }
    var size1 = setFontSize(12)
    var size2 = setFontSize(24)
    document.getElementById("btn1").onclick = () => {
        let pdom = document.getElementsByTagName("p")
        pdom[0].style.fontSize = size1()
    }
    document.getElementById("btn2").onclick = () => {
        let pdom = document.getElementsByTagName("p")
        pdom[0].style.fontSize = size2()
    }
</script>

用閉包模擬私有方法

使用閉包來模擬私有方法,私有方法不僅僅有利于限制對代碼的訪問,還提供了管理全局命名空間的強大能力,避免非核心的方法弄亂了代碼的公共接口部分。

示例5:使用閉包定義公共函數(shù),并令其可以訪問私有函數(shù)和變量

var Counter = (function () {
    var privateCounter = 0
    function operateCounter(val) {
        privateCounter += val
    }
    return {
        increment: function () {
            operateCounter(1);
        },
        decrement: function () {
            operateCounter(-1);
        },
        value: function () {
            return privateCounter;
        }
    }
})()
console.log(Counter.value())
Counter.increment()
Counter.increment()
console.log(Counter.value())
Counter.decrement()
console.log(Counter.value())

之前的示例中,每個閉包都有自己的詞法環(huán)境。示例5只創(chuàng)建了一個詞法環(huán)境,為3個函數(shù)共享:Counter.incrementCounter.decrement 和 Counter.value。

示例5的共享環(huán)境創(chuàng)建于一個立即執(zhí)行的匿名函數(shù)體內(nèi)。這個環(huán)境中包含兩個私有項:名為 privateCounter 的變量和名為 operateCounter 的函數(shù)。這兩項都無法在這個匿名函數(shù)外部直接訪問。必須通過匿名函數(shù)返回的三個公共函數(shù)訪問。

這三個公共函數(shù)共享同一個環(huán)境的閉包。多虧 JavaScript 的詞法作用域,它們都可以訪問 privateCounter 變量和 operateCounter 函數(shù)。

注意:示例5中,我們定義了一個立即執(zhí)行的匿名函數(shù),并賦值給Counter,那么我們也可以把這個匿名函數(shù)不立即執(zhí)行,賦值給變量makeCounter,這樣就能創(chuàng)建多個計數(shù)器。

var makeCounter = (function () {
    var privateCounter = 0
    function operateCounter(val) {
        privateCounter += val
    }
    return {
        increment: function () {
            operateCounter(1);
        },
        decrement: function () {
            operateCounter(-1);
        },
        value: function () {
            return privateCounter;
        }
    }
})
var Counter1 = makeCounter();
var Counter2 = makeCounter();

Counter1Counter2閉包都是引用自己詞法作用域內(nèi)的變量。

在一個閉包內(nèi)對變量的修改,不會影響到另外一個閉包中的變量。

閉包的用途

總結(jié)閉包的用途:

可以讀取函數(shù)內(nèi)部的變量;

讓變量的值始終保存在內(nèi)存中。

在循環(huán)中創(chuàng)建閉包:一個場景的錯誤

在引入let,關(guān)鍵字之前,在循環(huán)中創(chuàng)建閉包有一個常見的問題。

for(var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
} // 5 5 5 5 5

我們想輸出0 1 2 3 4,確發(fā)現(xiàn)打印了5個5出來,為什么?

for循環(huán)在宏任務(wù)階段就執(zhí)行了,setTimeout在微任務(wù)階段執(zhí)行,此時變量i因為是全局變量,i的值已經(jīng)變?yōu)?了,所以最后打印出來是5個5。

如何解決呢?

1.使用閉包

setTimeout的內(nèi)容放置閉包中,循環(huán)執(zhí)行時每個閉包中都有對應(yīng)的作用域i

for(var i = 0; i < 5; i++) {
  (function(i){
    setTimeout(() => {
      console.log(i)
    }, 0)
  })(i)
} // 0 1 2 3 4

2.使用let聲明變量i

for(let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
} // 0 1 2 3 4

3.使用forEach來遍歷數(shù)組

[0, 1, 2, 3, 4].forEach((i) => {
  setTimeout(() => {
    console.log(i)
  }, 0)
}) // 0 1 2 3 4

性能考量

如果不是某些特定任務(wù)需要使用閉包,在其他函數(shù)中創(chuàng)建函數(shù)是不明智的,因為閉包在處理速度和內(nèi)存消耗方面對腳本性能具有負(fù)面影響。

例如:在創(chuàng)建新的對象或者類時,方法通常應(yīng)該關(guān)聯(lián)到對象的原型上,而不是定義到對象的構(gòu)造器中。因為每次構(gòu)造器被調(diào)用時,方法都會被重新賦值一次。(即每創(chuàng)建一個對象,方法都會被重新賦值)

比如:

function CreateObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function () {
    return this.name;
  };
  this.getMessage = function () {
    return this.message;
  };
}

上面代碼中,并沒有利用到閉包的好處。因此可以避免使用閉包。修改后如下:

function CreateObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
CreateObject.prototype = {
  getName() {
    return this.name;
  },
  getMessage() {
    return this.message;
  },
};

但是,我們不建議重新定義對應(yīng)的原型,而是在原型的基礎(chǔ)上去添加方法,修改后如下:

function CreateObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
CreateObject.prototype.getName = function () {
  return this.name;
};
CreateObject.prototype.getMessage = function () {
  return this.message;
};

上面示例中,繼承的原型可以被所有對象共享,而不比在每一次創(chuàng)建對象時去定義方法。

使用閉包注意點

由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。

閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。

到此這篇關(guān)于一文帶你深入了解JavaScript中的閉包的文章就介紹到這了,更多相關(guān)JavaScript閉包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談JavaScript異步編程

    淺談JavaScript異步編程

    本文主要介紹了javascript的異步編程相關(guān)知識。具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01
  • axios?POST提交數(shù)據(jù)的三種請求方式寫法示例

    axios?POST提交數(shù)據(jù)的三種請求方式寫法示例

    這篇文章主要介紹了axios?POST提交數(shù)據(jù)的三種請求方式寫法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • 一個效果寫的HashTable代碼

    一個效果寫的HashTable代碼

    一個效果寫的HashTable代碼...
    2007-03-03
  • 詳解vscode中console.log的兩種快速寫法

    詳解vscode中console.log的兩種快速寫法

    這篇文章主要介紹了vscode中console.log的兩種快速寫法,每種方法通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • Bootstrap3 內(nèi)聯(lián)單選和多選框

    Bootstrap3 內(nèi)聯(lián)單選和多選框

    這篇文章主要介紹了Bootstrap3 內(nèi)聯(lián)單選和多選框的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-12-12
  • 通俗解釋JavaScript正則表達式快速記憶

    通俗解釋JavaScript正則表達式快速記憶

    正則表達式使用單個字符串來描述、匹配一系列符合某個句法規(guī)則的字符串。簡單來說,就是按照某種規(guī)則去匹配符合條件的字符串
    2017-08-08
  • 關(guān)于ckeditor在bootstrap中modal中彈框無法輸入的解決方法

    關(guān)于ckeditor在bootstrap中modal中彈框無法輸入的解決方法

    今天小編就為大家分享一篇關(guān)于ckeditor在bootstrap中modal中彈框無法輸入的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • 微信小程序綁定手機號獲取驗證碼功能

    微信小程序綁定手機號獲取驗證碼功能

    這篇文章主要介紹了微信小程序綁定手機號獲取驗證碼功能,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10
  • TypeScript里string和String的區(qū)別

    TypeScript里string和String的區(qū)別

    這篇文章主要介紹了TypeScript里string和String的區(qū)別,真的不止是大小寫的區(qū)別,string表示原生類型,而String表示對象,下文更多詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-03-03
  • JS自定義滾動條效果

    JS自定義滾動條效果

    這篇文章主要為大家詳細(xì)介紹了JS自定義滾動條效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03

最新評論