摘要:比如程序會被分解為解析語法分析將詞法單元流轉(zhuǎn)換成一個(gè)由元素逐級嵌套所組成的代表了程序語法接口的書,又稱抽象語法樹。代碼生成將抽象語法樹轉(zhuǎn)換為機(jī)器能夠識別的指令。
本文首發(fā)在我的個(gè)人博客:http://muyunyun.cn/
《你不知道的JavaScript》系列叢書給出了很多顛覆以往對JavaScript認(rèn)知的點(diǎn), 讀完上卷,受益匪淺,于是對其精華的知識點(diǎn)進(jìn)行了梳理。
什么是作用域作用域是一套規(guī)則,用于確定在何處以及如何查找變量。
編譯原理JavaScript是一門編譯語言。在傳統(tǒng)編譯語言的流程中,程序中一段源代碼在執(zhí)行之前會經(jīng)歷三個(gè)步驟,統(tǒng)稱為“編譯”。
分詞/詞法分析
將字符串分解成有意義的代碼塊,代碼塊又稱詞法單元。比如程序var a = 2;會被分解為var、a、=、2、;
解析/語法分析
將詞法單元流轉(zhuǎn)換成一個(gè)由元素逐級嵌套所組成的代表了程序語法接口的書,又稱“抽象語法樹”。
代碼生成
將抽象語法樹轉(zhuǎn)換為機(jī)器能夠識別的指令。
作用域 分別與編譯器、引擎進(jìn)行配合完成代碼的解析
引擎執(zhí)行時(shí)會與作用域進(jìn)行交流,確定RHS與LHS查找具體變量,如果查找不到會拋出異常。
編譯器負(fù)責(zé)語法分析以及生成代碼。
作用域負(fù)責(zé)收集并維護(hù)所有變量組成的一系列查詢,并確定當(dāng)前執(zhí)行的代碼對這些變量的訪問權(quán)限。
對于 var a = 2 這條語句,首先編譯器會將其分為兩部分,一部分是 var a,一部分是 a = 2。編譯器會在編譯期間執(zhí)行 var a,然后到作用域中去查找 a 變量,如果 a 變量在作用域中還沒有聲明,那么就在作用域中聲明 a 變量,如果 a 變量已經(jīng)存在,那就忽略 var a 語句。然后編譯器會為 a = 2 這條語句生成執(zhí)行代碼,以供引擎執(zhí)行該賦值操作。所以我們平時(shí)所提到的變量提升,無非就是利用這個(gè)先聲明后賦值的原理而已!
異常對于 var a = 10 這條賦值語句,實(shí)際上是為了查找變量 a, 并且將 10 這個(gè)數(shù)值賦予它,這就是 LHS 查詢。 對于 console.log(a) 這條語句,實(shí)際上是為了查找 a 的值并將其打印出來,這是 RHS 查詢。
為什么區(qū)分 LHS 和 RHS 是一件重要的事情?
在非嚴(yán)格模式下,LHS 調(diào)用查找不到變量時(shí)會創(chuàng)建一個(gè)全局變量,RHS 查找不到變量時(shí)會拋出 ReferenceError。 在嚴(yán)格模式下,LHS 和 RHS 查找不到變量時(shí)都會拋出 ReferenceError。
作用域共有兩種主要的工作模型。第一種是最為普遍的,被大多數(shù)編程語言所采用的詞法作用域( JavaScript 中的作用域就是詞法作用域)。另外一種是動態(tài)作用域,仍有一些編程語言在使用(比如Bash腳本、Perl中的一些模式等)。
詞法作用域詞法作用域是一套關(guān)于引擎如何尋找變量以及會在何處找到變量的規(guī)則。詞法作用域最重要的特征是它的定義過程發(fā)生在代碼的書寫階段(假設(shè)沒有使用 eval() 或 with )。來看示例代碼:
function foo() { console.log(a); // 2 } function bar() { var a = 3; foo(); } var a = 2; bar()
詞法作用域讓foo()中的a通過RHS引用到了全局作用域中的a,因此會輸出2。
動態(tài)作用域而動態(tài)作用域只關(guān)心它們從何處調(diào)用。換句話說,作用域鏈?zhǔn)腔谡{(diào)用棧的,而不是代碼中的作用域嵌套。因此,如果 JavaScript 具有動態(tài)作用域,理論上,下面代碼中的 foo() 在執(zhí)行時(shí)將會輸出3。
function foo() { console.log(a); // 3 } function bar() { var a = 3; foo(); } var a = 2; bar()函數(shù)作用域 匿名與具名
對于函數(shù)表達(dá)式一個(gè)最熟悉的場景可能就是回調(diào)函數(shù)了,比如
setTimeout( function() { console.log("I waited 1 second!") }, 1000 )
這叫作匿名函數(shù)表達(dá)式。函數(shù)表達(dá)式可以匿名,而函數(shù)聲明則不可以省略函數(shù)名。匿名函數(shù)表達(dá)式書寫起來簡單快捷,很多庫和工具也傾向鼓勵(lì)使用這種風(fēng)格的代碼。但它也有幾個(gè)缺點(diǎn)需要考慮。
匿名函數(shù)在棧追蹤中不會顯示出有意義的函數(shù)名,使得調(diào)試很困難。
如果沒有函數(shù)名,當(dāng)函數(shù)需要引用自身時(shí)只能使用已經(jīng)過期的 arguments.callee 引用,比如在遞歸中。另一個(gè)函數(shù)需要引用自身的例子,是在事件觸發(fā)后事件監(jiān)聽器需要解綁自身。
匿名函數(shù)省略了對于代碼可讀性 / 可理解性很重要的函數(shù)名。一個(gè)描述性的名稱可以讓代碼不言自明。
始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐:
setTimeout( function timeoutHandler() { // 我有名字了 console.log("I waited 1 second!") }, 1000 )提升 先有聲明還是先有賦值
考慮以下代碼:
a = 2; var a; console.log(a); // 2
考慮另外一段代碼
console.log(a); // undefined var a = 2;
我們習(xí)慣將 var a = 2; 看作一個(gè)聲明,而實(shí)際上 JavaScript 引擎并不這么認(rèn)為。它將 var a 和 a = 2 當(dāng)作兩個(gè)多帶帶的聲明,第一個(gè)是編譯階段的任務(wù),而第二個(gè)是執(zhí)行階段的任務(wù)。
這意味著無論作用域中的聲明出現(xiàn)在什么地方,都將在代碼本身被執(zhí)行前首先進(jìn)行處理。可以將這個(gè)過程形象地想象成所有的聲明(變量和函數(shù))都會被“移動”到各自作用域的最頂端,這個(gè)過程稱為提升。
可以看出,先有聲明后有賦值。
再來看以下代碼:
foo(); // TypeError bar(); // ReferenceError var foo = function bar() { // ... };
這個(gè)代碼片段經(jīng)過提升后,實(shí)際上會被理解為以下形式:
var foo; foo(); // TypeError bar(); // ReferenceError foo = function() { var bar = ...self... // ... };
這段程序中的變量標(biāo)識符 foo() 被提升并分配給全局作用域,因此 foo() 不會導(dǎo)致 ReferenceError。但是 foo 此時(shí)并沒有賦值(如果它是一個(gè)函數(shù)聲明而不是函數(shù)表達(dá)式就會賦值)。foo()由于對 undefined 值進(jìn)行函數(shù)調(diào)用而導(dǎo)致非法操作,因此拋出 TypeError 異常。另外即時(shí)是具名的函數(shù)表達(dá)式,名稱標(biāo)識符(這里是 bar )在賦值之前也無法在所在作用域中使用。
閉包之前寫過關(guān)于閉包的一篇文章深入淺出JavaScript之閉包(Closure)
循環(huán)和閉包要說明閉包,for 循環(huán)是最常見的例子。
for (var i = 1; i <= 5; i++) { setTimeout( function timer() { console.log(i); }, i*1000 ) }
正常情況下,我們對這段代碼行為的預(yù)期是分別輸出數(shù)字 1~5,每秒一次,每次一個(gè)。但實(shí)際上,這段代碼在運(yùn)行時(shí)會以每秒一次的頻率輸出五次6。
它的缺陷在于:根據(jù)作用域的工作原理,盡管循環(huán)中的五個(gè)函數(shù)是在各個(gè)迭代中分別定義的,但是它們都被封閉在一個(gè)共享的全局作用域中,因此實(shí)際上只有一個(gè)i。因此我們需要更多的閉包作用域。我們知道IIFE會通過聲明并立即執(zhí)行一個(gè)函數(shù)來創(chuàng)建作用域,我們來進(jìn)行改進(jìn):
for (var i = 1; i <= 5; i++) { (function() { var j = i; setTimeout( function timer() { console.log(j); }, j*1000 ) })(); }
還可以對這段代碼進(jìn)行一些改進(jìn):
for (var i = 1; i <= 5; i++) { (function(j) { setTimeout( function timer() { console.log(j); }, j*1000 ) })(i); }
在迭代內(nèi)使用 IIFE 會為每個(gè)迭代都生成一個(gè)新的作用域,使得延遲函數(shù)的回調(diào)可以將新的作用域封閉在每個(gè)迭代內(nèi)部,每個(gè)迭代中都會含有一個(gè)具有正確值的變量供我們訪問。
重返塊作用域我們使用 IIFE 在每次迭代時(shí)都創(chuàng)建一個(gè)新的作用域。換句話說,每次迭代我們都需要一個(gè)塊作用域。我們知道 let 聲明可以用來劫持塊作用域,那我們可以進(jìn)行這樣改:
for (var i = 1; i <= 5; i++) { let j = i; setTimeout( function timer() { console.log(j); }, j*1000 ) }
本質(zhì)上這是將一個(gè)塊轉(zhuǎn)換成一個(gè)可以被關(guān)閉的作用域。
此外,for循環(huán)頭部的 let 聲明還會有一個(gè)特殊行為。這個(gè)行為指出每個(gè)迭代都會使用上一個(gè)迭代結(jié)束時(shí)的值來初始化這個(gè)變量。
for (let i = 1; i <= 5; i++) { setTimeout( function timer() { console.log(i); }, i*1000 ) }this全面解析
之前寫過一篇深入淺出JavaScript之this。我們知道this是在運(yùn)行時(shí)進(jìn)行綁定的,并不是在編寫時(shí)綁定,它的上下文取決于函數(shù)調(diào)用時(shí)的各種條件。this的綁定和函數(shù)聲明的位置沒有任何關(guān)系,只取決于函數(shù)的調(diào)用方式。
this詞法來看下面這段代碼的問題:
var obj = { id: "awesome", cool: function coolFn() { console.log(this.id); } }; var id = "not awesome"; obj.cool(); // awesome setTimeout( obj.cool, 100); // not awesome
obj.cool() 與 setTimeout( obj.cool, 100 ) 輸出結(jié)果不一樣的原因在于 cool() 函數(shù)丟失了同 this 之間的綁定。解決方法最常用的是 var self = this;
var obj = { count: 0, cool: function coolFn() { var self = this; if (self.count < 1) { setTimeout( function timer(){ self.count++; console.log("awesome?"); }, 100) } } } obj.cool(); // awesome?
這里用到的知識點(diǎn)是我們非常熟悉的詞法作用域。self 只是一個(gè)可以通過詞法作用域和閉包進(jìn)行引用的標(biāo)識符,不關(guān)心 this 綁定的過程中發(fā)生了什么。
ES6 中的箭頭函數(shù)引人了一個(gè)叫作 this 詞法的行為:
var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout( () => { this.count++; console.log("awesome?"); }, 100) } } } obj.cool(); // awesome?
箭頭函數(shù)棄用了所有普通 this 綁定規(guī)則,取而代之的是用當(dāng)前的詞法作用域覆蓋了 this 本來的值。因此,這個(gè)代碼片段中的箭頭函數(shù)只是"繼承"了 cool() 函數(shù)的 this 綁定。
但是箭頭函數(shù)的缺點(diǎn)就是因?yàn)槠涫悄涿模衔囊呀榻B過具名函數(shù)比匿名函數(shù)更可取的原因。而且箭頭函數(shù)將程序員們經(jīng)常犯的一個(gè)錯(cuò)誤給標(biāo)準(zhǔn)化了:混淆了 this 綁定規(guī)則和詞法作用域規(guī)則。
箭頭函數(shù)不僅僅意味著可以少寫代碼。本書的作者認(rèn)為使用 bind() 是更靠得住的方式。
var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout( () => { this.count++; console.log("more awesome"); }.bind( this ), 100) } } } obj.cool(); // more awesome綁定規(guī)則
函數(shù)在執(zhí)行的過程中,可以根據(jù)下面這4條綁定規(guī)則來判斷 this 綁定到哪。
默認(rèn)綁定
獨(dú)立函數(shù)調(diào)用
隱式綁定
當(dāng)函數(shù)引用有上下文對象時(shí),隱式綁定規(guī)則會把函數(shù)調(diào)用中的 this 綁定到這個(gè)上下文對象
顯示綁定
call/apply
bind(本質(zhì)是對call/apply函數(shù)的封裝 fn.apply( obj, arguments ))
第三方庫的許多函數(shù)都提供了一個(gè)可選的參數(shù)(上下文),其作用和 bind() 一樣,確保回調(diào)函數(shù)使用指定的 this
new 綁定
JavaScript 中的 new 機(jī)制實(shí)際上和面向類的語言完全不同
實(shí)際上并不存在所謂的“構(gòu)造函數(shù)”,只有對于函數(shù)的“構(gòu)造調(diào)用”
書中對4條綁定規(guī)則的優(yōu)先級進(jìn)行了驗(yàn)證,得出以下的順序優(yōu)先級:
函數(shù)是否在 new 中調(diào)用(new 綁定)?如果是的話 this 綁定的是新創(chuàng)建的對象。
函數(shù)是否通過 call、apply(顯式綁定)或者硬綁定(bind)調(diào)用?如果是的話,this 綁定的是指定對象。
函數(shù)是否在某個(gè)上下文對象中調(diào)用(隱式綁定)?如果是的話,this 綁定的是那個(gè)上下文對象。
如果都不是的話,使用默認(rèn)綁定。在嚴(yán)格模式下,綁定到 undefined,否則綁定到全局對象。
被忽略的 this如果你把 null 或者 undefined 作為 this 的綁定對象傳入 call、apply 或者 bind,這些值在調(diào)用時(shí)會被忽略,實(shí)際應(yīng)用的是默認(rèn)規(guī)則。
什么時(shí)候會傳入 null/undefined 呢?一種非常常見的做法是用 apply(..) 來“展開”一個(gè)數(shù)組,并當(dāng)作參數(shù)傳入一個(gè)函數(shù)。類似地,bind(..) 可以對參數(shù)進(jìn)行柯里化(預(yù)先設(shè)置一些參數(shù)),如下代碼:
function foo(a, b) { console.log( "a:" + a + ", b:" + b ); } // 把數(shù)組"展開"成參數(shù) foo.apply(null, [2, 3]); // a:2, b:3 // 使用 bind(..) 進(jìn)行柯里化 var bar = foo.bind( null, 2); bar(3); // a:2, b:3
其中 ES6 中,可以用 ... 操作符代替 apply(..) 來“展開”數(shù)組,但是 ES6 中沒有柯里化的相關(guān)語法,因此還是需要使用 bind(..)。
使用 null 來忽略 this 綁定可能產(chǎn)生一些副作用。如果某個(gè)函數(shù)(比如第三庫中的某個(gè)函數(shù))確實(shí)使用了 this ,默認(rèn)綁定規(guī)則會把 this 綁定到全局對象,這將導(dǎo)致不可預(yù)計(jì)的后果。更安全的做法是傳入一個(gè)特殊的對象,一個(gè) “DMZ” 對象,一個(gè)空的非委托對象,即 Object.create(null)。
function foo(a, b) { console.log( "a:" + a + ", b:" + b ); } var ? = Object.create(null); // 把數(shù)組"展開"成參數(shù) foo.apply( ?, [2, 3]); // a:2, b:3 // 使用 bind(..) 進(jìn)行柯里化 var bar = foo.bind( ?, 2); bar(3); // a:2, b:3對象
JavaScript中的對象有字面形式(比如var a = { .. })和構(gòu)造形式(比如var a = new Array(..))。字面形式更常用,不過有時(shí)候構(gòu)造形式可以提供更多選擇。
作者認(rèn)為“JavaScript中萬物都是對象”的觀點(diǎn)是不對的。因?yàn)閷ο笾皇?6 個(gè)基礎(chǔ)類型( string、number、boolean、null、undefined、object )之一。對象有包括 function 在內(nèi)的子對象,不同子類型具有不同的行為,比如內(nèi)部標(biāo)簽 [object Array] 表示這是對象的子類型數(shù)組。
復(fù)制對象思考一下這個(gè)對象:
function anotherFunction() { /*..*/ } var anotherObject = { c: true }; var anotherArray = []; var myObject = { a: 2, b: anotherObject, // 引用,不是復(fù)本! c: anotherArray, // 另一個(gè)引用! d: anotherFunction }; anotherArray.push( myObject )
如何準(zhǔn)確地表示 myObject 的復(fù)制呢?
這里有一個(gè)知識點(diǎn)。
淺復(fù)制。復(fù)制出的新對象中 a 的值會復(fù)制舊對象中 a 的值,也就是 2,但是新對象中 b、c、d 三個(gè)屬性其實(shí)只是三個(gè)引用。
深復(fù)制。除了復(fù)制 myObject 以外還會復(fù)制 anotherArray。這時(shí)問題就來了,anotherArray 引用了 myObject, 所以又需要復(fù)制 myObject,這樣就會由于循環(huán)引用導(dǎo)致死循環(huán)。
對于 JSON 安全的對象(就是能用 JSON.stringify 序列號的字符串)來說,有一種巧妙的復(fù)制方法:
var newObj = JSON.parse( JSON.stringify(someObj) )
我認(rèn)為這種方法就是深復(fù)制。相比于深復(fù)制,淺復(fù)制非常易懂并且問題要少得多,ES6 定義了 Object.assign(..) 方法來實(shí)現(xiàn)淺復(fù)制。 Object.assign(..) 方法的第一個(gè)參數(shù)是目標(biāo)對象,之后還可以跟一個(gè)或多個(gè)源對象。它會遍歷一個(gè)或多個(gè)源對象的所有可枚舉的自由鍵并把它們復(fù)制到目標(biāo)對象,最后返回目標(biāo)對象,就像這樣:
var newObj = Object.assign( {}, myObject ); newObj.a; // 2 newObj.b === anotherObject; // true newObj.c === anotherArray; // true newObj.d === anotherFunction; // true類
JavaScript 有一些近似類的語法元素(比如 new 和 instanceof), 后來的 ES6 中新增了一些如 class 的關(guān)鍵字。但是 JavaScript 實(shí)際上并沒有類。類是一種設(shè)計(jì)模式,JavaScript 的機(jī)制其實(shí)和類完全不同。
類的繼承(委托)其實(shí)就是復(fù)制,但和其他語言中類的表現(xiàn)不同(其他語言類表現(xiàn)出來的都是復(fù)制行為),JavaScript 中的多態(tài)(在繼承鏈中不同層次名稱相同,但是功能不同的函數(shù))并不表示子類和父類有關(guān)聯(lián),子類得到的只是父類的一份復(fù)本。
JavaScript 通過顯示混入和隱式混入 call() 來模擬其他語言類的表現(xiàn)。此外,顯示混入實(shí)際上無法完全模擬類的復(fù)制行為,因?yàn)閷ο?和函數(shù)!別忘了函數(shù)也是對象)只能復(fù)制引用,無法復(fù)制被引用的對象或者函數(shù)本身。
檢查“類”關(guān)系思考下面的代碼:
function Foo() { // ... } Foo.prototype.blah = ...; var a = new Foo();
我們?nèi)绾握页?a 的“祖先”(委托關(guān)系)呢?
方法一:a instanceof Foo; // true (對象 instanceof 函數(shù))
方法二: Foo.prototype.isPrototypeOf(a); // true (對象 isPrototypeOf 對象)
方法三: Object.getPrototypeOf(a) === Foo.prototype; // true (Object.getPrototypeOf() 可以獲取一個(gè)對象的 [[Prototype]]) 鏈;
方法四: a.__proto__ == Foo.prototype; // true
構(gòu)造函數(shù)函數(shù)不是構(gòu)造函數(shù),而是當(dāng)且僅當(dāng)使用 new 時(shí),函數(shù)調(diào)用會變成“構(gòu)造函數(shù)調(diào)用”。
使用 new 會在 prototype 生成一個(gè) constructor 屬性,指向構(gòu)造調(diào)用的函數(shù)。
constructor 并不表示被構(gòu)造,而且 constructor 屬性并不是一個(gè)不可變屬性,它是不可枚舉的,但它是可以被修改的。
對象關(guān)聯(lián)來看下面的代碼:
var foo = { something: function() { console.log("Tell me something good..."); } }; var bar = Object.create(foo); bar.something(); // Tell me something good...
Object.create(..)會創(chuàng)建一個(gè)新對象 (bar) 并把它關(guān)聯(lián)到我們指定的對象 (foo),這樣我們就可以充分發(fā)揮 [[Prototype]] 機(jī)制的為例(委托)并且避免不必要的麻煩 (比如使用 new 的構(gòu)造函數(shù)調(diào)用會生成 .prototype 和 .constructor 引用)。
Object.create(null) 會創(chuàng)建一個(gè)擁有空鏈接的對象,這個(gè)對象無法進(jìn)行委托。由于這個(gè)對象沒有原型鏈,所以 instanceof 操作符無法進(jìn)行判斷,因此總是會返回 false 。這些特殊的空對象通常被稱作“字典”,它們完全不會受到原型鏈的干擾,因此非常適合用來存儲數(shù)據(jù)。
我們并不需要類來創(chuàng)建兩個(gè)對象之間的關(guān)系,只需要通過委托來關(guān)聯(lián)對象就足夠了。而Object.create(..)不包含任何“類的詭計(jì)”,所以它可以完美地創(chuàng)建我們想要的關(guān)聯(lián)關(guān)系。
此書的第二章第6部分就把面對類和繼承和行為委托兩種設(shè)計(jì)模式進(jìn)行了對比,我們可以看到行為委托是一種更加簡潔的設(shè)計(jì)模式,在這種設(shè)計(jì)模式中能感受到Object.create()的強(qiáng)大。
ES6中的Class來看一段 ES6中Class 的例子
class Widget { constructor(width, height) { this.width = width || 50; this.height = height || 50; this.$elem = null; } render($where){ if (this.$elem) { this.$elem.css({ width: this.width + "px", height: this.height + "px" }).appendTo($where); } } } class Button extends Widget { constructor(width, height, label) { super(width, height); this.label = label || "Default"; this.$elem = $("
除了語法更好看之外,ES6還有以下優(yōu)點(diǎn)
基本上不再引用雜亂的 .prototype 了。
Button 聲明時(shí)直接 “繼承” 了 Widget。
可以通過 super(..)來實(shí)現(xiàn)相對多態(tài),這樣任何方法都可以引用原型鏈上層的同名方法。
class 字面語法不能聲明屬性(只能聲明方法)。這是一種限制,但是它會排除掉許多不好的情況。
可以通過 extends 很自然地?cái)U(kuò)展對象(子)類型。
但是 class 就是完美的嗎?在傳統(tǒng)面向類的語言中,類定義之后就不會進(jìn)行修改,所以類的設(shè)計(jì)模式就不支持修改。但JavaScript 最強(qiáng)大的特性之一就是它的動態(tài)性,在使用 class 的有些時(shí)候還是會用到 .prototype 以及碰到 super (期望動態(tài)綁定然而靜態(tài)綁定) 的問題,class 基本上都沒有提供解決方案。
這也是本書作者希望我們思考的問題。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/83370.html
摘要:本書屬于基礎(chǔ)類書籍,會有比較多的基礎(chǔ)知識,所以這里僅記錄平常不怎么容易注意到的知識點(diǎn),不會全記,供大家和自己翻閱不錯(cuò),下冊的知識點(diǎn)就這么少,非常不推介看下冊上中下三本的讀書筆記你不知道的上讀書筆記你不知道的中讀書筆記你不知道的下讀書筆記第三 本書屬于基礎(chǔ)類書籍,會有比較多的基礎(chǔ)知識,所以這里僅記錄平常不怎么容易注意到的知識點(diǎn),不會全記,供大家和自己翻閱; 不錯(cuò),下冊的知識點(diǎn)就這么少,非...
摘要:的分句會創(chuàng)建一個(gè)塊作用域,其聲明的變量僅在中有效。而閉包的神奇作用是阻止此事發(fā)生。依然持有對該作用域的引用,而這個(gè)引用就叫做閉包。當(dāng)然,無論使用何種方式對函數(shù)類型的值進(jìn)行傳遞,當(dāng)函數(shù)在別處被調(diào)用時(shí)都可以觀察到閉包。 date: 16.12.8 Thursday 第一章 作用域是什么 LHS:賦值操作的目標(biāo)是誰? 比如: a = 2; RHS:誰是賦值操作的源頭? 比如: conso...
摘要:但是如果非全局的變量如果被遮蔽了,無論如何都無法被訪問到。但是如果引擎在代碼中找到,就會完全不做任何優(yōu)化。結(jié)構(gòu)的分句中具有塊級作用域。第四章提升編譯器函數(shù)聲明會被提升,而函數(shù)表達(dá)式不會被提升。 本書屬于基礎(chǔ)類書籍,會有比較多的基礎(chǔ)知識,所以這里僅記錄平常不怎么容易注意到的知識點(diǎn),不會全記,供大家和自己翻閱; 上中下三本的讀書筆記: 《你不知道的JavaScript》 (上) 讀書筆記...
摘要:有種內(nèi)置類型,分別是除對象之外,其他統(tǒng)稱為基本類型。另一個(gè)需要注意的是數(shù)組確切地說,數(shù)組也是的一個(gè)子類型我們可以通過下面的方法檢查變量是不是數(shù)組處理未聲明的變量時(shí),會返回這是因?yàn)橛幸粋€(gè)特殊的安全防范機(jī)制。 js有7種內(nèi)置類型,分別是undefined null boolean string number symbol object除對象之 Object 外,其他統(tǒng)稱為基本類型。符號 ...
摘要:異步請求線程在在連接后是通過瀏覽器新開一個(gè)線程請求將檢測到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)再放入事件循環(huán)隊(duì)列中。 基礎(chǔ):瀏覽器 -- 多進(jìn)程,每個(gè)tab頁獨(dú)立一個(gè)瀏覽器渲染進(jìn)程(瀏覽器內(nèi)核) 每個(gè)瀏覽器渲染進(jìn)程是多線程的,主要包括:GUI渲染線程 JS引擎線程 也稱為JS內(nèi)核,負(fù)責(zé)處理Javascript腳本程序。(例如V8引擎) JS引擎線程負(fù)...
閱讀 2733·2023-04-25 14:21
閱讀 1167·2021-11-23 09:51
閱讀 4001·2021-09-22 15:43
閱讀 606·2019-08-30 15:55
閱讀 1550·2019-08-29 11:28
閱讀 2439·2019-08-26 11:44
閱讀 1675·2019-08-23 18:15
閱讀 2875·2019-08-23 16:42