摘要:首先變量對于一個程序來說是一個很重要的角色那么問題來了這些變量存在哪里程序用到的時候如何找到變量呢所以需要一套規(guī)則來存儲變量方便之后再找到這套規(guī)則就成為作用域是一門編譯語言對于來說大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微妙的時間內(nèi)對于參與到一
首先,變量對于一個程序來說是一個很重要的角色, 那么問題來了 這些變量存在哪里,程序用到的時候如何找到變量呢?
所以需要一套規(guī)則來存儲變量方便之后再找到 這套規(guī)則就成為作用域.
js是一門編譯語言,對于js來說 大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微妙的時間內(nèi),
對于參與到一個程序 : var a = 2; 處理的時候,
引擎 : 從頭到尾負(fù)責(zé)整個js程序的編譯和執(zhí)行過程
編譯器: 負(fù)責(zé)的是語法分析和代碼生成
作用域: 負(fù)責(zé)收集并維護(hù)所有聲明的變量組成的一系列查詢,并實(shí)施一套非常嚴(yán)格的規(guī)則,最后確定當(dāng)前執(zhí)行的代碼對于這些變量的訪問權(quán)限
對于 var a = 2; 來說
首先編譯器會先詢問作用域是否已經(jīng)有一個該名稱a的變量存在同一個作用域的集合中 ,如果有了 編譯器就會忽略否則 則會要求作用域在當(dāng)前作用域集合中聲明一個新的變量 名稱為a;
然后編譯器生成代碼來進(jìn)行a=2 的復(fù)賦值操作, 到了引擎工作的時候了 首先會先在作用域中查找a這個變量,如果有,引擎就會對變量a進(jìn)行LHS查詢(LHS查詢是試圖找到變量本身然后進(jìn)行賦值操作(等號左邊left) | RHS查詢是指非等號左邊的查詢 即要取到某個變量的指),對a進(jìn)行賦值操作.如果找不到這個變量就會拋出異常了.
(
這里補(bǔ)充一下引擎的LHS查詢和RHS查詢 : 例如 var b=a; return a+b 這里的 LHS查詢有: b=...
RHS查詢有 =a; a...; b...; 對于變量沒有聲明的情況下,兩個查詢的行為是不一樣的, 對于 b=a
時,對b=進(jìn)行LHS查詢 會一直向上作用域查找該變量 如果在最頂層還沒有找到 那么就會在最頂層創(chuàng)建一個b變量(非嚴(yán)格模式下) ,
而對于=a進(jìn)行RHS查詢時,如果在所有作用域中找不到該變量就會拋出ReferenceError異常.
)
了解了js工作機(jī)制之后,那么對于 "聲明提升" 就會恍然大悟了,
例如:
foo(); function foo(){ console.log(a) //undefined var a= 2; } //首先編譯器先定義聲明foo 和 作用域中的a 然后才會到引擎執(zhí)行代碼進(jìn)行賦值操作,所以才有了以上的輸出, 這種效果即是"聲明提升"
意味著無論作用域中的聲明出現(xiàn)在什么地方,都會在代碼執(zhí)行前(編譯階段)進(jìn)行首先處理,想象成所有的聲明都會被移動到各自作用域的頂端,===>即成為 我們所說的提升.
對于作用域有了深入理解了之后 接下來說一說作用域閉包
網(wǎng)上對于閉包的定義太多了 總的來說 閉包實(shí)際上就是:
無論通過任何手段將內(nèi)部函數(shù)傳遞到所在的此法作用域以外,它都會持有對原始定義作用域的引用,無論在任何處執(zhí)行這個函數(shù)都會使用閉包,
所以說平時寫的代碼中
很多就是閉包只是自己沒有發(fā)現(xiàn)而已,本質(zhì)上無論何時何地,如果將(訪問他們各自此法作用域的)函數(shù)當(dāng)做第一級的值類型并到處傳遞,你就會看到閉包在這些函數(shù)中的應(yīng)用了.
在定時器,事件監(jiān)聽器,ajax請求,web workers..等任務(wù)中只要用了回調(diào)函數(shù) 其實(shí)就是在使用閉包!
幾個閉包的例子: 1. function foo(){ var a =2; function baz(){ consolo.log( a ) // 2 } bar(baz) } function bar(fn){ fn(); } foo(); //把內(nèi)部函數(shù)baz 傳遞給bar 當(dāng)調(diào)用這個內(nèi)部函數(shù)時,它涵蓋的foo()內(nèi)部作用域的閉包就可以觀察到了,因?yàn)槟茉L問到a. 2. function wait(message){ setTimeout(function timer(){ console.log(message) },1000) } wait("hello,closure") 3. function setBot(name,selector){ $(selector).click(function activator(){ console.log("activating:" + name) }) } setBot("test1","#bot1"); setBot("test2","#bot2");
ok,還有其他的代碼模式利用了閉包的作用,表面上看似乎和回調(diào)函數(shù)無關(guān),這就是 模塊
對于模塊模式,需要的兩個必要條件就是
必須有外部的封閉函數(shù),該函數(shù)必須被至少調(diào)用一次,(每次調(diào)用都會創(chuàng)建一個新的模塊實(shí)例)
封閉函數(shù)必須返回至少一個內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)!
舉個例子(單例模式) var foo = (function Moudle(){ var something = "oye"; var other = [1,2,3]; function dosomething(){ console.log(something); } function doother(other){ console.log(other.join("!")); } return { doSomething : dosomething, doother:doother } })(); foo.dosomething(); //oye foo.doother(); // 1!2!3 //模塊也是普通的函數(shù) 所以也可以接受參數(shù).
模塊模式的另一個簡單強(qiáng)大的用法是命名將要作為公共API返回的對象:
var foo = (function moudel(id){ function change(){ //修改公共API public.one = two; } function one(){ console.log(id); } function two(){ console.log(id.toUpperCase()); } var API = { change:change, identify:one } return API; })("foo moudel") foo.identify(); //foo moudel; foo.change(); foo.identify(); // FOO MOUDEL; //通過在模塊實(shí)例的內(nèi)部保留對公共API對象的內(nèi)部引用,可以從內(nèi)部對實(shí)例進(jìn)行修改,包括添加或者刪除方法和屬性,以及修改值.
對于現(xiàn)代的模塊機(jī)制
本質(zhì)上都是將這種模塊定義封裝進(jìn)一個友好的API
//模塊管理工具,MyModules var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i=0; i未來的模塊機(jī)制
即ES6為模塊增加的以及語法支持, 在通過莫魁岸系統(tǒng)進(jìn)行加載時,ES6會將文件當(dāng)做獨(dú)立模塊來處理.每個模塊都可以導(dǎo)入其他模塊或者特定的API成員,同樣也可以導(dǎo)出自己的API成員.
(
因?yàn)楹瘮?shù)的模塊并不是一個能被靜態(tài)識別的模式(編譯器無法識別,)他們的API語義只有在運(yùn)行時才被考慮進(jìn)來.
相比之下 ES6模塊API是靜態(tài)的,編譯器知道這一點(diǎn) 然后在編譯器會對模塊導(dǎo)出的API或者成員的引用做一次檢查是否存在,不存在就會拋出早期的錯誤 不會等到運(yùn)行期在動態(tài)解析
)bar.js function hello(who){ return "我是"+who } export hello; foo.js import hello from "bar" var new = "marry"; function newHello(){ console.log( hello(new).toUpperCase(); ) } export awesome; baz.js module foo from "foo"; module bar from "bar"; console.log(bar.hello("lil")) //我是lil foo.newHello(); //我是MARRY; // import 可以將一個模塊的一個或者多個API導(dǎo)入到當(dāng)前作用域中,并分別綁定在一個變量上. module會將整個模塊的API導(dǎo)入并綁定. export會將當(dāng)前模塊的一個標(biāo)識符(變量 函數(shù)) 導(dǎo)出為公共APII總結(jié):
當(dāng)函數(shù)可以記住并且能訪問所在語法作用域,即使函數(shù)實(shí)在當(dāng)前作用域之外執(zhí)行,這時候就產(chǎn)生了閉包.
對于模塊首先必須有外部的封閉函數(shù),該函數(shù)必須被至少調(diào)用一次,(每次調(diào)用都會創(chuàng)建一個新的模塊實(shí)例)
封閉函數(shù)必須返回至少一個內(nèi)部函數(shù),這樣內(nèi)部函數(shù)才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態(tài)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97576.html
摘要:這篇文章總結(jié)下之前看的文章和自己在工作中遇到的坑,純手寫的,有什么寫的不對的請多多提出修正哈變量提升何為變量提升里面的變量在聲明之前就可以使用,因?yàn)樵撀暶饕呀?jīng)被提升至該作用域函數(shù)或全局的頂部直接上代碼和都會變量提升優(yōu)先級上面可理解為函數(shù)這時 這篇文章總結(jié)下之前看的文章和自己在工作中遇到的坑,純手寫的,有什么寫的不對的請多多提出修正哈 變量提升 何為變量提升?js里面的變量在聲明之前就可...
摘要:函數(shù)作用域要理解閉包,必須從理解函數(shù)被調(diào)用時都會發(fā)生什么入手??梢哉f,閉包是函數(shù)作用域的副產(chǎn)品。無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會持有對原始定義作用域的引用,無論在何處執(zhí)行這個函數(shù)都會使用閉包。 函數(shù)作用域 要理解閉包,必須從理解函數(shù)被調(diào)用時都會發(fā)生什么入手。 我們知道,每個javascript函數(shù)都是一個對象,其中有一些屬性我們可以訪問到,有一些不可以訪問,...
摘要:大名鼎鼎的閉包面試必問。閉包的作用是什么。看到閉包在哪了嗎閉包到底是什么五年前,我也被這個問題困擾,于是去搜了并總結(jié)下來。關(guān)于閉包的謠言閉包會造成內(nèi)存泄露錯。閉包里面的變量明明就是我們需要的變量,憑什么說是內(nèi)存泄露這個謠言是如何來的因?yàn)椤? 本文為饑人谷講師方方原創(chuàng)文章,首發(fā)于 前端學(xué)習(xí)指南。 大名鼎鼎的閉包!面試必問。請用自己的話簡述 什么是「閉包」。 「閉包」的作用是什么。 首先...
摘要:類型是提供的引用類型之一,通過可需變更創(chuàng)建對象。調(diào)用自身的函數(shù)被稱之為遞歸函數(shù)。想要解決上述遞歸函數(shù)的問題,可以使用對象屬性替換具體的函數(shù)名。保護(hù)貢獻(xiàn)的局部變量。 Function類型 概述 Function與函數(shù) 函數(shù)是這樣的一段JavaScript代碼,她只定義一次,但是可能被執(zhí)行或調(diào)用多次。Function類型是JavaScript提供的引用類型之一,通過Function可u需變...
摘要:可見裝飾器改變了函數(shù)的功能。裝飾器除了改變函數(shù)功能之外還有一個特性是,函數(shù)裝飾器在導(dǎo)入模塊時立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時運(yùn)行。 什么是裝飾器 裝飾器是什么,簡單來說,裝飾器可以改變一個函數(shù)的行為,比如原本有一個函數(shù)用來計(jì)算菲波那切數(shù)列,我們給這個函數(shù)加個計(jì)算執(zhí)行時間的裝飾器,這樣原來的函數(shù)不僅能夠計(jì)算菲波那切數(shù)列,而且還可以輸出計(jì)算花費(fèi)了多少時間。 在Python中,有幾個很...
閱讀 740·2021-11-17 09:33
閱讀 3771·2021-09-01 10:46
閱讀 1762·2019-08-30 11:02
閱讀 3290·2019-08-29 15:05
閱讀 1407·2019-08-26 11:39
閱讀 2283·2019-08-23 17:04
閱讀 1982·2019-08-23 15:43
閱讀 1379·2019-08-23 14:12