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

JavaScript中eval和with語句如何影響作用域鏈的深度探索

 更新時間:2023年05月29日 11:48:23   作者:控心crazy  
這篇文章主要為大家介紹了JavaScript中eval和with語句如何影響作用域鏈的深度探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

在上篇文章中,我們介紹了深度剖析了作用域,并將其定義為一套規(guī)則,這套規(guī)則用來管理引擎如何在當前作用域以及嵌套的子作用域中根據(jù)標識符名稱進行變量查找。

上篇文章入口:JavaScript作用域深度剖析:從局部到全局一網打盡

而作用域一共分為兩種:詞法作用域 和 動態(tài)作用域, 而本篇文章我們將深入 詞法作用域,讓我們一起來了解一下吧。

詞法階段

簡單來說,詞法作用域就是定義在詞法階段的作用域。換句話說,詞法作用域是由你在寫代碼時將變量和塊作用域寫在哪里決定的。

上述代碼作用域:

包含著整個全局作用域,其中只有一個標識符: foo

包含著 foo 所創(chuàng)建的作用域,其中有三個標識符: a、bar、b

包含著 bar 所創(chuàng)建的作用域,其中只有一個標識符:c

  • 在此,只要假設每個函數(shù)都會創(chuàng)建一個新的作用域氣泡即可。
  • 記?。鹤饔糜虿檎視谡业降谝粋€匹配的標識符時停止。在多層的嵌套作用域中可定義同名的標識符,這叫做"遮蔽效應"(內部標識符 "遮蔽" 了外部的標識符)。
  • 作用域查找規(guī)則:作用域查找始終從運行時所處的最內部作用域開始,逐級向外或者說向上進行,直到遇見第一個匹配的標識符為止。

無論函數(shù)在哪里被調用,或如何被調用,它的詞法作用域都只由函數(shù)被聲明時所處的位置決定。

function foo(a) {
var b = a * 2;
function bar(c) {
    console.log(a, b, c);
}
bar(b * 3);
}
foo(2); // 2 4 12
  • 詞法作用域只會查找一級標識符,比如a、b、c。如果代碼中引用了 foo.bar.baz, 詞法作用域查找只會試圖查找 foo 標識符,找到這個變量后,對象屬性訪問規(guī)則會分別接管與 bar 和 baz 屬性的訪問。

欺騙詞法

JavaScript 有兩種機制來實現(xiàn)這個目的。

eval(不推薦使用)

  • JS 中的 eval(...) 函數(shù)可接收一個字符串作為參數(shù)。換句話說,在此位置寫的內容就好像是寫在那個位置上的代碼一樣。根據(jù)這個原理來理解 eval(...) 它是如何通過代碼欺騙和假裝成書寫時代碼就在那,來實現(xiàn)修改詞法作用域環(huán)境的。

在執(zhí)行 eval(...) 之后的代碼時,引擎并不 知道 或 在意 前面的代碼是否以動態(tài)形式插入進來的,并對詞法作用域的環(huán)境進行修改的。引擎只會如往常地進行此法作用域查找。

function foo(str, a) {
eval(str); // 欺騙!
console.log(a, b); // 1 3
}
var b = 2;
foo("var b = 3;", 1);
  • eval(...) 調用中的 var b = 3; 這段代碼就會被當做本來就在那里一樣來處理。由于這段代碼聲明了一個新的變量 b,因此它對已經存在的 foo(...) 詞法作用域進行了修改。事實上,這段代碼再 foo(...) 內部創(chuàng)建了一個變量 b,并遮蔽了外部(全局)作用域中的同名變量。
  • 當執(zhí)行 console.log(...) 時,會在 foo(...) 的內部找到 a 和 b,但永遠無法找到外部的 b。因此會輸出 1, 3, 而不是正常情況下輸出的 1, 2。
  • 默認情況下,eval(...)中所執(zhí)行的代碼中包含一個或多個聲明(無論是變量還是函數(shù)),都會對 eval(...) 所處的詞法作用域進行修改。

嚴格模式下:eval(...)在運行時有著自己的詞法作用域,意味著其中的聲明無法修改所在的作用域。

function foo(str) {
'use strict';
eval(str);
console.log(a); // ReferenceError: a is not defined
}
foo("var b = 3;");

with(不推薦使用)

with 通常被當做重復引用同一個對象中多個屬性的快捷方式。

var obj = {
a: 1,
b: 2,
c: 3
};
// 單調乏味的重復"obj"
obj.a = 2;
obj.b = 3;
obj.c = 4;
// 簡單的快捷方式
with (obj) {
a = 3;
b = 4;
c = 5;
}

其實不僅僅是為了方便訪問對象屬性,例如:

