中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

由JavaScript的with?引發的探索是怎樣的

發布時間:2022-01-07 11:10:41 來源:億速云 閱讀:152 作者:柒染 欄目:開發技術

本篇文章給大家分享的是有關由JavaScript的with 引發的探索是怎樣的,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

1. 背景

某天吃飯的時候突然想到,都說 with 會有問題,那么是什么問題,是怎樣導致的呢?知其然不知其所以然,在好奇心的驅使下,從 with 出發,一路追溯到 VO、AO。那么先來復習一下 with 是干嘛的吧。

2. with

js 的 with 是為對象訪問提供命名空間式的訪問方式,with 創建一個對象的命名空間,在這個命名空間內你可以直接訪問對象的屬性,而不需要通過對象來訪問:

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
    }
}

嗯,他還有作用域鏈的性質。但是,如果給不存在的屬性賦值,將會沿著作用域鏈給該變量賦值,在沒有找到變量時,非嚴格模式下將會自動在全局創建一個變量。這就導致了數據泄露。

那么導致數據泄露的原因是什么呢?這需要了解 LHS 查詢,這個待會再說。

那來看看 js 是怎么查詢的:當 with 對象 o 的時候,with 聲明的作用域是 o,從這里對 c 進行 LHS 查詢。o 的作用域和全局作用域都沒有找到 c,在非嚴格模式下,失敗的 LHS 會自動隱式的在全局創建一個標識符 c,如果是嚴格模式,則會拋出 ReferenceError

2.1. with 的性能問題

使用 with:

function f () {
    const o = { a: 1 }
    console.time();

    with(o) {  
        for (let i = 0; i < 100000; i++) {
            a = 2;
        }
    }

    console.timeEnd();
}

正常情況:

function f () {
    const o = { a: 1 }
    console.time();

    for (let i = 0; i < 100000; i++) {
        o.a = 2;
    }

    console.timeEnd();
}

將近 10 倍的差距。

原因是什么呢?

js 預編譯階段會進行的優化,由于 with 創建新的詞法作用域,導致 o 的 a 屬性和 o 分離開位于兩個不同的作用域,不能快速找到標識符,引擎將不會做任何優化。

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

3. LHS 和 RHS

  • LHS:賦值操作的目標是誰

  • RHS:誰是賦值操作的源頭

所以我們來看這段代碼:

var a = 1;

在執行的時候,這段代碼會被拆成兩部分

var a;
a = 1;

當我們使用 a

console.log(a);

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

當然,更直白的 LHS 也如上文寫到的 a = 1

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

LHS 和 RHS 查詢都會在當前執行作用域中開始,沿著作用域鏈向上查找,直到全局作用域。

而不成功的 RHS 會拋出 ReferenceError ,不成功的 LHS 會自動隱式地創建一個全局變量(非嚴格模式),并作為 LHS 的查詢結果(嚴格模式也會拋出 ReferenceError)。

那么,復習一下作用域鏈的查找吧。

4. 執行上下文和作用域鏈

在 js 中有三種代碼運行環境:

  • 全局執行環境

  • 函數執行環境

  • Eval 執行環境

js 代碼執行的時候,為了區分運行環境,會進入不同的執行上下文(Execution context,EC),這些執行上下文會構成一個執行上下文棧(Execution context stackECS)。

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

4.1. VO

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

  • 變量

  • 函數聲明式

  • 函數的形參

而函數表達式和沒有用 varletconst 聲明的變量(全局變量,存在于全局 EC 的 VO)不存在于 VO 中。對于全局 VO,有 VO === this === global。

4.2. AO

在函數 EC 中,VO 是不能直接訪問的,此時由激活對象(Activation ObjectAO)來替代 VO 的角色。AO 是在進入函數 EC 時被創建的,它通過函數的 arguments 進行初始化。這時,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

  • 藍色箭頭們則是作用域鏈(VO/AO + All Parent VO/AOs)

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

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

以上就是由JavaScript的with 引發的探索是怎樣的,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

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

AI

西乌珠穆沁旗| 聊城市| SHOW| 天长市| 休宁县| 开化县| 青阳县| 望城县| 新余市| 沙田区| 乐陵市| 凉山| 庆安县| 六盘水市| 玉山县| 锡林郭勒盟| 绥江县| 闻喜县| 巢湖市| 雅江县| 甘洛县| 澎湖县| 馆陶县| 玉环县| 尤溪县| 宁波市| 普陀区| 五寨县| 元江| 阜康市| 萨迦县| 盐池县| 台中市| 永寿县| 陆川县| 兰溪市| 塘沽区| 西宁市| 民县| 贡嘎县| 临安市|