成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

重讀你不知道的JS (上) 第一節(jié)五章

worldligang / 3269人閱讀

摘要:詞法作用域的查找規(guī)則是閉包的一部分。因此的確同閉包息息相關(guān),即使本身并不會(huì)真的使用閉包。而上面的創(chuàng)建一個(gè)閉包,本質(zhì)上這是將一個(gè)塊轉(zhuǎn)換成一個(gè)可以被關(guān)閉的作用域。結(jié)合塊級(jí)作用域與閉包模塊這個(gè)模式在中被稱為模塊。

你不知道的JS(上卷)筆記

你不知道的 JavaScript

JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 JavaScript 開發(fā)者,如果沒有認(rèn)真學(xué)習(xí)的話也無法真正理解它們.

上卷包括倆節(jié):

作用域和閉包

this 和對(duì)象原型

作用域和閉包

希望 Kyle 對(duì) JavaScript 工作原理每一個(gè)細(xì)節(jié)的批判性思 考會(huì)滲透到你的思考過程和日常工作中。知其然,也要知其所以然。

作用域閉包 啟示

秘訣: JavaScript中閉包無處不在,你只需要能夠識(shí)別并擁抱它。

閉包是基于詞法作用域書寫代碼時(shí)所產(chǎn)生的自然結(jié)果,你甚至不需要為了利用它們而有意識(shí)的創(chuàng)建閉包。

閉包的創(chuàng)建和使用在你的代碼中隨處可見。
你缺少的是根據(jù)你自己的意愿來識(shí)別、擁抱和影響閉包的思維環(huán)境。

實(shí)質(zhì)問題

當(dāng)函數(shù)可以記住并訪問所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行。

詞法作用域的查找規(guī)則是閉包的一部分。

function foo() {
  var a = 2;
  function bar() {
    console.log( a ); // 2
  }
  bar();
}
foo();

純學(xué)術(shù)的角度上說,上述代碼片段中,函數(shù) bar() 具有一個(gè)涵蓋 foo() 作用域的閉包 (事實(shí)上,涵蓋了它能訪問的所有作用域,比如全局作用域)。也可以認(rèn)為 bar() 被封閉在了 foo() 的作用域中。為什么呢?原因簡(jiǎn)單明了,因?yàn)?bar() 嵌套在 foo() 內(nèi)部。

閉包使得函數(shù)可以繼續(xù)訪問定義時(shí)的詞法作用域。

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

思考:

function foo() {
  var a = 2;
  function baz() {
    console.log( a, b ); // 2 , b能獲取到1嗎?
  }
  bar( baz );
}
function bar(fn) {
  var b = 1;
  fn(); // 媽媽快看呀,這就是閉包!
}
常見的閉包場(chǎng)景
function wait(message) {
  setTimeout( function timer() { // timer函數(shù)由引擎調(diào)用,但是已經(jīng)超出了wait作用域,所以存在閉包
      console.log( message );
  }, 1000 );
}
wait( "Hello, closure!" );

在定時(shí)器、事件監(jiān)聽器、 Ajax 請(qǐng)求、跨窗口通信、Web Workers 或者任何其他的異步(或者同步)任務(wù)中,只要使 用了回調(diào)函數(shù),實(shí)際上就是在使用閉包!

IIFE閉包
var a = 2;
(function IIFE() {
  console.log( a );
})();

盡管 IIFE 本身并不是觀察閉包的恰當(dāng)例子,但它的確創(chuàng)建了閉包,并且也是最常用來創(chuàng)建 可以被封閉起來的閉包的工具。因此 IIFE 的確同閉包息息相關(guān),即使本身并不會(huì)真的使用 閉包。

循環(huán)和閉包
for (var i=1; i<=5; i++) {
  setTimeout( function timer() {
    console.log( i );
  }, i*1000 );
}

延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行。事實(shí)上, 當(dāng)定時(shí)器運(yùn)行時(shí)即使每個(gè)迭代中執(zhí)行的是setTimeout(.., 0),所有的回調(diào)函數(shù)依然是在循 環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè) 6 出來。

缺陷是我們?cè)噲D假設(shè)循環(huán)中的每個(gè)迭代在運(yùn)行時(shí)都會(huì)給自己“捕獲”一個(gè) i 的副本。但是 根據(jù)作用域的工作原理,實(shí)際情況是盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的, 但是它們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè) i。
這樣說的話,當(dāng)然所有函數(shù)共享一個(gè) i 的引用。

