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

資訊專欄INFORMATION COLUMN

還擔(dān)心面試官問閉包?

tinyq / 701人閱讀

摘要:一言以蔽之,閉包,你就得掌握。當(dāng)函數(shù)記住并訪問所在的詞法作用域,閉包就產(chǎn)生了。所以閉包才會得以實(shí)現(xiàn)。從技術(shù)上講,這就是閉包。執(zhí)行后,他的內(nèi)部作用域并不會消失,函數(shù)依然保持有作用域的閉包。

網(wǎng)上總結(jié)閉包的文章已經(jīng)爛大街了,不敢說筆者這篇文章多么多么xxx,只是個人理解總結(jié)。各位看官瞅瞅就好,大神還希望多多指正。此篇文章總結(jié)與《JavaScript忍者秘籍》 《你不知道的JavaScript上卷》

系列博客地址:https://github.com/Nealyang/YOU-SHOULD-KNOW-JS

安利個人react技術(shù)棧+express+mongoose實(shí)戰(zhàn)個人博客教程 React-Express-Blog-Demo

前言

為什么我們需要理解并且掌握閉包,且不說大道理,就問你要不要成為JavaScript高手?不要?那你要不要面試找工作嘛。。。

再者,對于任何一個前端er或者JavaScript開發(fā)者來說,理解閉包可以看做是另一種意義上的重生。閉包是純函數(shù)編程語言的一個特性,因?yàn)樗蟠蠛喕瘡?fù)雜的操作,所以很容易在一些JavaScript庫以及其他高級代碼中找到閉包的使用。

一言以蔽之,閉包,你就得掌握。

談?wù)勯]包之前,我們先說說作用域

這里我們要說的作用域值得是詞法作用域。詞法作用域即為定義在詞法階段的作用域。換句話說,就是你寫代碼時將變量和塊作用域?qū)懺谀睦锼鶝Q定的。因此在詞法解析的時會保持作用域不變。(JavaScript引擎在運(yùn)行JavaScript代碼的時候大致經(jīng)過分詞/詞法分析、解析/語法分析、代碼生成三個步驟)。

老規(guī)矩,看代碼(就是代碼多~~)

function foo(a) {
  var b = a*2;
  function bar(c) {
    console.log(a,b,c);
  }
  bar(b*3);
}
foo(2);//2 4 12

這個例子中有三個逐級嵌套的作用域,如圖:


截圖來自《你不知道的JavaScript》

部分一包含整個作用域也就是全局作用域。其中包含標(biāo)識符:foo

部分二包含foo所創(chuàng)建的作用域,其中包含:a,bar和b

部分三包含bar所創(chuàng)建的作用域,其中包含:c

這些作用域氣泡的包含關(guān)系給引擎提供了足夠多的位置信息。在上面的代碼中,引擎執(zhí)行console.log的時候,并查找a,b,c。他首先在最里面的作用域,也就是bar(...)函數(shù)的作用域。引擎無法在這一層作用域中找到變量a,因此引擎會去上一級嵌套作用域foo(...)中查找,如果找到了,則即使用。

如果a,c 都存在作用域bar(...),foo(...)作用域中,console.log(...)即不需要到foo的外部作用域中去查找變量。

