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

資訊專欄INFORMATION COLUMN

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

yangrd / 2918人閱讀

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

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

前提

首先,我們需要知道 JavaScript 里面的函數(shù)會(huì)創(chuàng)建內(nèi)部詞法作用域,是的,JavaScript 是詞法作用域,也就是說作用域與作用域的層級(jí)關(guān)系在你書寫的時(shí)候就已經(jīng)確定了,而不是調(diào)用的時(shí)候,調(diào)用的時(shí)候確定的稱為動(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è) JavaScript 是動(dòng)態(tài)作用域,就要看函數(shù)的調(diào)用順序了,由于 apple 是在 orange 中調(diào)用的,所以 apple 的上層作用域是 orange 的局部作用域,那樣的話會(huì)輸出 orange!

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

什么是閉包?

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

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

什么叫記住作用域?

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

// 沒有閉包現(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í)候,本來 apple 在執(zhí)行完畢之后 apple 的局部作用域就應(yīng)該被銷毀,但是由于 fruit(output)output 傳引用給了 arg,所以在 fruit 執(zhí)行的這段時(shí)間內(nèi),arg 肯定是存在的,被引用的函數(shù) output 也得存在,而 output 依賴的 apple 函數(shù)產(chǎn)生的局部作用域也得存在,這就是所謂的“記住”,把作用域給記住了!

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

記住并訪問

上面的代碼,稍微修改一下就會(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í)際上訪問并執(zhí)行了 apple 局部作用域的 output,不僅僅是這樣,output 內(nèi)部還訪問了 count 變量,這兩次對(duì) apple 局部作用域的引用都是閉包!

所以,之所以說所有回調(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ù)所在的詞法作用域至少被訪問了一次,也就是至少訪問回調(diào)函數(shù)本身,而這個(gè)對(duì)作用域的引用就是閉包。

閉包的作用

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

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

能夠訪問函數(shù)所在詞法作用域的變量;

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

還有很多,就不一一說了,下面就是利用閉包來解決一個(gè)常見的問題:

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

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

其實(shí),上面也也會(huì)產(chǎn)生閉包,只不過 apple 記住并訪問的是全局作用域,為什么呢?因?yàn)榛卣{(diào)函數(shù)被當(dāng)做 setTimeout 的參數(shù)傳引用過去了,假設(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í)行完畢后就銷毀(函數(shù) apple 屬于全局作用域的),這時(shí)候循環(huán)早結(jié)束了,所以 i 也變成了 5,于是乎,這個(gè)時(shí)候 apple 對(duì)全局作用域的引用稱為閉包!

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

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

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

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

不過通常來說,閉包都是按以下方式產(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()

更常見的寫法是下面這種:

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 通常被稱為私有變量!

結(jié)語

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

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

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

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

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

相關(guān)文章

  • JS 中的閉包是什么?

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

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

    摘要:閉包反正看完我就懂了想要好好的理解閉包,你得首先理解作用域。其實(shí)這個(gè)閉包的產(chǎn)生過程可以理解為在里面的匿名函數(shù)定義時(shí)正處于懷孕階段,到外面調(diào)用時(shí),娃就出生了,娃就是閉包啦。閉包改變了變量的生命周期,變量將得到永生。 閉包?反正看完我就懂了 想要好好的理解閉包,你得首先理解作用域。別說了,趕緊去看作用域吧,?,這世界就是如此殘酷。好,言歸正傳,我們是來學(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í)語言定義的命名比如或者,它們?cè)谒凶饔糜騼?nèi)都有效且優(yōu)先級(jí)最高,所以在任何地方你都不能把變量命名為之類的,這樣是沒有意義的形式參數(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í),我們可以看一下這道題的講解過程實(shí)現(xiàn)步驟調(diào)用會(huì)將函數(shù)放入調(diào)用堆棧。由于調(diào)用堆棧是空的,事件循環(huán)將選擇回調(diào)并將其推入調(diào)用堆棧進(jìn)行處理。進(jìn)程再次重復(fù),堆棧不會(huì)溢出。 JavaScript是前端開發(fā)中非常重要的一門語言,瀏覽器是他主要運(yùn)行的地方。JavaScript是一個(gè)非常有意思的語言,但是他有很多一些概念,大...

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

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

0條評(píng)論

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