它需要有自己的變量,用來在每個(gè)迭代中儲(chǔ)存 i 的值:

for (var i=1; i<=5; i++) { 
  (function() { // IIFE 每次執(zhí)行都會(huì)立即創(chuàng)建一個(gè)詞法上的函數(shù)作用域
    var j = i; // 閉包作用域的變量j, 立即得到i的值 
    setTimeout( function timer() {
                 console.log( j ); // 訪問閉包作用域的變量j
             }, j*1000 );
  })();
}

變體:

for (var i=1; i<=5; i++) {
  (function(j) { // IIFE 每次執(zhí)行都會(huì)立即創(chuàng)建一個(gè)詞法上的函數(shù)作用域
    // 閉包作用域的變量j, 參數(shù)傳遞立即得到i的值
    setTimeout( function timer() {
                 console.log( j ); // 訪問閉包作用域的變量j
             }, j*1000 );
  })(i);
}
塊作用域和閉包

let 聲明,可以用來劫 持塊作用域,并且在這個(gè)塊作用域中聲明一個(gè)變量。

而上面的IIFE創(chuàng)建一個(gè)閉包,本質(zhì)上這是將一個(gè)塊轉(zhuǎn)換成一個(gè)可以被關(guān)閉的作用域。

結(jié)合塊級(jí)作用域與閉包:

for (let i=1; i<=5; i++) {
  setTimeout( function timer() {
    console.log( i );
  }, i*1000 );
}
模塊
function CoolModule() {
  var something = "cool";
  var another = [1, 2, 3];
  function doSomething() { 
    console.log( something );
  }
  function doAnother() {
    console.log( another.join( " ! " ) );
  }
  return {
    doSomething1: doSomething,
    doAnother: doAnother
  };
}
var foo = CoolModule(); 
foo.doSomething1(); // cool
foo.doAnother(); // 1 ! 2 ! 3

這個(gè)模式在 JavaScript 中被稱為模塊。最常見的實(shí)現(xiàn)模塊模式的方法通常被稱為模塊暴露, 這里展示的是其變體。
doSomething() 和 doAnother() 函數(shù)具有涵蓋模塊實(shí)例內(nèi)部作用域的閉包(通過調(diào)用 CoolModule() 實(shí)現(xiàn))。
當(dāng)通過返回一個(gè)含有屬性引用的對(duì)象的方式來將函數(shù)傳遞到詞法作 用域外部時(shí),我們已經(jīng)創(chuàng)造了可以觀察和實(shí)踐閉包的條件。

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

必須有外部的封閉函數(shù),該函數(shù)必須至少被調(diào)用一次(每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的模塊 實(shí)例)。

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

一個(gè)具有函數(shù)屬性的對(duì)象本身并不是真正的模塊。從方便觀察的角度看,一個(gè)從函數(shù)調(diào)用 所返回的,只有數(shù)據(jù)屬性而沒有閉包函數(shù)的對(duì)象并不是真正的模塊。

模塊模式另一個(gè)簡(jiǎn)單但強(qiáng)大的變化用法是,命名將要作為公共 API 返回的對(duì)象:
在上述模塊中,dosomething1被作為模塊內(nèi)部dosomething的公開訪問名。