無論函數(shù)在哪里被調(diào)用,且無論他們?nèi)绾伪徽{(diào)用,他的詞法作用域都只由函數(shù)被聲明的位置決定的。詞法作用域查找只會查找一級標(biāo)識符,比如a,b和c。

簡單理解詞法作用域的概念,其實(shí)也就是我們常說的作用域,關(guān)于JavaScript中欺騙詞法以及更多關(guān)于詞法作用域的介紹,請翻閱《你不知道的JavaScript》。

閉包的概念

說到閉包的概念,這里還真的比較模糊,我們且看下各種經(jīng)典書籍給出的概念

《JavaScript權(quán)威指南》中的概念

函數(shù)對象可以通過作用域鏈互相關(guān)聯(lián)起來,函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計算機(jī)科學(xué)中成為閉包

《JavaScript權(quán)威指南》中的概念

閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。

《JavaScript忍者秘籍》中的概念

閉包是一個函數(shù)在創(chuàng)建時允許該自身函數(shù)訪問并操作該自身函數(shù)以外的變量時所創(chuàng)建的作用域。

《你不知道的JavaScript》中的概念

閉包是基于詞法作用域書寫代碼時所產(chǎn)生的自然結(jié)果。當(dāng)函數(shù)記住并訪問所在的詞法作用域,閉包就產(chǎn)生了。

個人理解

閉包就是一個函數(shù),一個可以訪問并操作其他函數(shù)內(nèi)部變量的函數(shù)。也可以說是一個定義在函數(shù)內(nèi)部的函數(shù)。因?yàn)镴avaScript沒有動態(tài)作用域,而閉包的本質(zhì)是靜態(tài)作用域(靜態(tài)作用域規(guī)則查找一個變量聲明時依賴的是源程序中塊之間的靜態(tài)關(guān)系),所以函數(shù)訪問的都是我們定義時候的作用域,也就是詞法作用域。所以閉包才會得以實(shí)現(xiàn)。

我們常見的閉包形式就是a 函數(shù)套 b 函數(shù),然后 a 函數(shù)返回 b 函數(shù),這樣 b 函數(shù)在 a 函數(shù)以外的地方執(zhí)行時,依然能訪問 a 函數(shù)的作用域。其中“b 函數(shù)在 a 函數(shù)以外的地方執(zhí)行時”這一點(diǎn),才體現(xiàn)了閉包的真正的強(qiáng)大之處。

實(shí)質(zhì)性的問題
function outer() {
  var a = 2;
  function inner() {
    console.log(a);//2
  }
  inner();
}
outer();

基于詞法作用域和查找規(guī)則,inner函數(shù)是可以訪問到outer內(nèi)部定義的變量a的。從技術(shù)上講,這就是閉包。但是也可以說不是,因?yàn)橛脕斫忉宨nner對a的引用方法是詞法作用域的查找規(guī)則,而這些規(guī)則只是閉包中的一部分而已。

下面我們將上面的代碼修改下,讓我們能夠清晰的看到閉包

function outer() {
  var a = 2;
  function inner() {
    console.log(a);
  }
  return inner;
}
var neal = outer();
neal();//2

可能是所有講解閉包的博客中都用爛了的例子了。這里inner函數(shù)被正常調(diào)用執(zhí)行,并且可以訪問到outer函數(shù)里定義的變量a。講道理,在outer函數(shù)運(yùn)行后,通常函數(shù)整個內(nèi)部作用域都會被銷毀。

而閉包的神奇之處正是如此可以阻止垃圾回收這種事情的發(fā)生,事實(shí)上,內(nèi)部作用域已然存在且拿著a變量,所以沒有被回收。inner函數(shù)擁有outer函數(shù)內(nèi)部作用域的閉包,使得該作用域能夠一直存活,以供inner函數(shù)在之后的任何時間可以訪問。

inner()已然持有對該作用域的引用,而這個引用就被叫做閉包。

函數(shù)在定義時的詞法作用域以外的地方被調(diào)用,閉包使得函數(shù)可以繼續(xù)訪問定義時的詞法作用域。

無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會持有對原始定義作用域的引用,無論在何處執(zhí)行這個函數(shù)都會使用閉包

var fn;
function foo() {
  var a = 2;
  function baz() {
    console.log(a);
  }
  fn = baz;
}

function bar() {
  fn();
}
foo();
bar();

上面的代碼不做過多解釋,挺簡單,通過下面的代碼,我們再說下閉包的三個有趣的概念

var outerValue = "ninja";
var later;
function outFunction() {
  var innerValue = "Neal";
  function innerFunction(param){
      console.log(outerValue,innerValue,param,tooLate);
  }
  later = innerFunction;
}
console.log("tooLate is",tooLate);
outFunction();
later("Nealyang");
var tooLate = "Haha";
later("Neal_yang");

