欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

理解Javascript_13_執(zhí)行模型詳解

 更新時間:2010年10月20日 11:10:27   作者:  
在《理解Javascript_12_執(zhí)行模型淺析》一文中,我們初步的了解了執(zhí)行上下文與作用域的概念,那么這一篇將深入分析執(zhí)行上下文的構(gòu)建過程,了解執(zhí)行上下文、函數(shù)對象、作用域三者之間的關(guān)系。
函數(shù)執(zhí)行環(huán)境
簡單的代碼:
復(fù)制代碼 代碼如下:

function say(msg,other){
var str = "nobody say:";
this.name = '笨蛋的座右銘';
function method(){};//var method = function(){};
alert(str+msg);
}
say('hello world');
alert(name);//笨蛋的座右銘

當(dāng)調(diào)用say方法時,第一步是創(chuàng)建其執(zhí)行環(huán)境,在創(chuàng)建執(zhí)行環(huán)境的過程中,會按照定義的先后順序完成一系列操作:
1.首先會創(chuàng)建一個'活動對象'(Activation Object)?;顒訉ο笫且?guī)范中規(guī)定的另外一種機制。之所以稱之為對象,是因為它擁有可訪問的命名屬性,但是它又不像正常對象那樣具有原型(至少沒有預(yù)定義的原型),而且不能通過 JavaScript 代碼直接引用活動對象。
2.為函數(shù)調(diào)用創(chuàng)建執(zhí)行環(huán)境的下一步是創(chuàng)建一個 arguments 對象,這是一個類似數(shù)組的對象,它以整數(shù)索引的數(shù)組成員一一對應(yīng)地保存著調(diào)用函數(shù)時所傳遞的參數(shù)。這個對象也有 length 和 callee 屬性(更深入的內(nèi)容,請參見《理解Javascript_14_函數(shù)形式參數(shù)與arguments 》)。然后,會為活動對象創(chuàng)建一個名為“arguments”的屬性,該屬性引用前面創(chuàng)建的 arguments對象。
3.接著,為執(zhí)行環(huán)境分配作用域。作用域由對象列表(鏈)組成。(比較復(fù)雜,請參見:《理解Javascript_15_作用域分配與變量訪問規(guī)則,再送個閉包》)
4.之后會發(fā)生由 ECMA 262 中所謂'活動對象'完成的'變量實例化'(Variable Instatiation)的過程。此時會將函數(shù)的形式參數(shù)創(chuàng)建為可變對象的命名屬性,如果調(diào)用函數(shù)時傳遞的參數(shù)與形式參數(shù)一致,則將相應(yīng)參數(shù)的值賦給這些命名屬性(否則,會給命名屬性賦 undefined 值)。對于定義的內(nèi)部函數(shù),會以其聲明時所用名稱為可變對象創(chuàng)建同名屬性,而相應(yīng)的內(nèi)部函數(shù)則被創(chuàng)建為函數(shù)對象并指定給該屬性。變量實例化的最后一步是將在函數(shù)內(nèi)部聲明的所有局部變量創(chuàng)建為可變對象的命名屬性。注:在這個過程中,除了實際參數(shù)有值外和函數(shù)定義外,其它都被'預(yù)解析'為undefined值.
5.最后,要為使用 this 關(guān)鍵字而賦值。(此時的this指向的是全局對象,即window)

執(zhí)行環(huán)境創(chuàng)建成功后,就進入第二步:在函數(shù)體內(nèi),從上到下執(zhí)行代碼。
1.當(dāng)執(zhí)行到var str='nobody say'會發(fā)生稱之為'計算賦值表達式'的過程,此時,會將活動對象中key為str的值從undefined設(shè)置為'nobody say:'。
2.執(zhí)行到this.name='笨蛋的座右銘'時,會為作為this的對象添加屬性name,并賦值為'笨蛋的座右銘'.
3.然后是執(zhí)行function innerMethod(){};最后執(zhí)行'alert(str+msg),輸出'nobody say:hello world'.

function method(){}與var method = function(){}的區(qū)別
簡單的代碼:
復(fù)制代碼 代碼如下:

