91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Javascript中作用域的原理是什么

發布時間:2021-07-06 16:47:33 來源:億速云 閱讀:166 作者:Leah 欄目:開發技術

本篇文章給大家分享的是有關Javascript中作用域的原理是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

首先看一個例子:

var name = 'laruence';
function echo() {
	alert(name);
	var name = 'eve';
	alert(name);
	alert(age);
}

echo();

運行結果是什么呢?

上面的問題, 我相信會有很多人會認為是:

laruence
eve
[腳本出錯]

因為會以為在echo中, 第一次alert的時候, 會取到全局變量name的值, 而第二次值被局部變量name覆蓋, 所以第二次alert是’eve’. 而age屬性沒有定義, 所以腳本會出錯.

但其實, 運行結果應該是:

undefined
eve
[腳本出錯]

為什么呢?

JavaScript的作用域鏈

首先讓讓我們來看看Javasript(簡稱JS, 不完全代表JScript)的作用域的原理: JS權威指南中有一句很精辟的描述: ”JavaScript中的函數運行在它們被定義的作用域里,而不是它們被執行的作用域里.” 

為了接下來的知識, 你能順利理解, 我再提醒一下, 在JS中:”一切皆是對象, 函數也是”.

在JS中,作用域的概念和其他語言差不多, 在每次調用一個函數的時候 ,就會進入一個函數內的作用域,當從函數返回以后,就返回調用前的作用域.

JS的語法風格和C/C++類似, 但作用域的實現卻和C/C++不同,并非用“堆棧”方式,而是使用列表,具體過程如下(ECMA262中所述):
任何執行上下文時刻的作用域, 都是由作用域鏈(scope chain, 后面介紹)來實現.
在一個函數被定義的時候, 會將它定義時刻的scope chain鏈接到這個函數對象的[[scope]]屬性.
在一個函數對象被調用的時候,會創建一個活動對象(也就是一個對象), 然后對于每一個函數的形參,都命名為該活動對象的命名屬性, 然后將這個活動對象做為此時的作用域鏈(scope chain)最前端, 并將這個函數對象的[[scope]]加入到scope chain中.

看個例子:

	var func = function(lps, rps){
		var name = 'laruence';
		........
	}
	func();

在執行func的定義語句的時候, 會創建一個這個函數對象的[[scope]]屬性(內部屬性,只有JS引擎可以訪問, 但FireFox的幾個引擎(SpiderMonkey和Rhino)提供了私有屬性__parent__來訪問它), 并將這個[[scope]]屬性, 鏈接到定義它的作用域鏈上(后面會詳細介紹), 此時因為func定義在全局環境, 所以此時的[[scope]]只是指向全局活動對象window
active object.

在調用func的時候, 會創建一個活動對象(假設為aObj, 由JS引擎預編譯時刻創建, 后面會介紹),并創建arguments屬性, 然后會給這個對象添加倆個命名屬性aObj.lps, aObj.rps; 對于每一個在這個函數中申明的局部變量和函數定義, 都作為該活動對象的同名命名屬性.

然后將調用參數賦值給形參數,對于缺少的調用參數,賦值為undefined。

然后將這個活動對象做為scope chain的最前端, 并將func的[[scope]]屬性所指向的,定義func時候的頂級活動對象, 加入到scope china.

有了上面的作用域鏈, 在發生標識符解析的時候, 就會逆向查詢當前scope chain列表的每一個活動對象的屬性,如果找到同名的就返回。找不到,那就是這個標識符沒有被定義。

注意到, 因為函數對象的[[scope]]屬性是在定義一個函數的時候決定的, 而非調用的時候, 所以如下面的例子:

	var name = 'laruence';
	function echo() {
		alert(name);
	}

	function env() {
		var name = 'eve';
		echo();
	}

	env();

運行結果是:

laruence

結合上面的知識, 我們來看看下面這個例子:

function factory() {
	var name = 'laruence';
	var intro = function(){
		alert('I am ' + name);
	}
	return intro;
}

function app(para){
	var name = para;
	var func = factory();
	func();
}

app('eve');

當調用app的時候, scope chain是由: {window活動對象(全局)}->{app的活動對象} 組成.

在剛進入app函數體時, app的活動對象有一個arguments屬性, 倆個值為undefined的屬性: name和func. 和一個值為’eve’的屬性para;