//tooLate is undefined
//ninja Neal Nealyang undefined
//ninja Neal Neal_yang Haha

上面代碼運(yùn)行結(jié)果大家可以自行嘗試??傊瑥纳厦娴拇a中,我們可以看到閉包的有趣的三個概念

內(nèi)部函數(shù)的參數(shù)包含在閉包中

作用域之外的所有變量、即便是函數(shù)聲明之后的那些聲明,也都包含在閉包中.

相同作用域內(nèi),尚未聲明的變量,不能進(jìn)行提前引用

代碼處處有閉包
function wait(message) {
     setTimeout( function timer() {
         console.log( message ); }, 1000 ); }
wait( "Hello, closure!" );

如上的代碼,一個很常見的定時器,但是timer函數(shù)具有涵蓋wait作用域的閉包,因?yàn)榇诉€保留對變量Message的引用。

wait執(zhí)行1s后,他的內(nèi)部作用域并不會消失,timer函數(shù)依然保持有wait作用域的閉包。

深入到引擎內(nèi)部原理中,內(nèi)置的g工具函數(shù)setTimeout持有對一個參數(shù)的引用,引擎調(diào)用這個函數(shù),在例子中就是內(nèi)部的timer函數(shù),而詞法作用域在這個過程中保持完整。這就是閉包。

無論何時何地,如果將函數(shù)作為第一級值類型并到處傳遞,你就會看到閉包在這些函數(shù)中的使用。在定時器、事件監(jiān)聽、Ajax請求、跨窗口通信或者其他異步任務(wù)中,只要使用回調(diào)函數(shù),就在使用閉包。

在經(jīng)典的for循環(huán)中使用閉包
for (var i=1; i<=5; i++) {
     setTimeout( function timer() {
         console.log( i ); }, i*1000 ); }

如上for循環(huán),大家都知道輸出6,畢竟這個作用域中,我們只有一個i,所有的回調(diào)函數(shù)都是在這個for循環(huán)結(jié)束以后才執(zhí)行的。

如果我們試圖假設(shè)循環(huán)中的每一個迭代在運(yùn)行時都會給自己捕獲一個i的副本,但是根據(jù)作用域的工作原理,盡管循環(huán)中五個函數(shù)是在各個迭代中分別定義,但是他們都被封閉在共享的作用域中,因此還是只有一個i。

所以回到正題,我們需要使用閉包,在每一個循環(huán)中每一個迭代都讓他產(chǎn)生一個閉包作用域。

所以我們代碼修改如下:

for (var i=1; i<=5; i++) { (function() {
         setTimeout( function timer() {
             console.log( i ); }, i*1000 ); })(); }

but?。?!你也發(fā)現(xiàn)了,這樣并不姓,不是IIFE會產(chǎn)生一個閉包的么?是的沒錯,但是如果這個IIFE產(chǎn)生的閉包作用域是可空的,那么將它封裝起來又有什么意義呢?所以它需要點(diǎn)實(shí)質(zhì)性的東西,讓我們?nèi)ナ褂谩?/p>

 for (var i=1; i<=5; i++) { (function(j) {
         setTimeout( function timer() {
console.log( j ); }, j*1000 ); })( i ); }

當(dāng)然,如上問題我們可以使用es6中的let來解決。但是這里就不做過多說明了。大家可以自行Google。

模塊

這個部分比較簡單好理解,因?yàn)殚]包可以很好形成塊級作用域,對內(nèi)部變量有很好的隱藏。所以自然我們可以將其作為模塊開發(fā)的手段。撇開如今的export、import不談

直接看例子就好,操作比較常規(guī)

function foo() {
    var something = "cool"; 
    var another = [1, 2, 3];
    function doSomething() {
         console.log( something ); 
    } 
    function doAnother() {
         console.log( another.join( " ! " ) ); 
    }
    return {
        doSomething:doSomething,
        doAnother:doAnother
    }
}