function foo(obj) {
with (obj) {
    a = 2;
}
}
var o1 = {
a: 3
};
var o2 = {
b: 3
};
foo( o1 );
console.log( o1.a ); // 2
foo( o2 );
console.log( o2.a ); // undefined
console.log( a ); // 2——不好,a 被泄漏到全局作用域上了!
  • 這個例子中創(chuàng)建了 o1、o2 兩個對象,其中一個具有 a 屬性,另一個沒有。foo(...) 函數(shù)接收一個 obj 參數(shù),該參數(shù)是一個對象引用,并對這個對象引用執(zhí)行了 with(obj){...}。在 with 內部,只是對變量 a 進行了簡單的詞法引用,實際上就是一個 LHS, 并將 2 復制給了它。
  • 當我們將 o1 傳遞進去,a = 2 賦值操作找到了 o1.a 并將 2 賦值給它,這在后面的console.log(o1.a) 中可以體現(xiàn)出來。而當 o2 傳遞進去,o2 沒有 a 屬性,因此不會創(chuàng)建一個屬性,o2.a 保持 undefined。
  • 但是可以注意到一個奇怪的副作用,實際上 a = 2 賦值操作創(chuàng)建了一個全局的變量 a。這是怎么回事?

    • with 可將一個沒有或有多個屬性的對象處理為一個完全隔離的詞法作用域,因此這個對象的屬性會被處理為定義在這個作用域中的詞法標識符。
    • 盡管 with 塊可將一個對象處理為詞法作用域,但這個塊內中正常的 var 聲明并不會被限制在這個塊的作用域中,而是被添加到 with 所處的函數(shù)作用域中。
  • eval 與 with 的區(qū)別?

    • eval(...) 函數(shù)接收一個或多個聲明的代碼,會修改其所處的詞法作用域,而 with 聲明實際上是根據(jù)你傳遞給它的對象憑空創(chuàng)建一個全新的詞法作用域。
  • 另外不推薦使用 eval(...) 和 with(...){...} 的原因是會被嚴格模式所影響(限制)。with 被完全禁止,而在保留核心功能的前提下,間接或非安全地使用 eval(...) 也被禁止了。

 性能

  • 你可能會問,如果他們能實現(xiàn)更復雜的功能,并且代碼更具有擴展性,難道不是非常好的功能嗎?答案是否定的。
  • JavaScript 引擎會在編譯階段進行數(shù)項的性能優(yōu)化。其中有些優(yōu)化依賴于能夠根據(jù)代碼的詞法進行靜態(tài)分析,并預先確定所有變量和函數(shù)的定義位置,才能在執(zhí)行過程中快速找到標識符。
  • 但如果引擎在代碼中發(fā)現(xiàn)了 eval(..) 或 with,它只能簡單地假設關于標識符位置的判斷都是無效的,因為無法在詞法分析階段明確知道 eval(..) 會接收到什么代碼,這些代碼會如何對作用域進行修改,也無法知道傳遞給 with 用來創(chuàng)建新詞法作用域的對象的內容到底是什么。
  • 最悲觀的情況是如果出現(xiàn)了 eval(..) 或 with,所有的優(yōu)化可能都是無意義的,因此最簡單的做法就是完全不做任何優(yōu)化。
  • 如果代碼中大量使用 eval(..) 或 with,那么運行起來一定會變得非常慢。無論引擎多聰明,試圖將這些悲觀情況的副作用限制在最小范圍內,也無法避免如果沒有這些優(yōu)化,代碼會運行得更慢這個事實。

小結

  • 詞法作用域意味著作用域是由書寫代碼時函數(shù)聲明的位置來決定。
  • JavaScript 有兩種機制可欺騙詞法作用域:eval(...) 和 with(...){...}。

    • eval(...): 修改所處位置的詞法作用域。
    • with(...){...}: 將對象的引用當做作用域來處理,將對象中的屬性當做作用域中標識符來處理,從而創(chuàng)建一個新的詞法作用域。
  • eval(...) 和 with(...){...} 這兩個機制的副作用是引擎無法在編譯時對作用域查找進行優(yōu)化的。所以, 不要使用他們。

特殊字符描述:

  • 問題標注 Q:(question)
  • 答案標注 R:(result)
  • 注意事項標準:A:(attention matters)
  • 詳情描述標注:D:(detail info)
  • 總結標注:S:(summary)
  • 分析標注:Ana:(analysis)
  • 提示標注:T:(tips)

以上就是JavaScript中eval和with語句如何影響作用域鏈的深度探索的詳細內容,更多關于JS eval with作用域鏈的資料請關注腳本之家其它相關文章!

相關文章

最新評論