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

資訊專欄INFORMATION COLUMN

《你不知道的JS》讀書筆記---作用域及閉包

denson / 3415人閱讀

摘要:注此讀書筆記只記錄本人原先不太理解的內容經過閱讀你不知道的后的理解。作用域及閉包基礎,代碼運行的幕后工作者引擎及編譯器。

注:此讀書筆記只記錄本人原先不太理解的內容經過閱讀《你不知道的JS》后的理解。
作用域及閉包基礎,JS代碼運行的幕后工作者:引擎及編譯器。引擎負責JS程序的編譯及執(zhí)行,編譯器負責詞法分析和代碼生成。那么作用域就像一個容器,引擎及編譯器都從這里提取東西。
如 var a = 2 這句簡單的代碼,聲明一個變量a,同時給他賦值為2.
背后運行的過程,編譯器階段:
進行詞法分析,將證據代碼切分成 var, a, =, 2 并逐一分析是否有特定作用,詞法分析的過程中遇到var a 會詢問作用域在當前作用域下是否有a這個變量,若有,則忽略聲明,繼續(xù)編譯,若無,就要求作用域在當前作用域聲明該變量。 而后生成 var a = 2的代碼。
引擎階段:運行var a =2 時 首先訪問當前作用域是否有a 的變量,有就賦值為2,沒有就向上查詢。若最終能找到該變量則使用該變量并給它賦值為2,若沒有就會拋出一個異常。
以上就是其背后的運行過程。

塊作用域:
JS語言中似乎是沒有相對其他語言相關的塊作用域的說法即{}框定的內容不算一個封閉的作用域,簡單的例子:

if(true){
    var b = "this is in the block"
}

console.log(b) //this is in the block

顯然b變量在if的語句塊外獲取到了,有如下幾種做法可以限定作用域:
let, IIFE(立即執(zhí)行函數(shù)表達式)[相當于變成了函數(shù)作用域], with
但是用with的時候需要注意:
簡單的例子

var ob1 = {
    a : 1
}

var ob2 = {
    b : 2
}

with(ob1) {
    a = 2
}
with(ob2) {
    a = 1
}
ob1.a // 2
ob2.a //undefined
a // 1





發(fā)現(xiàn)在ob2里面還是無法找到a的屬性,同時全局變量多了個a變量賦值為了1,這里的原理是在ob2的作用域中LHS尋找a變量,并沒有找到,則會創(chuàng)建一個屬于全局作用域的a變量(非嚴格模式)[這是硬知識,不知道其這樣實現(xiàn)的原理是什么]。所以可以在全局環(huán)境中找到a這個變量,其值為1.原理跟下述的例子類似:

function foo(){
    var a = 1
    b = a // 這里函數(shù)作用域中b變量是未聲明的,編譯器在進行LHS查詢時由于未能在所有的作用域中找到b‘
          //,(非嚴格模式下)會自動聲明一個全局變量
    something
    return something
}

就像上述的例子,只不過是換了個對象而已,由ob2換成了foo函數(shù)對象。

閉包
閉包是一個極其有意思的現(xiàn)象,官方的解釋是這樣的,函數(shù)在其所在的作用域外被調用,同時該函數(shù)訪問了其作用域中的某些變量,這就形成了一個閉包。

一個簡單的例子:

function closure(){
    var a = "here is closure"
    function out(){
        console.log(a)
    }
    return out
}

var test = closure()
a = "here is out of closure"
test() // "here is closure"

這里輸出的是"here is closure",分析下代碼:

var test = closure() //  var test = function(){
                     //             console.log(a)
                     //  }
a = "here is out of closure"
test()

理論上來說,按照作用域的概念,這里應該輸出的是上一句的a變量,這里test的函數(shù)無法訪問到closure內部的變量的。這就是閉包的作用,我的理解是,他會將作用域給鎖定,內存中原函數(shù)的內部的變量并不會被回收。因此在函數(shù)在其作用域外被調用,還是能使用其作用域中的變量a.

利用閉包這個特性,JS還有很多有意思的東西:
下面這個比較通俗點的例子,廖學峰大佬的JS教程中的例子:

   function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }
    
    var array = count()
    var f1 = array[0]
    var f2 = array[1]
    var f3 = array[2]
    f1()  // 16
    f2()  // 16
    f3()  // 16
    
    

預期的輸出應該是1, 4, 9,但實際的結果是全都是16,原因就在于JS的for循環(huán)沒有塊作用域這個屬性,數(shù)組添加元素的內容是一個函數(shù),而函數(shù)并非立即調用,他們都共享一個作用域,而作用域中的變量i只有一個,其最終的結果就是4,所以所有的輸出都是16。

要解決這個問題,可以用快作用域的方法,let, IIFE將i在迭代時的作用域鎖定。

 function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            (function(){
                var j = i  //利用IIFE時需要聲明變量接受變量i,否則IIFE中的作用域為空,向上查詢,使用                
                          //的仍然是i,而全局共享的i依舊取最終的結果
                arr.push(function () {
                             return j * j;
                        });
            })()
        }
        return arr;
    }
    
    var array = count()
    var f1 = array[0]
    var f2 = array[1]
    var f3 = array[2]
    f1()  // 1
    f2()  // 4
    f3()  // 9

