js的作用域和閉包

當(dāng)我寫(xiě)一段js代碼并運(yùn)行時(shí),有三個(gè)非常重要的角色:引擎、 編譯器、 作用域

下面模擬一段js代碼運(yùn)行的過(guò)程
如下代碼

var a = 2
  1. 遇到var a 編譯器會(huì)詢(xún)問(wèn)作用域是否已經(jīng)有一個(gè)變量a存在于統(tǒng)一作用域下,如果有則忽略該聲明,繼續(xù)進(jìn)行編譯,,否則會(huì)要求作用域在當(dāng)前作用域聲明新的變量a
  2. 編譯器會(huì)為引擎生產(chǎn)運(yùn)行時(shí)所需要的代碼,用來(lái)處理a=2的賦值操作,引擎運(yùn)行時(shí),會(huì)先詢(xún)問(wèn)作用域是否有變量a,如果是,會(huì)引用這個(gè)變量,否則,引擎會(huì)繼續(xù)查找該變量。
  3. 如果引擎找到了a,就把2賦值給它。否則引擎就會(huì)丟出一個(gè)異常

詞法作用域

  1. let:可以將變量綁定在所在的作用域內(nèi)(通常是{....}內(nèi)部),也就是說(shuō)let為它聲明的變量隱式的劫持了所在的塊作用域
    注意:使用顯式的代碼能讓代碼更加的清晰,如下
var a = 6
if (a) {
    {    // <--  顯式的塊
        let boy = "Jack"
        console.log(boy)
    }
}

聲明提前的小細(xì)節(jié)

查看以下代碼

foo()    //TypeError 此時(shí)只聲明了foo,并未定義foo是函數(shù)
bar()    //ReferenceError      找不到bar

var foo = function bar() {
    console.log("hello")
}

以上代碼會(huì)被理解為以下形式

var foo

foo()
bar()

foo = function () {
    var bar = ..self..
}

注意:包括函數(shù)表達(dá)式的賦值操作在內(nèi)的賦值操作不會(huì)被提升

閉包

考慮如下代碼

function foo() {
    var a = 2

    function bar() {
        console.log(a)
    }

    return bar
}

var baz = foo()

baz()      //2  這就是典型的閉包了

我們打開(kāi)控制臺(tái)研究下這段代碼


看看綠色箭頭處,這就是閉包

注意:無(wú)論何種方式將內(nèi)部函數(shù)傳遞到所在的詞法作用域外,它都會(huì)持有對(duì)原始作用域的引用,無(wú)論在何處執(zhí)行這個(gè)函數(shù),都會(huì)使用閉包

閉包的本質(zhì)

將(訪(fǎng)問(wèn)它們各自詞法作用域的)函數(shù)當(dāng)作第一級(jí)的值類(lèi)型并且到處傳遞,這就是閉包

考慮以下例子

function CoolModule() {
    var something = "cool"
    var another = [1, 2, 3]

    function dosomeThing() {
        console.log(something)
    }

    function doAnother() {
        console.log(another.join("!"))
    }

    return {
        doSomeThing: dosomeThing,
        doAnother: doAnother
    }
}

var foo = CoolModule()

foo.doSomeThing()    //cool
foo.doAnother()    //1!2!3

這是典型的閉包應(yīng)用,在js中被稱(chēng)為模塊,最常見(jiàn)的實(shí)現(xiàn)模塊的方式通常被稱(chēng)為模塊暴露

模塊模式需要具備兩個(gè)必要條件

  1. 必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊實(shí)例)
  2. 封閉函數(shù)必須返回至少一個(gè)內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪(fǎng)問(wèn)或者修改私有的狀態(tài)

模塊模式的功能

命名將要作為公共API返回的對(duì)象

var foo = (function CoolModule(id) {
  function change() {
    publickAPI.identify = identify2
  }

  function identify1() {
    console.log(id)
  }

  function identify2() {
    console.log(id.toUpperCase())
  }

  var publickAPI = {
    change: change,
    identify: identify1
  }

  return publickAPI
})("foo module")

foo.identify()    //foo module
foo.change()
foo.identify()    //FOO MODULE

通過(guò)模塊化實(shí)例的內(nèi)部保留對(duì)公共API對(duì)象的內(nèi)部引用,可以從內(nèi)部對(duì)模塊實(shí)例進(jìn)行修改,包括添加或刪除方法和屬性,以及修改它們的值

現(xiàn)代化的模塊機(jī)制

大多數(shù)模塊依賴(lài)加載器/管器本質(zhì)上都是將這種模塊定義封裝進(jìn)一個(gè)友好的API

考慮如下代碼:

var MyModules = (function Manager() {
  var modules = {}

  function define(name, deps, impl) {
    for (var i = 0; i < deps.length; i++) {
      deps[i] = modules[deps[i]]
    }
    modules[name] = impl.apply(impl, deps)
  }

  function get() {
    return modules[name]
  }

  return {
    define: define,
    get: get
  }
})()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容