国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

作用域閉包,你真的懂了嗎?

yangrd / 2931人閱讀

摘要:曾幾何時(shí),閉包好像就是一個(gè)十分難以捉摸透的東西,看了很多文章,對(duì)閉包都各有說(shuō)法,以致讓我十分暈,什么內(nèi)部變量外部變量的,而且大多數(shù)都只描述一個(gè)過(guò)程,沒(méi)有給閉包的定義,最后,舉幾個(gè)例子,告訴你這就是閉包。

曾幾何時(shí),閉包好像就是一個(gè)十分難以捉摸透的東西,看了很多文章,對(duì)閉包都各有說(shuō)法,以致讓我十分暈,什么內(nèi)部變量、外部變量的,而且大多數(shù)都只描述一個(gè)過(guò)程,沒(méi)有給閉包的定義,最后,舉幾個(gè)例子,告訴你這就是閉包。于是乎,我從來(lái)都是帶有疑問(wèn)使用閉包的:閉包是指作用域,還是指函數(shù),還是指訪(fǎng)問(wèn)外部變量的過(guò)程?還有外部變量有多外面?直到最近的學(xué)習(xí),我才漸漸清晰......

前提

首先,我們需要知道 JavaScript 里面的函數(shù)會(huì)創(chuàng)建內(nèi)部詞法作用域,是的,JavaScript 是詞法作用域,也就是說(shuō)作用域與作用域的層級(jí)關(guān)系在你書(shū)寫(xiě)的時(shí)候就已經(jīng)確定了,而不是調(diào)用的時(shí)候,調(diào)用的時(shí)候確定的稱(chēng)為動(dòng)態(tài)作用域,由于不是本篇文章的重點(diǎn),就不再詳細(xì)解釋了,舉兩個(gè)例子自己領(lǐng)悟:

var name = "fruit"
function apple () {
  console.log(name)
}
function orange () {
  var name = "orange"
  apple()
}

orange()  // fruit

由于 JavaScript 是詞法作用域,所以 apple 函數(shù)的局部作用域的上層作用域是全局作用域,從書(shū)寫(xiě)的位置就看出來(lái)了。假設(shè) JavaScript 是動(dòng)態(tài)作用域,就要看函數(shù)的調(diào)用順序了,由于 apple 是在 orange 中調(diào)用的,所以 apple 的上層作用域是 orange 的局部作用域,那樣的話(huà)會(huì)輸出 orange!

這樣的話(huà),就制定了一套作用域訪(fǎng)問(wèn)的規(guī)則,這也是會(huì)有閉包的原因之一!

什么是閉包?

函數(shù)記住并訪(fǎng)問(wèn)其所在的詞法作用域,叫做閉包現(xiàn)象,而此時(shí)函數(shù)對(duì)作用域的引用叫做閉包。

當(dāng)我看到這句話(huà)的時(shí)候,淚流滿(mǎn)面,國(guó)外的作者就是一語(yǔ)道破真相。簡(jiǎn)單的說(shuō),閉包就是引用,對(duì)誰(shuí)的引用呢,對(duì)作用域的引用,只不過(guò)這種引用是有條件的——首先要記住作用域,然后再訪(fǎng)問(wèn)作用域!

什么叫記住作用域?

首先,我們都知道,在 JavaScript 里面,如果函數(shù)被調(diào)用過(guò)了,并且以后不會(huì)被用到,那么垃圾回收機(jī)制就會(huì)銷(xiāo)毀由函數(shù)創(chuàng)建的作用域,我們還知道,對(duì)象(函數(shù)也是對(duì)象)的傳遞屬于傳引用,也就是類(lèi)似于C語(yǔ)言里面的指針,并不會(huì)把真正的值拷貝給變量,而是把對(duì)象所在的位置傳遞給變量,所以,當(dāng)函數(shù)被傳引用到一個(gè)還未銷(xiāo)毀的作用域的某個(gè)變量,由于變量存在,所以函數(shù)得存在,又因?yàn)楹瘮?shù)的存在依賴(lài)于函數(shù)所在的詞法作用域,所以函數(shù)所在的詞法作用域也得存在,這樣一來(lái),就記住了該詞法作用域。也就解釋了該節(jié)的標(biāo)題!下面舉個(gè)例子說(shuō)明一下:

