摘要:注此讀書(shū)筆記只記錄本人原先不太理解的內(nèi)容經(jīng)過(guò)閱讀你不知道的后的理解。作用域及閉包基礎(chǔ),代碼運(yùn)行的幕后工作者引擎及編譯器。
注:此讀書(shū)筆記只記錄本人原先不太理解的內(nèi)容經(jīng)過(guò)閱讀《你不知道的JS》后的理解。
作用域及閉包基礎(chǔ),JS代碼運(yùn)行的幕后工作者:引擎及編譯器。引擎負(fù)責(zé)JS程序的編譯及執(zhí)行,編譯器負(fù)責(zé)詞法分析和代碼生成。那么作用域就像一個(gè)容器,引擎及編譯器都從這里提取東西。
如 var a = 2 這句簡(jiǎn)單的代碼,聲明一個(gè)變量a,同時(shí)給他賦值為2.
背后運(yùn)行的過(guò)程,編譯器階段:
進(jìn)行詞法分析,將證據(jù)代碼切分成 var, a, =, 2 并逐一分析是否有特定作用,詞法分析的過(guò)程中遇到var a 會(huì)詢問(wèn)作用域在當(dāng)前作用域下是否有a這個(gè)變量,若有,則忽略聲明,繼續(xù)編譯,若無(wú),就要求作用域在當(dāng)前作用域聲明該變量。 而后生成 var a = 2的代碼。
引擎階段:運(yùn)行var a =2 時(shí) 首先訪問(wèn)當(dāng)前作用域是否有a 的變量,有就賦值為2,沒(méi)有就向上查詢。若最終能找到該變量則使用該變量并給它賦值為2,若沒(méi)有就會(huì)拋出一個(gè)異常。
以上就是其背后的運(yùn)行過(guò)程。
塊作用域:
JS語(yǔ)言中似乎是沒(méi)有相對(duì)其他語(yǔ)言相關(guān)的塊作用域的說(shuō)法即{}框定的內(nèi)容不算一個(gè)封閉的作用域,簡(jiǎn)單的例子:
if(true){ var b = "this is in the block" } console.log(b) //this is in the block
顯然b變量在if的語(yǔ)句塊外獲取到了,有如下幾種做法可以限定作用域:
let, IIFE(立即執(zhí)行函數(shù)表達(dá)式)[相當(dāng)于變成了函數(shù)作用域], with
但是用with的時(shí)候需要注意:
簡(jiǎn)單的例子
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里面還是無(wú)法找到a的屬性,同時(shí)全局變量多了個(gè)a變量賦值為了1,這里的原理是在ob2的作用域中LHS尋找a變量,并沒(méi)有找到,則會(huì)創(chuàng)建一個(gè)屬于全局作用域的a變量(非嚴(yán)格模式)[這是硬知識(shí),不知道其這樣實(shí)現(xiàn)的原理是什么]。所以可以在全局環(huán)境中找到a這個(gè)變量,其值為1.原理跟下述的例子類似:
function foo(){ var a = 1 b = a // 這里函數(shù)作用域中b變量是未聲明的,編譯器在進(jìn)行LHS查詢時(shí)由于未能在所有的作用域中找到b‘ //,(非嚴(yán)格模式下)會(huì)自動(dòng)聲明一個(gè)全局變量 something return something }
就像上述的例子,只不過(guò)是換了個(gè)對(duì)象而已,由ob2換成了foo函數(shù)對(duì)象。
閉包
閉包是一個(gè)極其有意思的現(xiàn)象,官方的解釋是這樣的,函數(shù)在其所在的作用域外被調(diào)用,同時(shí)該函數(shù)訪問(wèn)了其作用域中的某些變量,這就形成了一個(gè)閉包。
一個(gè)簡(jiǎn)單的例子:
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()
理論上來(lái)說(shuō),按照作用域的概念,這里應(yīng)該輸出的是上一句的a變量,這里test的函數(shù)無(wú)法訪問(wèn)到closure內(nèi)部的變量的。這就是閉包的作用,我的理解是,他會(huì)將作用域給鎖定,內(nèi)存中原函數(shù)的內(nèi)部的變量并不會(huì)被回收。因此在函數(shù)在其作用域外被調(diào)用,還是能使用其作用域中的變量a.
利用閉包這個(gè)特性,JS還有很多有意思的東西:
下面這個(gè)比較通俗點(diǎn)的例子,廖學(xué)峰大佬的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
預(yù)期的輸出應(yīng)該是1, 4, 9,但實(shí)際的結(jié)果是全都是16,原因就在于JS的for循環(huán)沒(méi)有塊作用域這個(gè)屬性,數(shù)組添加元素的內(nèi)容是一個(gè)函數(shù),而函數(shù)并非立即調(diào)用,他們都共享一個(gè)作用域,而作用域中的變量i只有一個(gè),其最終的結(jié)果就是4,所以所有的輸出都是16。
要解決這個(gè)問(wèn)題,可以用快作用域的方法,let, IIFE將i在迭代時(shí)的作用域鎖定。
function count() { var arr = []; for (var i=1; i<=3; i++) { (function(){ var j = i //利用IIFE時(shí)需要聲明變量接受變量i,否則IIFE中的作用域?yàn)榭眨蛏喜樵?,使? //的仍然是i,而全局共享的i依舊取最終的結(jié)果 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
這里的上下兩個(gè)例子都是閉包的例子,不同的是,第一個(gè)閉包的訪問(wèn)的作用域是函數(shù)count的內(nèi)部作用域共享一個(gè)變量i,取最后的值,而下一個(gè)例子,閉包訪問(wèn)的作用域是一個(gè)IIFE的內(nèi)部作用域,為變量j。而IIFE是類似一個(gè)函數(shù)作用域,j是不會(huì)隨著i變化而變化,這里執(zhí)行了3次IIFE,每次的IIFE對(duì)應(yīng)的變量j是不同的,不會(huì)相互影響,所以達(dá)到了預(yù)期輸出,如果IIFE中作用域?yàn)榭?,那么閉包訪問(wèn)的仍舊是count的作用域,因此依舊達(dá)不到期望的輸出。
同理let也能達(dá)到預(yù)期的輸出
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)結(jié)構(gòu)有個(gè)特殊的地方,for 循環(huán)頭部的 let 聲明還會(huì)有一
個(gè)特殊的行為。這個(gè)行為指出變量在循環(huán)過(guò)程中不止被聲明一次,每次迭代都會(huì)聲明。隨
后的每個(gè)迭代都會(huì)使用上一個(gè)迭代結(jié)束時(shí)的值來(lái)初始化這個(gè)變量。
所以還可以這樣實(shí)現(xiàn),使代碼結(jié)構(gòu)更為明朗:
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
以上就是對(duì)當(dāng)初作用域不太理解的地方如今通過(guò)此書(shū)獲得的認(rèn)識(shí),以上只是個(gè)人的理解,依舊存有疑問(wèn),如為什么在IIFE實(shí)現(xiàn)獨(dú)立作用域的時(shí)候若不聲明var j = i或獲得的結(jié)果依舊不是期望的值,是因?yàn)殚]包在當(dāng)前作用域訪問(wèn)不到i的時(shí)候,訪問(wèn)了上一級(jí)的作用域,同時(shí)將這個(gè)作用域保存在了作用域鏈中嗎?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/90400.html
摘要:的分句會(huì)創(chuàng)建一個(gè)塊作用域,其聲明的變量?jī)H在中有效。而閉包的神奇作用是阻止此事發(fā)生。依然持有對(duì)該作用域的引用,而這個(gè)引用就叫做閉包。當(dāng)然,無(wú)論使用何種方式對(duì)函數(shù)類型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標(biāo)是誰(shuí)? 比如: a = 2; RHS:誰(shuí)是賦值操作的源頭? 比如: conso...
摘要:閉包在循環(huán)中的應(yīng)用延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使沒(méi)給迭代中執(zhí)行的是多有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè)出來(lái)。 閉包在循環(huán)中的應(yīng)用 延遲函數(shù)的回調(diào)會(huì)在循環(huán)結(jié)束時(shí)才執(zhí)行;事實(shí)上,當(dāng)定時(shí)器運(yùn)行時(shí)即使沒(méi)給迭代中執(zhí)行的是 setTime(..., 0),多有的回調(diào)函數(shù)依然是在循環(huán)結(jié)束后才會(huì)被執(zhí)行,因此會(huì)每次輸出一個(gè)6出來(lái)。 for(var...
摘要:比如程序會(huì)被分解為解析語(yǔ)法分析將詞法單元流轉(zhuǎn)換成一個(gè)由元素逐級(jí)嵌套所組成的代表了程序語(yǔ)法接口的書(shū),又稱抽象語(yǔ)法樹(shù)。代碼生成將抽象語(yǔ)法樹(shù)轉(zhuǎn)換為機(jī)器能夠識(shí)別的指令。 showImg(https://segmentfault.com/img/remote/1460000009682106?w=640&h=280); 本文首發(fā)在我的個(gè)人博客:http://muyunyun.cn/ 《你不知道的...
摘要:但是如果非全局的變量如果被遮蔽了,無(wú)論如何都無(wú)法被訪問(wèn)到。但是如果引擎在代碼中找到,就會(huì)完全不做任何優(yōu)化。結(jié)構(gòu)的分句中具有塊級(jí)作用域。第四章提升編譯器函數(shù)聲明會(huì)被提升,而函數(shù)表達(dá)式不會(huì)被提升。 本書(shū)屬于基礎(chǔ)類書(shū)籍,會(huì)有比較多的基礎(chǔ)知識(shí),所以這里僅記錄平常不怎么容易注意到的知識(shí)點(diǎn),不會(huì)全記,供大家和自己翻閱; 上中下三本的讀書(shū)筆記: 《你不知道的JavaScript》 (上) 讀書(shū)筆記...
摘要:建筑的頂層代表全局作用域。實(shí)際的塊級(jí)作用域遠(yuǎn)不止如此塊級(jí)作用域函數(shù)作用域早期盛行的立即執(zhí)行函數(shù)就是為了形成塊級(jí)作用域,不污染全局。這便是閉包的特點(diǎn)吧經(jīng)典面試題下面的代碼輸出內(nèi)容答案?jìng)€(gè)如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫(xiě)在前面 這一系列的筆記是在《javascript高級(jí)程序設(shè)計(jì)》讀書(shū)筆記系列的升華版本,旨在將零碎...
閱讀 2699·2021-11-22 12:05
閱讀 3588·2021-10-14 09:42
閱讀 1821·2021-07-28 00:15
閱讀 2119·2019-08-30 11:08
閱讀 1626·2019-08-29 17:31
閱讀 1055·2019-08-29 16:42
閱讀 2470·2019-08-26 11:55
閱讀 2249·2019-08-26 11:49