簡單說明下,doSomething和doAnother函數(shù)具有涵蓋模塊實(shí)例內(nèi)部作用域的閉包。當(dāng)通過返回一個含有屬性引用的對象的方式來將函數(shù)傳遞到詞法作用域外部,我們已經(jīng)創(chuàng)造了可以觀察和實(shí)踐的 閉包條件。

必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次

封閉函數(shù)必須返回至少一個內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或修改私有的狀態(tài)。

當(dāng)然,上面的代碼我們還可以寫成IIFE的形式。但是畢竟市場上講解閉包的好文是在太多,這里我們就點(diǎn)到為止。

交流

掃碼關(guān)注我的個人微信公眾號,分享更多原創(chuàng)文章。點(diǎn)擊交流學(xué)習(xí)加我微信、qq群。一起學(xué)習(xí),一起進(jìn)步

歡迎兄弟們加入:

Node.js技術(shù)交流群:209530601

React技術(shù)棧:398240621

前端技術(shù)雜談:604953717 (新建)

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

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

相關(guān)文章

  • 面試官問我:什么是JavaScript閉包,我該如何回答

    摘要:到底什么是閉包這個問題在面試是時候經(jīng)常都會被問,很多小白一聽就懵逼了,不知道如何回答好。上面這么說閉包是一種特殊的對象。閉包的注意事項(xiàng)通常,函數(shù)的作用域及其所有變量都會在函數(shù)執(zhí)行結(jié)束后被銷毀。從而使用閉包模塊化代碼,減少全局變量的污染。 閉包,有人說它是一種設(shè)計理念,有人說所有的函數(shù)都是閉包。到底什么是閉包?這個問題在面試是時候經(jīng)常都會被問,很多小白一聽就懵逼了,不知道如何回答好。這個...

    BenCHou 評論0 收藏0
  • 前端基礎(chǔ)

    摘要:談起閉包,它可是兩個核心技術(shù)之一異步基于打造前端持續(xù)集成開發(fā)環(huán)境本文將以一個標(biāo)準(zhǔn)的項(xiàng)目為例,完全拋棄傳統(tǒng)的前端項(xiàng)目開發(fā)部署方式,基于容器技術(shù)打造一個精簡的前端持續(xù)集成的開發(fā)環(huán)境。 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日...

    graf 評論0 收藏0
  • 拒信收割機(jī)の前端面經(jīng)(CVTE,唯品會,百度)

    摘要:另外回答的時候要淡定,一些問題就算不懂也不能慌,要和面試官談笑風(fēng)生,然后盡量扯回到自己懂的東西上面大公司比如百度給我的感覺就是很重視基礎(chǔ)思維和潛力。 —— 雖然我的offer少,但是我的拒信多啊 這幾天終于閑下來,做一點(diǎn)微小的工作,整理了一些之前幾家公司的前端面試題和個人經(jīng)驗(yàn),想做前端的師弟妹可以參考,也歡迎各同行大神來指教~ (以下問題不分先后,時間久遠(yuǎn)難免有些遺漏;很多問題面試官都...

    yzd 評論0 收藏0
  • 拒信收割機(jī)の前端面經(jīng)(CVTE,唯品會,百度)

    摘要:另外回答的時候要淡定,一些問題就算不懂也不能慌,要和面試官談笑風(fēng)生,然后盡量扯回到自己懂的東西上面大公司比如百度給我的感覺就是很重視基礎(chǔ)思維和潛力。 —— 雖然我的offer少,但是我的拒信多啊 這幾天終于閑下來,做一點(diǎn)微小的工作,整理了一些之前幾家公司的前端面試題和個人經(jīng)驗(yàn),想做前端的師弟妹可以參考,也歡迎各同行大神來指教~ (以下問題不分先后,時間久遠(yuǎn)難免有些遺漏;很多問題面試官都...

    channg 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<