function say(){
method01();//method01
method02();//error
function method01(){
alert('method01');
}
var method02 = function(){
alert('method02');
}
}
say();

為什么調(diào)用方法method01能正常運行,而調(diào)用方法method02卻會報錯呢?
  首先,你要明確的知道m(xù)ethod01為一個函數(shù)對象,而method02為一個變量,它指向于另一個函數(shù)對象。根據(jù)上一節(jié)的內(nèi)容,在'活動對象'完成的'變量實例化'(Variable Instatiation)的過程中,對函數(shù)method01進行了正常的'預(yù)解析',而對于變量method02解析為undefined值,當(dāng)進入到執(zhí)行代碼的環(huán)節(jié)時,因為method02的調(diào)用在其計算函數(shù)表達式之前,因此將undefined當(dāng)作方法調(diào)來用,必然會報錯。解決方案比較簡單,就是將var method02=function(){...}放到其method02()的調(diào)用之前就可以了或者是用函數(shù)聲明的方式定義函數(shù)(function method(){...})。
注:計算函數(shù)表達式就是指程序執(zhí)行到var method02 = function(){...}。method02此時真正指向一個函數(shù)對象。因為在'預(yù)解析'時,method02只被賦于了undefined.

全局執(zhí)行環(huán)境(《執(zhí)行模型淺析》中已經(jīng)講過,不再深入)
  在一個頁面中,第一次載入JS代碼時創(chuàng)建一個全局執(zhí)行環(huán)境,全局執(zhí)行環(huán)境的作用域鏈實際上只由一個對象,即全局對象(window),在開始JavaScript代碼的執(zhí)行之前,引擎會創(chuàng)建好這個Scope Chain結(jié)構(gòu)。全局執(zhí)行環(huán)境也會有變量實例化的過程,它的內(nèi)部函數(shù)就是涉及大部分 JavaScript 代碼的、常規(guī)的頂級函數(shù)聲明。而且,在變量實例化過程中全局對象就是可變對象,這就是為什么全局性聲明的函數(shù)是全局對象屬性的原因。全局性聲明的變量同樣如此全局執(zhí)行環(huán)境會使用 this 對象來引用全局對象。

注:區(qū)別'函數(shù)執(zhí)行環(huán)境'中的活動對象(Activation Object)和全局執(zhí)行環(huán)境中的可變對象(Vriable Object),Variable Object也叫做Activation Object(因為有一些差異存在,所以規(guī)范中重新取一個名字以示區(qū)別,全局執(zhí)行環(huán)境/Eval執(zhí)行環(huán)境中叫Variable Object,函數(shù)執(zhí)行環(huán)境中就叫做Activation Object)。

Eval執(zhí)行環(huán)境
構(gòu)建Eval執(zhí)行環(huán)境時的可變對象(Variable Object)就是調(diào)用eval時當(dāng)前執(zhí)行上下文中的可變對象(Variable Object)。在全局執(zhí)行環(huán)境中調(diào)用eval函數(shù),它的可變對象(Variable Object)就是全局對象;在函數(shù)中調(diào)用eval,它的可變對象(Variable Object)就是函數(shù)的活動對象(Activation Object)。
復(fù)制代碼 代碼如下:

//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(arg){
var innerVar = "variable in function";
eval(' \
var evalVar = "variable in eval"; \
document.write(arg + "<br />"); \
document.write(innerVar + "<br />"); \
');
document.write(evalVar);
}
fn("arguments for function");

輸出結(jié)果是:
arguments for function
variable in function
variable in eval
說明: eval調(diào)用中可以訪問函數(shù)fn的參數(shù)、局部變量;在eval中定義的局部變量在函數(shù)fn中也可以訪問,因為它們的Varible Object是同一個對象。
進入Eval Code執(zhí)行時會創(chuàng)建一個新的Scope Chain,內(nèi)容與當(dāng)前執(zhí)行上下文的Scope Chain完全一樣。

最后的實例
代碼如下:
復(fù)制代碼 代碼如下:

var outerVar1="variable in global code";
function fn1(arg1, arg2){
var innerVar1="variable in function code";
function fn2() { return outerVar1+" - "+innerVar1+" - "+" - "+(arg1 + arg2); }
return fn2();
}
var outerVar2=fn1(10, 20);