現(xiàn)代的模塊機(jī)制
var MyModules = (function Manager() { // 模塊 管理器/依賴加載器
  var modules = {};
  function define(name, deps, impl) {
    for (var i=0; i
未來的模塊機(jī)制

基于函數(shù)的模塊并不是一個(gè)能被穩(wěn)定識(shí)別的模式(編譯器無法識(shí)別),它們 的 API 語義只有在運(yùn)行時(shí)才會(huì)被考慮進(jìn)來。因此可以在運(yùn)行時(shí)修改一個(gè)模塊 的 API。
相比之下,ES6 模塊 API 更加穩(wěn)定(API 不會(huì)在運(yùn)行時(shí)改變)。由于編輯器知 道這一點(diǎn),因此可以在(的確也這樣做了)編譯期檢查對(duì)導(dǎo)入模塊的 API 成 員的引用是否真實(shí)存在。如果 API 引用并不存在,編譯器會(huì)在運(yùn)行時(shí)拋出一 個(gè)或多個(gè)“早期”錯(cuò)誤,而不會(huì)像往常一樣在運(yùn)行期采用動(dòng)態(tài)的解決方案。

ES6 的模塊沒有“行內(nèi)”格式,必須被定義在獨(dú)立的文件中(一個(gè)文件一個(gè)模塊)。瀏覽 器或引擎有一個(gè)默認(rèn)的“模塊加載器”(可以被重載,但這遠(yuǎn)超出了我們的討論范圍)可 以在導(dǎo)入模塊時(shí)異步地加載模塊文件。

小結(jié)

閉包就好像從 JavaScript 中分離出來的一個(gè)充滿神秘色彩的未開化世界,只有最勇敢的人 才能夠到達(dá)那里。但實(shí)際上它只是一個(gè)標(biāo)準(zhǔn),顯然就是關(guān)于如何在函數(shù)作為值按需傳遞的 詞法環(huán)境中書寫代碼的。
當(dāng)函數(shù)可以記住并訪問所在的詞法作用域,即使函數(shù)是在當(dāng)前詞法作用域之外執(zhí)行,這時(shí) 就產(chǎn)生了閉包。
如果沒能認(rèn)出閉包,也不了解它的工作原理,在使用它的過程中就很容易犯錯(cuò),比如在循 環(huán)中。但同時(shí)閉包也是一個(gè)非常強(qiáng)大的工具,可以用多種形式來實(shí)現(xiàn)模塊等模式。
模塊有兩個(gè)主要特征:

(1)為創(chuàng)建內(nèi)部作用域而調(diào)用了一個(gè)包裝函數(shù);
(2)包裝函數(shù)的返回 值必須至少包括一個(gè)對(duì)內(nèi)部函數(shù)的引用,這樣就會(huì)創(chuàng)建涵蓋整個(gè)包裝函數(shù)內(nèi)部作用域的閉 包。

現(xiàn)在我們會(huì)發(fā)現(xiàn)代碼中到處都有閉包存在,并且我們能夠識(shí)別閉包然后用它來做一些有用 的事!

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101541.html

相關(guān)文章

  • 重讀你不知道JS () 一節(jié)四章

    摘要:如果提升改變了代碼執(zhí)行的順序,會(huì)造成非常嚴(yán)重的破壞。聲明本身會(huì)被提升,而包括函數(shù)表達(dá)式的賦值在內(nèi)的賦值操作并不會(huì)提升。要注意避免重復(fù)聲明,特別是當(dāng)普通的聲明和函數(shù)聲明混合在一起的時(shí)候,否則會(huì)引起很多危險(xiǎn)的問題 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 Ja...

    chanjarster 評(píng)論0 收藏0
  • 重讀你不知道JS () 一節(jié)三章

    摘要:如果是聲明中的第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式。給函數(shù)表達(dá)式指定一個(gè)函數(shù)名可以有效的解決以上問題。始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐。也有開發(fā)者干脆關(guān)閉了靜態(tài)檢查工具對(duì)重復(fù)變量名的檢查。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 Ja...

    lavor 評(píng)論0 收藏0
  • 重讀你不知道JS () 一節(jié)二章

    摘要:詞法作用域定義在詞法階段的作用域由你在寫代碼時(shí)將變量和塊作用域?qū)懺谀膩頉Q定的,因此當(dāng)詞法分析器處理代碼時(shí)會(huì)保持作用域不變。欺騙詞法作用域在詞法分析器處理過后依然可以修改作用域。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 JavaScript 開發(fā)者,如果沒...

    baihe 評(píng)論0 收藏0
  • 重讀你不知道JS () 一節(jié)一章

    摘要:的抽象語法樹中可能會(huì)有一個(gè)叫作的頂級(jí)節(jié)點(diǎn),接下來是一個(gè)叫作它的值是的子節(jié)點(diǎn),以及一個(gè)叫作的子節(jié)點(diǎn)。值得注意的是,是非常重要的異常類型。嚴(yán)格模式下,未聲明的和倆者行為相同,都會(huì)是。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡(jiǎn)單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 JavaScript 開發(fā)者,如果...

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

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

0條評(píng)論

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