// 沒(méi)有閉包現(xiàn)象的時(shí)候
function apple () {
  var count = 0

  function output () {
    console.log(count)
  }

  fruit(output)
}

function fruit (arg) {
  console.log("fruit")
}

apple() // fruit

當(dāng)我們?cè)谡{(diào)用 apple 的時(shí)候,本來(lái) apple 在執(zhí)行完畢之后 apple 的局部作用域就應(yīng)該被銷(xiāo)毀,但是由于 fruit(output)output 傳引用給了 arg,所以在 fruit 執(zhí)行的這段時(shí)間內(nèi),arg 肯定是存在的,被引用的函數(shù) output 也得存在,而 output 依賴(lài)的 apple 函數(shù)產(chǎn)生的局部作用域也得存在,這就是所謂的“記住”,把作用域給記住了!

但是,上面的例子是閉包現(xiàn)象嗎?不是,因?yàn)楹瘮?shù) output 內(nèi)部并沒(méi)有訪(fǎng)問(wèn)記住的詞法作用域的變量!在執(zhí)行 fruit(output) 的過(guò)程中,只發(fā)生了 arg = output 的傳引用賦值,而這個(gè)過(guò)程,只是把二者關(guān)聯(lián)起來(lái)了,并沒(méi)有去取 arg 引用的對(duì)象的值,所以實(shí)際上也并沒(méi)有訪(fǎng)問(wèn) output 所在的詞法作用域!

記住并訪(fǎng)問(wèn)

上面的代碼,稍微修改一下就會(huì)產(chǎn)生閉包現(xiàn)象了:

function apple () {
  var count = 0

  function output () {
    console.log(count)
  }

  fruit(output)
}

function fruit (arg) {
  arg()
}

apple() // 0

現(xiàn)在,調(diào)用 fruit 時(shí),apple 的局部作用域處于“記住”的狀態(tài),這時(shí)候, fruit 內(nèi)部調(diào)用了 arg(),因?yàn)閭饕茫瑢?shí)際上訪(fǎng)問(wèn)并執(zhí)行了 apple 局部作用域的 output,不僅僅是這樣,output 內(nèi)部還訪(fǎng)問(wèn)了 count 變量,這兩次對(duì) apple 局部作用域的引用都是閉包!

所以,之所以說(shuō)所有回調(diào)函數(shù)的調(diào)用都會(huì)產(chǎn)生閉包現(xiàn)象,也是因?yàn)檫@個(gè)回調(diào)函數(shù)被傳給了另外一個(gè)函數(shù)的參數(shù),所以在另外一個(gè)函數(shù)的作用域消失之前,回調(diào)函數(shù)所在的詞法作用域都被記住了,由于回調(diào)函數(shù)一定會(huì)被執(zhí)行,所以回調(diào)函數(shù)所在的詞法作用域至少被訪(fǎng)問(wèn)了一次,也就是至少訪(fǎng)問(wèn)回調(diào)函數(shù)本身,而這個(gè)對(duì)作用域的引用就是閉包。

閉包的作用

根據(jù)上面的講解,估計(jì)你自己都能倒背如流了:

記住了函數(shù)所在的詞法作用域,使其不被銷(xiāo)毀;

能夠訪(fǎng)問(wèn)函數(shù)所在詞法作用域的變量;

創(chuàng)建模塊(設(shè)計(jì)私有變量、公有函數(shù)等等)

還有很多,就不一一說(shuō)了,下面就是利用閉包來(lái)解決一個(gè)常見(jiàn)的問(wèn)題:

for (var i = 0; i < 5; i++) {
  // 為了方便說(shuō)明,給函數(shù)起名叫 apple
  setTimeout(function apple () {
    console.log(i) // 5 個(gè) 5
  }, 0)
}

首先讀者們先思考一個(gè)問(wèn)題,這會(huì)產(chǎn)生閉包嗎?

