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

由?JavaScript?的?with?引發(fā)的探索

 更新時(shí)間:2022年01月25日 17:17:49   作者:前端巔峰?  
這篇文章主要介紹了由?JavaScript?的?with?引發(fā)的探索,js?的?with?是為對(duì)象訪問提供命名空間式的訪問方式,with?創(chuàng)建一個(gè)對(duì)象的命名空間,在這個(gè)命名空間內(nèi)你可以直接訪問對(duì)象的屬性,而不需要通過對(duì)象來訪問,下面一起來了解更多詳細(xì)內(nèi)容吧

一下文章來源于微信公眾號(hào)前端巔峰

1. 背景

某天吃飯的時(shí)候突然想到,都說 with 會(huì)有問題,那么是什么問題,是怎樣導(dǎo)致的呢?知其然不知其所以然,在好奇心的驅(qū)使下,從 with 出發(fā),一路追溯到 VO、AO。那么先來復(fù)習(xí)一下 with 是干嘛的吧。

2. with

js 的 with 是為對(duì)象訪問提供命名空間式的訪問方式,with 創(chuàng)建一個(gè)對(duì)象的命名空間,在這個(gè)命名空間內(nèi)你可以直接訪問對(duì)象的屬性,而不需要通過對(duì)象來訪問:

const o = { a: 1, b: 2 };

with (o) {
? ? console.log(a); // 1
? ? b = 3; // o: { a: 1, b: 3 }
}

看起來挺方便的哈?

const o = { a: 1, b: 2 };
const p = { a: 3 };
with (o) {
? ? console.log(a); // 1
? ? with (p) {
? ? ? ? console.log(a); // 3
? ? ? ? b = 4; // o: { a: 1, b: 4 }
? ? ? ? c = 5; // window.c = 5
? ? }
}

嗯,他還有作用域鏈的性質(zhì)。但是,如果給不存在的屬性賦值,將會(huì)沿著作用域鏈給該變量賦值,在沒有找到變量時(shí),非嚴(yán)格模式下將會(huì)自動(dòng)在全局創(chuàng)建一個(gè)變量。這就導(dǎo)致了數(shù)據(jù)泄露。

那么導(dǎo)致數(shù)據(jù)泄露的原因是什么呢?這需要了解 LHS 查詢,這個(gè)待會(huì)再說。

那來看看 js 是怎么查詢的:當(dāng) with 對(duì)象 o 的時(shí)候,with 聲明的作用域是 o,從這里對(duì) c 進(jìn)行 LHS 查詢。o 的作用域和全局作用域都沒有找到 c,在非嚴(yán)格模式下,失敗的 LHS 會(huì)自動(dòng)隱式的在全局創(chuàng)建一個(gè)標(biāo)識(shí)符 c,如果是嚴(yán)格模式,則會(huì)拋出 ReferenceError。

2.1. with 的性能問題

使用 with:

將近 10 倍的差距。

原因是什么呢?

js 預(yù)編譯階段會(huì)進(jìn)行的優(yōu)化,由于 with 創(chuàng)建新的詞法作用域,導(dǎo)致 o 的 a 屬性和 o 分離開位于兩個(gè)不同的作用域,不能快速找到標(biāo)識(shí)符,引擎將不會(huì)做任何優(yōu)化。

這就好比你去某家店,引擎給了你一個(gè)牛逼的老大,老板一眼就知道該怎么做;套上 with 之后,老板不知道你的老大是誰,還要花時(shí)間去找,時(shí)間就這樣浪費(fèi)了。

3. LHS 和 RHS

  • LHS:賦值操作的目標(biāo)是誰
  • RHS:誰是賦值操作的源頭

所以我們來看這段代碼:

var a = 1;

在執(zhí)行的時(shí)候,這段代碼會(huì)被拆成兩部分

var a;
a = 1;

當(dāng)我們使用 a 時(shí)

console.log(a);