這里的上下兩個例子都是閉包的例子,不同的是,第一個閉包的訪問的作用域是函數(shù)count的內部作用域共享一個變量i,取最后的值,而下一個例子,閉包訪問的作用域是一個IIFE的內部作用域,為變量j。而IIFE是類似一個函數(shù)作用域,j是不會隨著i變化而變化,這里執(zhí)行了3次IIFE,每次的IIFE對應的變量j是不同的,不會相互影響,所以達到了預期輸出,如果IIFE中作用域為空,那么閉包訪問的仍舊是count的作用域,因此依舊達不到期望的輸出。

同理let也能達到預期的輸出

 function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
                let j = i  
                arr.push(function () {
                             return j * j;
                        });
        }
        return arr;
    }
    
    var array = count()
    var f1 = array[0]
    var f2 = array[1]
    var f3 = array[2]
    f1()  // 1
    f2()  // 4
    f3()  // 9

而for循環(huán)結構有個特殊的地方,for 循環(huán)頭部的 let 聲明還會有一
個特殊的行為。這個行為指出變量在循環(huán)過程中不止被聲明一次,每次迭代都會聲明。隨
后的每個迭代都會使用上一個迭代結束時的值來初始化這個變量。
所以還可以這樣實現(xiàn),使代碼結構更為明朗:

function count() {
        var arr = [];
        for (let i=1; i<=3; i++) {
                arr.push(function () {
                             return i * i;
                        });
        }
        return arr;
    }
    
    var array = count()
    var f1 = array[0]
    var f2 = array[1]
    var f3 = array[2]
    f1()  // 1
    f2()  // 4
    f3()  // 9

以上就是對當初作用域不太理解的地方如今通過此書獲得的認識,以上只是個人的理解,依舊存有疑問,如為什么在IIFE實現(xiàn)獨立作用域的時候若不聲明var j = i或獲得的結果依舊不是期望的值,是因為閉包在當前作用域訪問不到i的時候,訪問了上一級的作用域,同時將這個作用域保存在了作用域鏈中嗎?

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

轉載請注明本文地址:http://systransis.cn/yun/90400.html

相關文章

  • 你不知道JavaScript上卷之作用域與閉包·讀書筆記

    摘要:的分句會創(chuàng)建一個塊作用域,其聲明的變量僅在中有效。而閉包的神奇作用是阻止此事發(fā)生。依然持有對該作用域的引用,而這個引用就叫做閉包。當然,無論使用何種方式對函數(shù)類型的值進行傳遞,當函數(shù)在別處被調用時都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標是誰? 比如: a = 2; RHS:誰是賦值操作的源頭? 比如: conso...

    Raaabbit 評論0 收藏0
  • 你不知道JS讀書筆記閉包在循環(huán)中應用

    摘要:閉包在循環(huán)中的應用延遲函數(shù)的回調會在循環(huán)結束時才執(zhí)行事實上,當定時器運行時即使沒給迭代中執(zhí)行的是多有的回調函數(shù)依然是在循環(huán)結束后才會被執(zhí)行,因此會每次輸出一個出來。 閉包在循環(huán)中的應用 延遲函數(shù)的回調會在循環(huán)結束時才執(zhí)行;事實上,當定時器運行時即使沒給迭代中執(zhí)行的是 setTime(..., 0),多有的回調函數(shù)依然是在循環(huán)結束后才會被執(zhí)行,因此會每次輸出一個6出來。 for(var...

    weapon 評論0 收藏0
  • 讀書筆記-你不知道JavaScript(上)

    摘要:比如程序會被分解為解析語法分析將詞法單元流轉換成一個由元素逐級嵌套所組成的代表了程序語法接口的書,又稱抽象語法樹。代碼生成將抽象語法樹轉換為機器能夠識別的指令。 showImg(https://segmentfault.com/img/remote/1460000009682106?w=640&h=280); 本文首發(fā)在我的個人博客:http://muyunyun.cn/ 《你不知道的...

    jzzlee 評論0 收藏0
  • 你不知道JavaScript》 (上) 閱讀摘要

    摘要:但是如果非全局的變量如果被遮蔽了,無論如何都無法被訪問到。但是如果引擎在代碼中找到,就會完全不做任何優(yōu)化。結構的分句中具有塊級作用域。第四章提升編譯器函數(shù)聲明會被提升,而函數(shù)表達式不會被提升。 本書屬于基礎類書籍,會有比較多的基礎知識,所以這里僅記錄平常不怎么容易注意到的知識點,不會全記,供大家和自己翻閱; 上中下三本的讀書筆記: 《你不知道的JavaScript》 (上) 讀書筆記...

    FingerLiu 評論0 收藏0
  • 你不知道javascript》筆記_作用域與閉包

    摘要:建筑的頂層代表全局作用域。實際的塊級作用域遠不止如此塊級作用域函數(shù)作用域早期盛行的立即執(zhí)行函數(shù)就是為了形成塊級作用域,不污染全局。這便是閉包的特點吧經典面試題下面的代碼輸出內容答案個如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫在前面 這一系列的筆記是在《javascript高級程序設計》讀書筆記系列的升華版本,旨在將零碎...

    galaxy_robot 評論0 收藏0

發(fā)表評論

0條評論

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