其實(shí),上面也也會(huì)產(chǎn)生閉包,只不過(guò) apple 記住并訪(fǎng)問(wèn)的是全局作用域,為什么呢?因?yàn)榛卣{(diào)函數(shù)被當(dāng)做 setTimeout 的參數(shù)傳引用過(guò)去了,假設(shè) setTimeout 實(shí)現(xiàn)如下

var setTimeout = function (callback, delay) {
  // 延遲
  callback()
}

看到?jīng)],因?yàn)?setTimeout 屬于異步函數(shù),所以會(huì)等到 JS 執(zhí)行完畢之后再調(diào)用 callback,所以這段時(shí)間 callback 一直存在,所以函數(shù) apple 也一直存在,所以全局作用域并不會(huì)等 JavaScript 執(zhí)行完畢后就銷(xiāo)毀(函數(shù) apple 屬于全局作用域的),這時(shí)候循環(huán)早結(jié)束了,所以 i 也變成了 5,于是乎,這個(gè)時(shí)候 apple 對(duì)全局作用域的引用稱(chēng)為閉包!

上面也說(shuō)了回調(diào)函數(shù)調(diào)用都會(huì)產(chǎn)生閉包,這里就當(dāng)舉例說(shuō)明一下!

那么怎么解決以上問(wèn)題呢,很簡(jiǎn)單,讓回調(diào)函數(shù)記住不同的作用域就行了!

for (var i = 0; i < 5; i++) {
  // 為了方便說(shuō)明,給函數(shù)起名叫 apple
  (function baz (i) {
    setTimeout(function apple () {
      console.log(i)
    }, 0)
  })(i)  // 0 1 2 3 4
}

上面用立即執(zhí)行函數(shù)解決了問(wèn)題,因?yàn)楹瘮?shù)有局部作用域,所以調(diào)用 5 次函數(shù)會(huì)產(chǎn)生 5 個(gè)局部作用域,每個(gè)作用域的 i 由各次循環(huán)的 i 傳遞賦值,而每個(gè)作用域內(nèi)都存在 apple ,都記住了各自的作用域,也就取到了不同的 i

不過(guò)通常來(lái)說(shuō),閉包都是按以下方式產(chǎn)生:

function apple () {
  var name = "apple"

  var output = function () {
    console.log(name)
  }

  return output
}

var out = apple()
out()  // apple

上述將函數(shù)傳引用給了全局作用域的變量,顯然,閉包(對(duì) apple 作用域的引用)在全局作用域都存在的情況下都可能發(fā)生,而且后面也執(zhí)行了 out()

更常見(jiàn)的寫(xiě)法是下面這種:

function Apple () {
  var name = "apple"

  var output = function () {
    console.log(name)
  }

  var setName = function (arg) {
    name = arg
  }

  return {
    output: output,
    setName: setName
  }
}

var apple = Apple()
apple.output()  // apple
apple.setName("Apple")
apple.output()  // Apple

這就是模塊的一個(gè)例子,name 通常被稱(chēng)為私有變量!

結(jié)語(yǔ)

閉包沒(méi)什么了不起的,這是被人玩的過(guò)于玄乎,其實(shí)這是人們很自然的想法:我在別的地方調(diào)用函數(shù),總得保持函數(shù)正常運(yùn)行吧!“閉包”這種機(jī)制很輕松的幫你解決了這個(gè)問(wèn)題,我們不必搞懂閉包是什么也經(jīng)常在實(shí)現(xiàn)它(如果這句話(huà)寫(xiě)在前面,會(huì)不會(huì)很多人都不看了,哈哈),這是語(yǔ)言設(shè)計(jì)者的過(guò)人之處,但是,你不搞懂它,總被人質(zhì)疑:你不懂閉包吧!實(shí)際上,我們都實(shí)現(xiàn)了很多次閉包,所以,你把內(nèi)部機(jī)制詳細(xì)搞清楚了,就不會(huì)再害怕別人的質(zhì)疑了,哈哈!當(dāng)然,如果你喜歡鉆研,更有必要了解其中的機(jī)制了,體會(huì)到尋找語(yǔ)言設(shè)計(jì)者設(shè)計(jì)思路的快感!

最后,再總結(jié)一下:函數(shù)記住并訪(fǎng)問(wèn)其所在的詞法作用域,叫做閉包現(xiàn)象,而此時(shí)函數(shù)對(duì)作用域的引用叫做閉包。
最后的最后,再?gòu)?qiáng)調(diào)一下:閉包就是引用!