執(zhí)行處理過程大致如下:
1. 初始化Global Object即window對象,Variable Object為window對象本身。創(chuàng)建Scope Chain對象,假設(shè)為scope_1,其中只包含window對象。
2. 掃描JS源代碼(讀入源代碼、可能有詞法語法分析過程),從結(jié)果中可以得到定義的變量名、函數(shù)對象。按照掃描順序:
2.1 發(fā)現(xiàn)變量outerVar1,在window對象上添加outerVar1屬性,值為undefined;
2.2 發(fā)現(xiàn)函數(shù)fn1的定義,使用這個定義創(chuàng)建函數(shù)對象,傳給創(chuàng)建過程的Scope Chain為scope_1。將結(jié)果添加到window的屬性中,名字為fn1,值為返回的函數(shù)對象。注意fn1的內(nèi)部[[Scope]]就是 scope_1。另外注意,創(chuàng)建過程并不會對函數(shù)體中的JS代碼做特殊處理,可以理解為只是將函數(shù)體JS代碼的掃描結(jié)果保存在函數(shù)對象的內(nèi)部屬性上,在函數(shù)執(zhí)行時再做進一步處理。這對理解Function Code,尤其是嵌套函數(shù)定義中的Variable Instantiation很關(guān)鍵;
2.3 發(fā)現(xiàn)變量outerVar2,在window對象上添加outerVar2屬性,值為undefined;
3. 執(zhí)行outerVar1賦值語句,賦值為"variable in global code"。
4. 執(zhí)行函數(shù)fn1,得到返回值:
4.1 創(chuàng)建Activation Object,假設(shè)為activation_1;創(chuàng)建一個新的Scope Chain,假設(shè)為scope_2,scope_2中第一個對象為activation_1,第二個對象為window對象(取自fn1的 [[Scope]],即scope_1中的內(nèi)容);
4.2 處理參數(shù)列表。在activation_1上設(shè)置屬性arg1、arg2,值分別為10、20。創(chuàng)建arguments對象并進行設(shè)置,將arguments設(shè)置為activation_1的屬性;
4.3 對fn1的函數(shù)體執(zhí)行類似步驟2的處理過程:
4.3.1 發(fā)現(xiàn)變量innerVar1,在activation_1對象上添加innerVar1屬性,值為undefine;
4.3.2 發(fā)現(xiàn)函數(shù)fn2的定義,使用這個定義創(chuàng)建函數(shù)對象,傳給創(chuàng)建過程的Scope Chain為scope_2(函數(shù)fn1的Scope Chain為當(dāng)前執(zhí)行上下文的內(nèi)容)。將結(jié)果添加到activation_1的屬性中,名字為fn2,值為返回的函數(shù)對象。注意fn2的內(nèi)部 [[Scope]]就是scope_2;
4.4 執(zhí)行innerVar1賦值語句,賦值為"variable in function code"。
4.5 執(zhí)行fn2:
4.5.1 創(chuàng)建Activation Object,假設(shè)為activation_2;創(chuàng)建一個新的Scope Chain,假設(shè)為scope_3,scope_3中第一個對象為activation_2,接下來的對象依次為activation_1、window 對象(取自fn2的[[Scope]],即scope_2);
4.5.2 處理參數(shù)列表。因為fn2沒有參數(shù),所以只用創(chuàng)建arguments對象并設(shè)置為activation_2的屬性。
4.5.3 對fn2的函數(shù)體執(zhí)行類似步驟2的處理過程,沒有發(fā)現(xiàn)變量定義和函數(shù)聲明。
4.5.4 執(zhí)行函數(shù)體。對任何一個變量引用,從scope_3上進行搜索,這個示例中,outerVar1將在window上找到;innerVar1、arg1、arg2將在activation_1上找到。
4.5.5 丟棄scope_3、activation_2(指它們可以被垃圾回收了)。
4.5.6 返回fn2的返回值。
4.6 丟棄activation_1、scope_2。
4.7 返回結(jié)果。
5. 將結(jié)果賦值給outerVar2。

參考:
http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html
http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html

相關(guān)文章

最新評論