Javascript作用域指的是什么?這個(gè)問題可能是我們?nèi)粘W(xué)習(xí)或工作經(jīng)常見到的。希望通過這個(gè)問題能讓你收獲頗深。下面是小編給大家?guī)?lái)的參考內(nèi)容,讓我們一起來(lái)看看吧!
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、成都小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了靖西免費(fèi)建站歡迎大家使用!
本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于Javascript作用域的深入解析(代碼示例),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助。
作用域
作用域是一套規(guī)則,用于確定在何處以及如何查找變量(標(biāo)識(shí)符)。如果查找的目的是對(duì)變量進(jìn)行賦值,那么就會(huì)使用 LHS 查詢;如果目的是獲取變量的值,就會(huì)使用 RHS 查詢。賦值操作符會(huì)導(dǎo)致 LHS 查詢。 = 操作符或調(diào)用函數(shù)時(shí)傳入?yún)?shù)的操作都會(huì)導(dǎo)致關(guān)聯(lián)作用域的賦值操作。
JavaScript 引擎首先會(huì)在代碼執(zhí)行前對(duì)其進(jìn)行編譯,在這個(gè)過程中,像 var a = 2 這樣的聲明會(huì)被分解成兩個(gè)獨(dú)立的步驟:
首先, var a 在其作用域中聲明新變量。這會(huì)在最開始的階段,也就是代碼執(zhí)行前進(jìn)行。
接下來(lái), a = 2 會(huì)查詢(LHS 查詢)變量 a 并對(duì)其進(jìn)行賦值。
LHS 和 RHS 查詢都會(huì)在當(dāng)前執(zhí)行作用域中開始,如果有需要(也就是說它們沒有找到所需的標(biāo)識(shí)符),就會(huì)向上級(jí)作用域繼續(xù)查找目標(biāo)標(biāo)識(shí)符,這樣每次上升一級(jí)作用域(一層樓),最后抵達(dá)全局作用域(頂層),無(wú)論找到或沒找到都將停止。
不成功的 RHS 引用會(huì)導(dǎo)致拋出 ReferenceError 異常。不成功的 LHS 引用會(huì)導(dǎo)致自動(dòng)隱式地創(chuàng)建一個(gè)全局變量(非嚴(yán)格模式下),該變量使用 LHS 引用的目標(biāo)作為標(biāo)識(shí)符,或者拋出 ReferenceError 異常(嚴(yán)格模式下)。
詞法作用域
詞法作用域意味著作用域是由書寫代碼時(shí)函數(shù)聲明的位置來(lái)決定的。編譯的詞法分析階段基本能夠知道全部標(biāo)識(shí)符在哪里以及是如何聲明的,從而能夠預(yù)測(cè)在執(zhí)行過程中如何對(duì)它們進(jìn)行查找。
JavaScript 中有兩個(gè)機(jī)制可以“欺騙”詞法作用域: eval(..) 和 with 。前者可以對(duì)一段包含一個(gè)或多個(gè)聲明的“代碼”字符串進(jìn)行演算,并借此來(lái)修改已經(jīng)存在的詞法作用域(在運(yùn)行時(shí))。后者本質(zhì)上是通過將一個(gè)對(duì)象的引用當(dāng)作作用域來(lái)處理,將對(duì)象的屬性當(dāng)作作用域中的標(biāo)識(shí)符來(lái)處理,從而創(chuàng)建了一個(gè)新的詞法作用域(同樣是在運(yùn)行時(shí))。
這兩個(gè)機(jī)制的副作用是引擎無(wú)法在編譯時(shí)對(duì)作用域查找進(jìn)行優(yōu)化,因?yàn)橐嬷荒苤?jǐn)慎地認(rèn)為這樣的優(yōu)化是無(wú)效的。使用這其中任何一個(gè)機(jī)制都將導(dǎo)致代碼運(yùn)行變慢。不要使用它們。
因?yàn)?JavaScript 采用的是詞法作用域,函數(shù)的作用域在函數(shù)定義的時(shí)候就決定了。
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope();//local scope
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();//local scope
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } var foo = checkscope(); foo();//local scope
函數(shù)聲明:function 函數(shù)名稱 (參數(shù):可選){ 函數(shù)體 }
函數(shù)表達(dá)式:function 函數(shù)名稱(可選)(參數(shù):可選){ 函數(shù)體 }
辨別:
如果不聲明函數(shù)名稱,它肯定是表達(dá)式。
如果function foo(){}是作為賦值表達(dá)式的一部分的話,那它就是一個(gè)函數(shù)表達(dá)式,如果function foo(){}被包含在一個(gè)函數(shù)體內(nèi),或者位于程序的最頂部的話,那它就是一個(gè)函數(shù)聲明。
被括號(hào)括住的(function foo(){}),他是表達(dá)式的原因是因?yàn)槔ㄌ?hào) ()是一個(gè)分組操作符,它的內(nèi)部只能包含表達(dá)式
function foo(){} // 聲明,因?yàn)樗浅绦虻囊徊糠? (function(){ function bar(){} // 聲明,因?yàn)樗呛瘮?shù)體的一部分 })(); var bar = function foo(){}; // 表達(dá)式,因?yàn)樗琴x值表達(dá)式的一部分 new function bar(){}; // 表達(dá)式,因?yàn)樗莕ew表達(dá)式 (function foo(){}); // 表達(dá)式:包含在分組操作符內(nèi) try { (var x = 5); // 分組操作符,只能包含表達(dá)式而不能包含語(yǔ)句:這里的var就是語(yǔ)句 } catch(err) { // SyntaxError }
函數(shù)聲明在條件語(yǔ)句內(nèi)雖然可以用,但是沒有被標(biāo)準(zhǔn)化,最好使用函數(shù)表達(dá)式
函數(shù)聲明會(huì)覆蓋變量聲明,但不會(huì)覆蓋變量賦值
function value(){ return 1; } var value; alert(typeof value); //"function"
函數(shù)是 JavaScript 中最常見的作用域單元。本質(zhì)上,聲明在一個(gè)函數(shù)內(nèi)部的變量或函數(shù)會(huì)在所處的作用域中“隱藏”起來(lái),這是有意為之的良好軟件的設(shè)計(jì)原則。但函數(shù)不是唯一的作用域單元。
塊作用域指的是變量和函數(shù)不僅可以屬于所處的作用域,也可以屬于某個(gè)代碼塊(通常指 { .. } 內(nèi)部)。
從 ES3 開始, try/catch 結(jié)構(gòu)在 catch 分句中具有塊作用域。
在 ES6 中引入了 let 關(guān)鍵字( var 關(guān)鍵字的表親),用來(lái)在任意代碼塊中聲明變量。
if(..) { let a = 2; } 會(huì)聲明一個(gè)劫持了 if 的 { .. } 塊的變量,并且將變量添加到這個(gè)塊中。
有些人認(rèn)為塊作用域不應(yīng)該完全作為函數(shù)作用域的替代方案。兩種功能應(yīng)該同時(shí)存在,開發(fā)者可以并且也應(yīng)該根據(jù)需要選擇使用何種作用域,創(chuàng)造可讀、可維護(hù)的優(yōu)良代碼。
我們習(xí)慣將 var a = 2; 看作一個(gè)聲明,而實(shí)際上 JavaScript 引擎并不這么認(rèn)為。它將 var a和 a = 2 當(dāng)作兩個(gè)單獨(dú)的聲明,第一個(gè)是編譯階段的任務(wù),而第二個(gè)則是執(zhí)行階段的任務(wù)。
這意味著無(wú)論作用域中的聲明出現(xiàn)在什么地方,都將在代碼本身被執(zhí)行前首先進(jìn)行處理??梢詫⑦@個(gè)過程形象地想象成所有的聲明(變量和函數(shù))都會(huì)被“移動(dòng)”到各自作用域的最頂端,這個(gè)過程被稱為提升。
聲明本身會(huì)被提升,而包括函數(shù)表達(dá)式的賦值在內(nèi)的賦值操作并不會(huì)提升。
要注意避免重復(fù)聲明,特別是當(dāng)普通的 var 聲明和函數(shù)聲明混合在一起的時(shí)候,否則會(huì)引
起很多危險(xiǎn)的問題!
var a; if (!("a" in window)) { a = 1; } alert(a);
通常,程序員會(huì)錯(cuò)誤的認(rèn)為,只有匿名函數(shù)才是閉包。其實(shí)并非如此,正如我們所看到的 —— 正是因?yàn)樽饔糜蜴?,使得所有的函?shù)都是閉包(與函數(shù)類型無(wú)關(guān): 匿名函數(shù),F(xiàn)E,NFE,F(xiàn)D都是閉包), 這里只有一類函數(shù)除外,那就是通過Function構(gòu)造器創(chuàng)建的函數(shù),因?yàn)槠鋄[Scope]]只包含全局對(duì)象。 為了更好的澄清該問題,我們對(duì)ECMAScript中的閉包作兩個(gè)定義(即兩種閉包):
ECMAScript中,閉包指的是:
從理論角度:所有的函數(shù)。因?yàn)樗鼈兌荚趧?chuàng)建的時(shí)候就將上層上下文的數(shù)據(jù)保存起來(lái)了。哪怕是簡(jiǎn)單的全局變量也是如此,因?yàn)楹瘮?shù)中訪問全局變量就相當(dāng)于是在訪問自由變量,這個(gè)時(shí)候使用最外層的作用域。
從實(shí)踐角度:以下函數(shù)才算是閉包:
即使創(chuàng)建它的上下文已經(jīng)銷毀,它仍然存在(比如,內(nèi)部函數(shù)從父函數(shù)中返回)
在代碼中引用了自由變量
for (var i=1; i<=5; i++) { (function(j) { setTimeout( function timer() { console.log( j ); }, j*1000 ); })( i ); } for (var i=1; i<=5; i++) { let j = i; // 是的,閉包的塊作用域! setTimeout( function timer() { console.log( j ); }, j*1000 ); } for (let i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); }
var data = []; for (var i = 0; i < 3; i++) { data[i] = function () { console.log(i); }; } data[0]();//3 data[1]();//3 data[2]();//3
模塊有兩個(gè)主要特征:(1)為創(chuàng)建內(nèi)部作用域而調(diào)用了一個(gè)包裝函數(shù);(2)包裝函數(shù)的返回
值必須至少包括一個(gè)對(duì)內(nèi)部函數(shù)的引用,這樣就會(huì)創(chuàng)建涵蓋整個(gè)包裝函數(shù)內(nèi)部作用域的閉
包。
var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i=0; i未來(lái)模塊機(jī)制
//bar.js function hello(who) { return "Let me introduce: " + who; } export hello;//foo.js // 僅從 "bar" 模塊導(dǎo)入 hello() import hello from "bar"; var hungry = "hippo"; function awesome() { console.log( hello( hungry ).toUpperCase() ); } export awesome;baz.js // 導(dǎo)入完整的 "foo" 和 "bar" 模塊 module foo from "foo"; module bar from "bar"; console.log( bar.hello( "rhino" ) ); // Let me introduce: rhino foo.awesome(); // LET ME INTRODUCE: HIPPO塊作用域替代方案
Google 維護(hù)著一個(gè)名為 Traceur 的項(xiàng)目,該項(xiàng)目正是用來(lái)將 ES6 代碼轉(zhuǎn)換成兼容 ES6 之前的環(huán)境(大部分是 ES5,但不是全部)。TC39 委員會(huì)依賴這個(gè)工具(也有其他工具)來(lái)測(cè)試他們指定的語(yǔ)義化相關(guān)的功能。
{ try { throw undefined; } catch (a) { a = 2; console.log( a ); } } console.log( a )上下文
EC(執(zhí)行環(huán)境或者執(zhí)行上下文,Execution Context)
EC={ VO:{/* 函數(shù)中的arguments對(duì)象, 參數(shù), 內(nèi)部的變量以及函數(shù)聲明 */}, this:{}, Scope:{ /* VO以及所有父執(zhí)行上下文中的VO */} }ECS(執(zhí)行環(huán)境棧Execution Context Stack)
//ECS=[Window] A(//ECS=[Window,A] B(//ECS=[Window,A,B] //run B ) //ECS=[Window,A] ) //ECS=[Window]VO(變量對(duì)象,Variable Object)
var a = 10; function test(x) { var b = 20; }; test(30); /* VO(globalContext) a: 10, test: VO(test functionContext) x: 30 b: 20 */AO(活動(dòng)對(duì)象,Active Object)
function test(a, b) { var c = 10; function d() {} var e = function _e() {}; (function x() {}); } test(10); /* AO(test) = { a: 10, b: undefined, c: undefined, d:e: undefined }; */ scope chain(作用域鏈)和[[scope]]屬性
Scope = AO|VO + [[Scope]]例子
var x = 10; function foo() { var y = 20; function bar() { var z = 30; alert(x + y + z); } bar(); } foo(); // 60
全局上下文的變量對(duì)象是:
globalContext.VO === Global = { x: 10 foo:};
在“foo”創(chuàng)建時(shí),“foo”的[[scope]]屬性是:
foo.[[Scope]] = [ globalContext.VO ];
在“foo”激活時(shí)(進(jìn)入上下文),“foo”上下文的活動(dòng)對(duì)象是:
fooContext.AO = { y: 20, bar:};
“foo”上下文的作用域鏈為:
fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.: fooContext.Scope = [ fooContext.AO, globalContext.VO ];
內(nèi)部函數(shù)“bar”創(chuàng)建時(shí),其[[scope]]為:
bar.[[Scope]] = [ fooContext.AO, globalContext.VO ];
在“bar”激活時(shí),“bar”上下文的活動(dòng)對(duì)象為:
barContext.AO = { z: 30 };
“bar”上下文的作用域鏈為:
barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.: barContext.Scope = [ barContext.AO, fooContext.AO, globalContext.VO ];
對(duì)“x”、“y”、“z”的標(biāo)識(shí)符解析如下:
- "x" -- barContext.AO // not found -- fooContext.AO // not found globalContext.VO // found - 10 - "y" -- barContext.AO // not found fooContext.AO // found - 20 - "z" barContext.AO // found - 30感謝各位的閱讀!看完上述內(nèi)容,你們對(duì)Javascript作用域指的是什么大概了解了嗎?希望文章內(nèi)容對(duì)大家有所幫助。如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)頁(yè)標(biāo)題:Javascript作用域指的是什么
文章位置:http://weahome.cn/article/ppjjso.html