更多文章請(qǐng)看我的個(gè)人博客

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/80778.html

相關(guān)文章

  • JS 中的閉包是什么?

    摘要:大名鼎鼎的閉包面試必問(wèn)。閉包的作用是什么。看到閉包在哪了嗎閉包到底是什么五年前,我也被這個(gè)問(wèn)題困擾,于是去搜了并總結(jié)下來(lái)。關(guān)于閉包的謠言閉包會(huì)造成內(nèi)存泄露錯(cuò)。閉包里面的變量明明就是我們需要的變量,憑什么說(shuō)是內(nèi)存泄露這個(gè)謠言是如何來(lái)的因?yàn)椤? 本文為饑人谷講師方方原創(chuàng)文章,首發(fā)于 前端學(xué)習(xí)指南。 大名鼎鼎的閉包!面試必問(wèn)。請(qǐng)用自己的話(huà)簡(jiǎn)述 什么是「閉包」。 「閉包」的作用是什么。 首先...

    Enlightenment 評(píng)論0 收藏0
  • 閉包?反正看完我就懂了

    摘要:閉包反正看完我就懂了想要好好的理解閉包,你得首先理解作用域。其實(shí)這個(gè)閉包的產(chǎn)生過(guò)程可以理解為在里面的匿名函數(shù)定義時(shí)正處于懷孕階段,到外面調(diào)用時(shí),娃就出生了,娃就是閉包啦。閉包改變了變量的生命周期,變量將得到永生。 閉包?反正看完我就懂了 想要好好的理解閉包,你得首先理解作用域。別說(shuō)了,趕緊去看作用域吧,?,這世界就是如此殘酷。好,言歸正傳,我們是來(lái)學(xué)習(xí)閉包的。O(∩_∩)O 什么是閉包...

    sean 評(píng)論0 收藏0
  • 理解 JavaScript(二)

    摘要:所以形式參數(shù)是本地的,不是外部的或者全局的。這叫做函數(shù)聲明,函數(shù)聲明會(huì)連通命名和函數(shù)體一起被提升至作用域頂部。這叫做函數(shù)表達(dá)式,函數(shù)表達(dá)式只有命名會(huì)被提升,定義的函數(shù)體則不會(huì)。 Scoping & Hoisting var a = 1; function foo() { if (!a) { var a = 2; } alert(a); }; ...

    luxixing 評(píng)論0 收藏0
  • JS基礎(chǔ)篇--函數(shù)聲明與定義,作用,函數(shù)聲明與表達(dá)式的區(qū)別

    摘要:在中,有四種方式可以讓命名進(jìn)入到作用域中按優(yōu)先級(jí)語(yǔ)言定義的命名比如或者,它們?cè)谒凶饔糜騼?nèi)都有效且優(yōu)先級(jí)最高,所以在任何地方你都不能把變量命名為之類(lèi)的,這樣是沒(méi)有意義的形式參數(shù)函數(shù)定義時(shí)聲明的形式參數(shù)會(huì)作為變量被至該函數(shù)的作用域內(nèi)。 Scoping & Hoisting 例: var a = 1; function foo() { if (!a) { var ...

    TerryCai 評(píng)論0 收藏0
  • 8道經(jīng)典JavaScript面試題解析,真的掌握J(rèn)avaScript了嗎

    摘要:瀏覽器的主要組成包括有調(diào)用堆棧,事件循環(huán),任務(wù)隊(duì)列和。好了,現(xiàn)在有了前面這些知識(shí),我們可以看一下這道題的講解過(guò)程實(shí)現(xiàn)步驟調(diào)用會(huì)將函數(shù)放入調(diào)用堆棧。由于調(diào)用堆棧是空的,事件循環(huán)將選擇回調(diào)并將其推入調(diào)用堆棧進(jìn)行處理。進(jìn)程再次重復(fù),堆棧不會(huì)溢出。 JavaScript是前端開(kāi)發(fā)中非常重要的一門(mén)語(yǔ)言,瀏覽器是他主要運(yùn)行的地方。JavaScript是一個(gè)非常有意思的語(yǔ)言,但是他有很多一些概念,大...

    taowen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<