摘要:遮蔽效應(yīng)作用域查找會(huì)在找到第一個(gè)匹配的標(biāo)識符時(shí)停止,不會(huì)繼續(xù)往上層作用域查找,這就會(huì)產(chǎn)生遮蔽效應(yīng)。會(huì)發(fā)現(xiàn)每一次輸出的都是為啥勒所有的回調(diào)函數(shù)回在循環(huán)結(jié)束后才會(huì)執(zhí)行事件循環(huán)。
三劍客
編譯,顧名思義,就是源代碼執(zhí)行前會(huì)經(jīng)歷的過程,分三個(gè)步驟,
分詞/詞法分析,將我們寫的代碼字符串分解成多個(gè)詞法單元
解析/語法分析,將詞法單元集合生成抽象語法樹(AST)
代碼生成,抽象語法樹(AST)轉(zhuǎn)換成可執(zhí)行代碼的過程
Tip1:js在語法分析和代碼生成階段有對運(yùn)行性能進(jìn)行優(yōu)化,對冗余元素進(jìn)行優(yōu)化
Tip2:js的編譯過程不是發(fā)生在構(gòu)建之前,而是代碼執(zhí)行之前
理解作用域,首先知道三劍客,分別是
引擎:負(fù)責(zé)整個(gè)代碼編譯及執(zhí)行的過程
編譯器: 負(fù)責(zé)詞法分析、語法分析、代碼生成
作用域:負(fù)責(zé)維護(hù)與收集所有聲明的標(biāo)識符,保證當(dāng)前執(zhí)行代碼對這些標(biāo)識符的訪問權(quán)限
舉例子,加深印象,對于var a = 2,三劍客如何協(xié)同工作,
編譯器進(jìn)行分詞、語法分析,然后要代碼生成時(shí),遇到 var a,問一下當(dāng)前作用域集合“你有沒有這個(gè)名稱的變量呀?”,作用域如果說有,那么忽略聲明,繼續(xù)編譯,如果說沒有,那么就要要求作用域收集一下,并且給它個(gè)名字a,然后編譯器就生成了代碼,引擎準(zhǔn)備來執(zhí)行了,先問當(dāng)前作用域集合,“你這里有a這個(gè)變量嗎?”,有引擎就拿來用,沒有就繼續(xù)找該變量,要么找到,就給它附個(gè)值2,沒有那就給你報(bào)個(gè)錯(cuò)!
作用域嵌套LSH查詢,通俗解釋就是找到所聲明變量,并且對其賦值的行為
RSH查詢,通俗解釋就是查找聲明的變量
當(dāng)一個(gè)塊或是函數(shù)嵌套在另外一個(gè)塊或函數(shù)時(shí),就會(huì)產(chǎn)生作用域嵌套,于是在當(dāng)前作用域找不到某個(gè)變量時(shí),引擎會(huì)往外層嵌套作用域繼續(xù)查找,直達(dá)到最外層作用域(全局作用域)為止,也就是所謂的作用域鏈啦!
詞法作用域 相信有很多人都是搞不懂詞法作用域是什么?所謂的詞法作用域,就是定義在詞法階段(詞法分析)的作用域,由你寫代碼時(shí)將變量和塊作用域?qū)懺谀睦飦頉Q定的。
遮蔽效應(yīng)作用域查找會(huì)在找到第一個(gè)匹配的標(biāo)識符時(shí)停止,不會(huì)繼續(xù)往上層作用域查找,這就會(huì)產(chǎn)生遮蔽效應(yīng)。
欺騙詞法作用域eval函數(shù),修改詞法作用域
with關(guān)鍵字,創(chuàng)建詞法作用域
導(dǎo)致性能下降的原因,前面我們提過,在解析/語法分析、生成代碼階段,我們會(huì)對代碼進(jìn)行優(yōu)化,剔除冗余元素,但是當(dāng)使用eval/with時(shí),所有優(yōu)化變得沒有意義,因?yàn)榇嬖诓豢深A(yù)見性,不知道修改和創(chuàng)建的詞法作用域是什么?所以會(huì)導(dǎo)致性能下降。
函數(shù)聲明、函數(shù)表達(dá)式、匿名函數(shù)表達(dá)式函數(shù)表達(dá)式:function為第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式
匿名函數(shù)表達(dá)式:沒有函數(shù)名,匿名函數(shù)在棧追蹤這種不會(huì)顯示有意義的函數(shù)名,使得調(diào)試?yán)щy
IIFE: 立即執(zhí)行函數(shù)表達(dá)式((function() { ... }())、(function(){ ... })())
let為其聲明的變量隱式地去劫持了所在的塊級作用域,不會(huì)在塊級作用域中進(jìn)行提升【變量提升】
Demo: with關(guān)鍵字為塊級作用域、{...}為塊級作用域,用完即銷毀
const常量,不可修改!
任何聲明在某個(gè)作用域(函數(shù)作用域和塊級作用域)的變量,都是屬于這個(gè)作用域。
每個(gè)作用域都會(huì)進(jìn)行提升操作。
函數(shù)聲明會(huì)被提升,函數(shù)表達(dá)式不會(huì)提升,變量聲明提升的過程中,函數(shù)會(huì)優(yōu)先!
閉包,有權(quán)訪問另外一個(gè)函數(shù)的變量標(biāo)識符的函數(shù),比較常見的一個(gè)閉包問題,就是for循環(huán)。
for(var i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i*1000) }
會(huì)發(fā)現(xiàn)每一次輸出的都是6,為啥勒?所有的回調(diào)函數(shù)回在循環(huán)結(jié)束后才會(huì)執(zhí)行(事件循環(huán))。所以每次都是輸出一個(gè)6來。
解決辦法:
1、IIFE,每個(gè)迭代儲存i的值
for(var i = 1; i <= 5; i++) { (function(i) { var j = i; setTimeout(function() { console.log(j); }, j*1000); })(i) }
2、IIFE,將i入?yún)⑿薷某?b>j
for(var i = 1; i <= 5; i++) { (function(j) { setTimeout(function() { console.log(j); }, j*1000); })(i) }
3、創(chuàng)建閉包的塊作用域
for(var i = 1; i <= 5; i++) { let j = i; setTimeout(function() { console.log(j); }, j*1000); }
4、最優(yōu)
for(let i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i*1000) }
另外一個(gè)閉包常用的場景,就是模塊暴露了,在這里提供一個(gè)現(xiàn)代模塊機(jī)制的實(shí)現(xiàn)方式,大家可以細(xì)細(xì)品嘗,
var MyMoudles = (function Manger() { var modules = {}; function define(name, deps, impl) { for (var i = 0; i < deps.length; i++) { deps[i] = modules[deps[i]]; } modules[name] = impl.apply(impl, deps); } function get(name) { return modules[name]; } })()
調(diào)用Demo:
MyMoudles.define("bar", [], function() { function hello(who) { return "Let me introduce: " + who; } return { hello: hello } }) MyMoudles.define("foo", ["bar"], function(bar) { var hungry = "hippo"; function awesome() { console.log(bar.hello(hungry).toUpperCase()); } return { awesome: awesome } }) var bar = MyMoudles.get("bar"); var foo = MyMoudles.get("foo"); console.log(bar.hello("hippo")); // Let me introduce: hippo foo.awesome();未來模塊機(jī)制
ES6提供了全新的模塊機(jī)制,基于函數(shù)的模塊(如上述現(xiàn)代模塊機(jī)制)并不是一個(gè)能被靜態(tài)識別的模式(編譯器無法識別),它們的API語義只有等到代碼運(yùn)行時(shí)才會(huì)考慮進(jìn)來,而ES6模塊就是一個(gè)能被靜態(tài)識別的模式,就是說API在編譯階段就會(huì)檢查API成員是否存在。
原文地址
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106384.html
摘要:詞法熟悉語法的開發(fā)者,箭頭函數(shù)在涉及綁定時(shí)的行為和普通函數(shù)的行為完全不一致。被忽略的作為的綁定對象傳入,使用的是默認(rèn)綁定規(guī)則。使用內(nèi)置遍歷數(shù)組返回迭代器函數(shù)普通對象不含有,無法使用,可以進(jìn)行改造,個(gè)人博客地址 this詞法 熟悉ES6語法的開發(fā)者,箭頭函數(shù)在涉及this綁定時(shí)的行為和普通函數(shù)的行為完全不一致。跟普通this綁定規(guī)則不一樣,它使用了當(dāng)前的詞法作用域覆蓋了this本來的值。...
摘要:最近剛剛看完了你不知道的上卷,對有了更進(jìn)一步的了解。你不知道的上卷由兩部分組成,第一部分是作用域和閉包,第二部分是和對象原型。附錄詞法這一章并沒有說明機(jī)制,只是介紹了中的箭頭函數(shù)引入的行為詞法。第章混合對象類類理論類的機(jī)制類的繼承混入。 最近剛剛看完了《你不知道的 JavaScript》上卷,對 JavaScript 有了更進(jìn)一步的了解。 《你不知道的 JavaScript》上卷由兩部...
摘要:的分句會(huì)創(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...
摘要:如果是聲明中的第一個(gè)詞,那么就是一個(gè)函數(shù)聲明,否則就是一個(gè)函數(shù)表達(dá)式。給函數(shù)表達(dá)式指定一個(gè)函數(shù)名可以有效的解決以上問題。始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐。也有開發(fā)者干脆關(guān)閉了靜態(tài)檢查工具對重復(fù)變量名的檢查。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 Ja...
摘要:詞法作用域的查找規(guī)則是閉包的一部分。因此的確同閉包息息相關(guān),即使本身并不會(huì)真的使用閉包。而上面的創(chuàng)建一個(gè)閉包,本質(zhì)上這是將一個(gè)塊轉(zhuǎn)換成一個(gè)可以被關(guān)閉的作用域。結(jié)合塊級作用域與閉包模塊這個(gè)模式在中被稱為模塊。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復(fù)雜微妙技術(shù)的語言,即使是經(jīng)驗(yàn)豐富的 Jav...
閱讀 2277·2023-04-25 14:50
閱讀 1294·2021-10-13 09:50
閱讀 1876·2019-08-30 15:56
閱讀 1856·2019-08-29 15:29
閱讀 2897·2019-08-29 15:27
閱讀 3588·2019-08-29 15:14
閱讀 1209·2019-08-29 13:01
閱讀 3311·2019-08-26 14:06