|
導讀網頁的本質就是超級文本標記語言,通過結合使用其他的Web技術(如:腳本語言、公共網關接口、組件等),可以創造出功能強大的網頁。因而,超級文本標記語言是萬維網(Web)編程的基礎,也就是說萬維網是建立... 網頁的本質就是超級文本標記語言,通過結合使用其他的Web技術(如:腳本語言、公共網關接口、組件等),可以創造出功能強大的網頁。因而,超級文本標記語言是萬維網(Web)編程的基礎,也就是說萬維網是建立在超文本基礎之上的。超級文本標記語言之所以稱為超文本標記語言,是因為文本中包含了所謂“超級鏈接”點。 本篇文章給大家帶來的內容是關于Javascript作用域的深入解析(代碼示例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。作用域 作用域是一套規則,用于確定在何處以及如何查找變量(標識符)。如果查找的目的是對變量進行賦值,那么就會使用 LHS 查詢;如果目的是獲取變量的值,就會使用 RHS 查詢。賦值操作符會導致 LHS 查詢。 = 操作符或調用函數時傳入參數的操作都會導致關聯作用域的賦值操作。 JavaScript 引擎首先會在代碼執行前對其進行編譯,在這個過程中,像 var a = 2 這樣的聲明會被分解成兩個獨立的步驟:
LHS 和 RHS 查詢都會在當前執行作用域中開始,如果有需要(也就是說它們沒有找到所需的標識符),就會向上級作用域繼續查找目標標識符,這樣每次上升一級作用域(一層樓),最后抵達全局作用域(頂層),無論找到或沒找到都將停止。 不成功的 RHS 引用會導致拋出 ReferenceError 異常。不成功的 LHS 引用會導致自動隱式地創建一個全局變量(非嚴格模式下),該變量使用 LHS 引用的目標作為標識符,或者拋出 ReferenceError 異常(嚴格模式下)。 詞法作用域 詞法作用域意味著作用域是由書寫代碼時函數聲明的位置來決定的。編譯的詞法分析階段基本能夠知道全部標識符在哪里以及是如何聲明的,從而能夠預測在執行過程中如何對它們進行查找。 JavaScript 中有兩個機制可以“欺騙”詞法作用域: eval(..) 和 with 。前者可以對一段包含一個或多個聲明的“代碼”字符串進行演算,并借此來修改已經存在的詞法作用域(在運行時)。后者本質上是通過將一個對象的引用當作作用域來處理,將對象的屬性當作作用域中的標識符來處理,從而創建了一個新的詞法作用域(同樣是在運行時)。 這兩個機制的副作用是引擎無法在編譯時對作用域查找進行優化,因為引擎只能謹慎地認為這樣的優化是無效的。使用這其中任何一個機制都將導致代碼運行變慢。不要使用它們。 因為 JavaScript 采用的是詞法作用域,函數的作用域在函數定義的時候就決定了。 var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();//local scopevar scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();//local scopevar scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();//local scope函數表達式和函數聲明函數聲明:function 函數名稱 (參數:可選){ 函數體 } 辨別:
function foo(){} // 聲明,因為它是程序的一部分
(function(){
function bar(){} // 聲明,因為它是函數體的一部分
})();
var bar = function foo(){}; // 表達式,因為它是賦值表達式的一部分
new function bar(){}; // 表達式,因為它是new表達式
(function foo(){}); // 表達式:包含在分組操作符內
try {
(var x = 5); // 分組操作符,只能包含表達式而不能包含語句:這里的var就是語句
} catch(err) {
// SyntaxError
}
function value(){
return 1;
}
var value;
alert(typeof value); //"function"函數作用域和塊作用域函數是 JavaScript 中最常見的作用域單元。本質上,聲明在一個函數內部的變量或函數會在所處的作用域中“隱藏”起來,這是有意為之的良好軟件的設計原則。但函數不是唯一的作用域單元。 塊作用域指的是變量和函數不僅可以屬于所處的作用域,也可以屬于某個代碼塊(通常指 { .. } 內部)。 從 ES3 開始, try/catch 結構在 catch 分句中具有塊作用域。 在 ES6 中引入了 let 關鍵字( var 關鍵字的表親),用來在任意代碼塊中聲明變量。 有些人認為塊作用域不應該完全作為函數作用域的替代方案。兩種功能應該同時存在,開發者可以并且也應該根據需要選擇使用何種作用域,創造可讀、可維護的優良代碼。 提升我們習慣將 var a = 2; 看作一個聲明,而實際上 JavaScript 引擎并不這么認為。它將 var a和 a = 2 當作兩個單獨的聲明,第一個是編譯階段的任務,而第二個則是執行階段的任務。 這意味著無論作用域中的聲明出現在什么地方,都將在代碼本身被執行前首先進行處理。可以將這個過程形象地想象成所有的聲明(變量和函數)都會被“移動”到各自作用域的最頂端,這個過程被稱為提升。 聲明本身會被提升,而包括函數表達式的賦值在內的賦值操作并不會提升。 var a;
if (!("a" in window)) {
a = 1;
}
alert(a);作用域閉包通常,程序員會錯誤的認為,只有匿名函數才是閉包。其實并非如此,正如我們所看到的 —— 正是因為作用域鏈,使得所有的函數都是閉包(與函數類型無關: 匿名函數,FE,NFE,FD都是閉包), 這里只有一類函數除外,那就是通過Function構造器創建的函數,因為其[[Scope]]只包含全局對象。 為了更好的澄清該問題,我們對ECMAScript中的閉包作兩個定義(即兩種閉包): ECMAScript中,閉包指的是: 從理論角度:所有的函數。因為它們都在創建的時候就將上層上下文的數據保存起來了。哪怕是簡單的全局變量也是如此,因為函數中訪問全局變量就相當于是在訪問自由變量,這個時候使用最外層的作用域。 循環閉包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模塊模塊有兩個主要特征:(1)為創建內部作用域而調用了一個包裝函數;(2)包裝函數的返回 現代模塊機制var MyModules = (function Manager() {
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];
}
return {
define: define,
get: get
};
})();未來模塊機制//bar.js
function hello(who) {
return "Let me introduce: " + who;
}
export hello;//foo.js
// 僅從 "bar" 模塊導入 hello()
import hello from "bar";
var hungry = "hippo";
function awesome() {
console.log(
hello( hungry ).toUpperCase()
);
}
export awesome;baz.js
// 導入完整的 "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 維護著一個名為 Traceur 的項目,該項目正是用來將 ES6 代碼轉換成兼容 ES6 之前的環境(大部分是 ES5,但不是全部)。TC39 委員會依賴這個工具(也有其他工具)來測試他們指定的語義化相關的功能。 {
try {
throw undefined;
} catch (a) {
a = 2;
console.log( a );
}
}
console.log( a )上下文EC(執行環境或者執行上下文,Execution Context)EC={
VO:{/* 函數中的arguments對象, 參數, 內部的變量以及函數聲明 */},
this:{},
Scope:{ /* VO以及所有父執行上下文中的VO */}
}ECS(執行環境棧Execution Context Stack)//ECS=[Window]
A(//ECS=[Window,A]
B(//ECS=[Window,A,B]
//run B
)
//ECS=[Window,A]
)
//ECS=[Window]VO(變量對象,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(活動對象,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: <reference to FunctionDeclaration "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
globalContext.VO === Global = {
x: 10
foo: <reference to function>
};
foo.[[Scope]] = [ globalContext.VO ];
fooContext.AO = {
y: 20,
bar: <reference to function>
};
fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.: fooContext.Scope = [ fooContext.AO, globalContext.VO ];
bar.[[Scope]] = [ fooContext.AO, globalContext.VO ];
barContext.AO = {
z: 30
};
barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.: barContext.Scope = [ barContext.AO, fooContext.AO, globalContext.VO ];
- "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 以上就是Javascript作用域的深入解析(代碼示例)的詳細內容,更多請關注php中文網其它相關文章! 網站建設是一個廣義的術語,涵蓋了許多不同的技能和學科中所使用的生產和維護的網站。 |
溫馨提示:喜歡本站的話,請收藏一下本站!