對(duì) a 進(jìn)行了 RHS 查詢,以獲得 a 的值,并隱式賦值給console.log 函數(shù)的形參,賦值對(duì)象的查找也是 LHS 查詢。

當(dāng)然,更直白的 LHS 也如上文寫到的 a = 1。

在變量還沒有聲明(在任何作用域中都無法找到該變量)情況下,這兩種查詢行為是不一樣的。

LHS 和 RHS 查詢都會(huì)在當(dāng)前執(zhí)行作用域中開始,沿著作用域鏈向上查找,直到全局作用域。

而不成功的 RHS 會(huì)拋出 ReferenceError ,不成功的 LHS 會(huì)自動(dòng)隱式地創(chuàng)建一個(gè)全局變量(非嚴(yán)格模式),并作為 LHS 的查詢結(jié)果(嚴(yán)格模式也會(huì)拋出 ReferenceError)。

那么,復(fù)習(xí)一下作用域鏈的查找吧。

4. 執(zhí)行上下文和作用域鏈

在 js 中有三種代碼運(yùn)行環(huán)境:

  • 全局執(zhí)行環(huán)境
  • 函數(shù)執(zhí)行環(huán)境
  • Eval 執(zhí)行環(huán)境

js 代碼執(zhí)行的時(shí)候,為了區(qū)分運(yùn)行環(huán)境,會(huì)進(jìn)入不同的執(zhí)行上下文(Execution context,EC),這些執(zhí)行上下文會(huì)構(gòu)成一個(gè)執(zhí)行上下文棧(Execution context stack,ECS)。

對(duì)于每個(gè) EC 都有一個(gè)變量對(duì)象(Variable object,VO),作用域鏈(Scope chain)和 this 三個(gè)主要屬性。

4.1. VO

變量對(duì)象(Variable object)是與 EC 相關(guān)的作用域,存儲(chǔ)了在 EC 中定義的變量和函數(shù)聲明。VO 中一般會(huì)包含以下信息:

  • 變量
  • 函數(shù)聲明式
  • 函數(shù)的形參

而函數(shù)表達(dá)式和沒有用 varlet、const 聲明的變量(全局變量,存在于全局 EC 的 VO)不存在于 VO 中。對(duì)于全局 VO,有 VO === this === global。

4.2. AO

在函數(shù) EC 中,VO 是不能直接訪問的,此時(shí)由激活對(duì)象(Activation Object,AO)來替代 VO 的角色。AO 是在進(jìn)入函數(shù) EC 時(shí)被創(chuàng)建的,它通過函數(shù)的 arguments 進(jìn)行初始化。這時(shí),VO === AO。

如:

function foo(b) {
? ? const a = 1;
}
foo(1);
// AO:{ arguments: {...}, a: 1, b: 1 }

4.3. 作用域鏈

了解了 EC、AO、VO,再來看作用域鏈

var x = 10;
function foo() {
? ? var y = 20;
? ??
? ? function bar() {
? ? ? ? var z = 30;
? ? ? ?
? ? ? ? console.log(x + y + z);
? ? };
? ??
? ? bar()
};
foo();

  • 橙色箭頭指向 VO/AO
  • 藍(lán)色箭頭們則是作用域鏈(VO/AO + All Parent VO/AOs)

理解了查找過程,很容易想到 js 中的原型鏈,而作用域鏈和原型鏈則可以組合成一個(gè)二維查找:先通過作用域鏈查找到某個(gè)對(duì)象,再查找這個(gè)對(duì)象上的屬性。

const foo = {}
function bar() {
? ? Object.prototype.a = 'Set foo.a from prototype';
? ? returnfunction () {
? ? ? ? console.log(foo.a);
? ? }
}
bar()();?
// Set foo.a from prototype

到此這篇關(guān)于由 JavaScript 的 with 引發(fā)的探索的文章就介紹到這了,更多相關(guān) JavaScript 的 with 引發(fā)的探索內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論