一.欺騙詞法作用域
1.eval
①eval
函數(shù)可以接收一個字符串作為參數(shù),以動態(tài)形式插入程序的某個位置趟薄,并對其詞法作用域的環(huán)境進(jìn)行修改。(可在運行期修改書寫期的詞法作用域)
function foo(str,a){
eval(str);
console.log(a,b);
}
var b = 2;
foo('var b = 5;',1);
>>>1 5
②詞法作用域是什么?
簡單來說,詞法作用域就是定義在詞法階段的作用域绕娘。換句話說,詞法作用域是由你在寫代碼時將變量和塊作用域?qū)懺谀睦飦頉Q定的栽连。因此险领,當(dāng)詞法分析器處理代碼時會保持作用域不變(大部分情況下這樣)。
③在嚴(yán)格模式下秒紧, eval()
函數(shù)在運行時擁有自己的詞法作用域绢陌。這意味著其中的聲明無法修改所在的作用域。
function foo(str,a){
"use strict"
eval(str);
console.log(a,b);
}
var b = 2;
foo('var b = 5;',1);
>>>1 2
function foo(str,a){
"use strict"
eval(str);
console.log(a,b);
}
foo('var b = 5;',1);
>>>Uncaught ReferenceError: b is not defined
2.with
①with
通常被當(dāng)做重復(fù)引用同一個對象中的多個屬性的快捷方式熔恢,可以不需要重復(fù)引用對象本身:
var obj = {
a: 1,
b: 2,
c: 3
};
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);
o1.a;
>>>2
foo(o2);
o2.a;
>>>undefined
a;
>>>2
//a被泄露到全局作用域中脐湾。因為,在非嚴(yán)格模式下叙淌,若a=2中的變量a未聲明秤掌,
也就是在任何作用域中都查找不到變量a,那么就會在全局作用域中創(chuàng)建一個變量a鹰霍,并將2賦值于它机杜。
③盡管with
塊可以將一個對象處理為詞法作用域,但是這個塊內(nèi)部正常的變量聲明并不會被限制在這個塊的作用域中衅谷,而是被添加到with
所處的函數(shù)的作用域中。
④with聲明實際上是根據(jù)你傳遞給它的對象憑空創(chuàng)建了一個全新的詞法作用域似将。
⑤當(dāng)我們傳遞對象o1
給with
時获黔,with
所聲明的作用域是o1
蚀苛,而這個作用域中含有一個與o1.a
屬性同名的標(biāo)識符。但當(dāng)我們將o2
對象作為作用域時玷氏,其中沒有a
標(biāo)識符堵未,因此進(jìn)行了正常的LHS (Left Hand Side)
查找,[最后導(dǎo)致創(chuàng)建了一個全局變量a并賦值為2]盏触。
⑥本質(zhì)上:通過將一個對象的引用當(dāng)成作用域來處理渗蟹,將對象的屬性當(dāng)成作用域中的標(biāo)識符來處理,從而創(chuàng)建了一個新的詞法作用域赞辩。(同樣也在運行時)
3.性能
①JavaScript引擎會在編譯階段進(jìn)行數(shù)項的性能優(yōu)化雌芽。其中有些優(yōu)化依賴與能夠根據(jù)代碼的詞法進(jìn)行靜態(tài)分析,并預(yù)先確定所有變量和函數(shù)的定義位置辨嗽,才能在執(zhí)行過程中快速找到標(biāo)識符世落。但是eval()
和with
的存在影響了引擎的性能。
②eval()
和with
還有個問題是:會被嚴(yán)格模式限制糟需。所以屉佳,避免使用它們。