此時的scope chain如下:

[[scope chain]] = [
{
	para : 'eve',
	name : undefined,
	func : undefined,
	arguments : []
}, {
	window call object
}
]

當調用進入factory的函數體的時候, 此時的factory的scope chain為:

[[scope chain]] = [
{
	name : undefined,
	intor : undefined
}, {
	window call object
}
]

注意到, 此時的作用域鏈中, 并不包含app的活動對象.

在定義intro函數的時候, intro函數的[[scope]]為:

[[scope chain]] = [
{
	name : 'laruence',
	intor : undefined
}, {
	window call object
}
]

從factory函數返回以后,在app體內調用intor的時候, 發生了標識符解析, 而此時的sope chain是:

[[scope chain]] = [
{
	intro call object
}, {
	name : 'laruence',
	intor : undefined
}, {
	window call object
}
]

因為scope chain中,并不包含factory活動對象. 所以, name標識符解析的結果應該是factory活動對象中的name屬性, 也就是’laruence’.

所以運行結果是:

I am laruence

現在, 大家對”JavaScript中的函數運行在它們被定義的作用域里,而不是它們被執行的作用域里.”這句話, 應該有了個全面的認識了吧?

Javascript的預編譯

我們都知道,JS是一種腳本語言, JS的執行過程, 是一種翻譯執行的過程.
那么JS的執行中, 有沒有類似編譯的過程呢?

首先, 我們來看一個例子:

	<script>
	alert(typeof eve); //function
		function eve() {
			alert('I am Laruence');
		};
	</script>

誒? 在alert的時候, eve不是應該還是未定義的么? 怎么eve的類型還是function呢?

恩, 對, 在JS中, 是有預編譯的過程的, JS在執行每一段JS代碼之前, 都會首先處理var關鍵字和function定義式(函數定義式和函數表達式).
如上文所說, 在調用函數執行之前, 會首先創建一個活動對象, 然后搜尋這個函數中的局部變量定義,和函數定義, 將變量名和函數名都做為這個活動對象的同名屬性, 對于局部變量定義,變量的值會在真正執行的時候才計算, 此時只是簡單的賦為undefined.

而對于函數的定義,是一個要注意的地方:

<script>
	alert(typeof eve); //結果:function
	alert(typeof walle); //結果:undefined
	function eve() { //函數定義式
		alert('I am Laruence');
	};
	var walle = function() { //函數表達式
	}
	alert(typeof walle); //結果:function
</script>

這就是函數定義式和函數表達式的不同, 對于函數定義式, 會將函數定義提前. 而函數表達式, 會在執行過程中才計算.

說到這里, 順便說一個問題 :

	var name = 'laruence';
	age = 26;

我們都知道不使用var關鍵字定義的變量, 相當于是全局變量, 聯系到我們剛才的知識:

在對age做標識符解析的時候, 因為是寫操作, 所以當找到到全局的window活動對象的時候都沒有找到這個標識符的時候, 會在window活動對象的基礎上, 返回一個值為undefined的age屬性.

也就是說, age會被定義在頂級作用域中.

現在, 也許你注意到了我剛才說的: JS在執行每一段JS代碼..
對, 讓我們看看下面的例子:

<script>
	alert(typeof eve); //結果:undefined
</script>
<script>
	function eve() {
		alert('I am Laruence');
	}
</script>

明白了么? 也就是JS的預編譯是以段為處理單元的…

揭開謎底

現在讓我們回到我們的第一個問題:

當echo函數被調用的時候, echo的活動對象已經被預編譯過程創建, 此時echo的活動對象為:

[callObj] = {
name : undefined
}

以上就是Javascript中作用域的原理是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

津南区| 绥中县| 安宁市| 光山县| 哈密市| 松滋市| 铅山县| 南部县| 宜春市| 高台县| 什邡市| 商城县| 环江| 剑阁县| 晋宁县| 斗六市| 西林县| 兴业县| 忻州市| 汾西县| 嵩明县| 玛沁县| 漳州市| 江门市| 肇庆市| 都匀市| 安康市| 黑龙江省| 应用必备| 武清区| 讷河市| 交城县| 甘德县| 阳谷县| 五莲县| 祁连县| 腾冲县| 通江县| 西林县| 金门县| 台东市|