成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JS學習系列 02 - 詞法作用域

bladefury / 1907人閱讀

摘要:作用域有兩種主要工作模型詞法作用域和動態(tài)作用域??赡軙幸恍┩瑢W認為是,那就是沒有搞清楚詞法作用域的概念。在嚴格模式下,在運行時有自己的詞法作用域,意味著其中的聲明無法修改所在的作用域。

1. 兩種作用域

“作用域”我們知道是一套規(guī)則,用來管理引擎如何在當前作用域以及嵌套的子作用域中根據(jù)標識符名稱進行變量查找。

作用域有兩種主要工作模型:詞法作用域動態(tài)作用域。

大多數(shù)語言采用的都是詞法作用域,少數(shù)語言采用動態(tài)作用域(例如 Bash 腳本),這里我們主要討論詞法作用域。

2. 詞法

大部分標準語言編譯器的第一個工作階段叫作詞法化。
簡單地說,詞法作用域是由你在寫代碼時將變量和函數(shù)(塊)作用域?qū)懺谀睦飦頉Q定的。當然,也會有一些方法來動態(tài)修改作用域,后邊我會介紹。

舉個例子:

var a = 2;

function foo1 () {
   console.log(a);
}

function foo2 () {
   var a = 10;

   foo1();
}

foo2();

這里輸出結果是多少呢?

注意,這里結果打印的是 2

可能會有一些同學認為是 10,那就是沒有搞清楚詞法作用域的概念。
前邊介紹了,詞法作用域只取決于代碼書寫時的位置,那么在這個例子中,函數(shù) foo1 定義時的位置決定了它的作用域,通過下圖理解:

foo1 和 foo2 都是分別定義在全局作用域中的函數(shù),它們是并列的,所以在 foo1 的作用域鏈中并不包含 foo2 的作用域,雖然在 foo2 中調(diào)用了 foo1,但是 foo1 對變量 a 進行 RHS 查詢時,在自己的作用域沒有找到,引擎會去 foo1 的上級作用域(也就是全局作用域)中查找,而并不會去 foo2 的作用域中查找,最終在全局作用域中找到 a 的值為 2。

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

3. 欺騙詞法

JavaScript 中有 3 種方式可以用來“欺騙詞法”,動態(tài)改變作用域。

第一種: eval

JavaScript 中 eval(...) 函數(shù)可以接受一個字符串作為參數(shù),并將其中的內(nèi)容視為好像在書寫時就存在于程序中這個位置的代碼。

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

舉個例子:

function foo (str) {
   eval(str);        // "欺騙"詞法

   console.log(a);
}

var a = 2;

foo("var a = 10;");

如大家所想,輸出結果為 10。
因為 eval("var a = 10;") 在 foo 的作用域中新創(chuàng)建了一個同名變量 a,引擎在 foo 作用域中對 a 進行 RHS 查詢,找到了新定義的 a,值為 10,所以不再向上查找全局作用域中的 a,所以導致輸出結果為 10,這就是 eval(...) 的作用。

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

"use strict;"

function foo (str) {
   eval(str);        // eval() 有自己的作用域,所以并不會修改 foo 的詞法作用域

   console.log(a);
}

var a = 2;

foo("var a = 10;");

這里輸出結果為 2。

JavaScript 中還有一些功能和 eval(...) 類似的函數(shù),例如 setTimeout(...) 和 setInterval(...) 的第一個參數(shù)可以是一個字符串,字符串的內(nèi)容可以解釋為一段動態(tài)生成的代碼。這些功能已經(jīng)過時并且不被提倡,最好不要使用它們。new Function(...) 函數(shù)的最后一個參數(shù)也可以接受代碼字符串,并將其轉(zhuǎn)化為動態(tài)生成的函數(shù),也盡量避免使用。

在程序中動態(tài)生成代碼的使用場景非常罕見,因為它所帶來的好處無法抵消性能上的損失。

第二種: with
with 通常被當做重復引用同一個對象中的多個屬性的快捷方式,可以不需要重復引用對象本身。

舉個例子:

var obj = {
   a: 2,
   b: 3
};

with (obj) {
   console.log(a);      // 2
   console.log(b);      // 3
   c = 4;         
};

console.log(c);          // 4, c 被泄露到全局作用域上

如上所示,我們對 c 進行 LHS 查詢,因為在 with 引入的新作用域中沒有找到 c,所以向上一級作用域(這里是全局作用域)查找,也沒有找到,在非嚴格模式下,在全局對象中新建了一個屬性 c 并賦值為 4。

with 可以將一個沒有或有多個屬性的對象處理為一個完全隔離的詞法作用域,因此這個對象的屬性也會被處理為定義在這個作用域中的詞法標識符。

盡管 with 塊可以將一個對象處理為詞法作用域,但是這個塊內(nèi)部正常的 var 聲明并不會限制在這個塊作用域中,而是被添加到 with 所處的函數(shù)作用域中。

嚴格模式下,with 被完全禁止使用。

"use strict";

var obj = {
   a: 2,
   b: 3
};

with (obj) {
   console.log(a);     
   console.log(b);      
   c = 4;         
};

console.log(c);       

第三種: try...catch
try...catch 可以測試代碼中的錯誤。try 部分包含需要運行的代碼,而 catch 部分包含錯誤發(fā)生時運行的代碼。

舉個例子:

try {
   foo();
} catch (err) {
   console.log(err);   

   var a = 2; 
// 打印出 "ReferenceError: foo is not defined at :2:4"
}

console.log(a);      // 2

當 try 中的代碼出現(xiàn)錯誤時,就會進入 catch 塊,此時會把異常對象添加到作用域鏈的最前端,類似于 with 一樣,catch 中定義的局部變量也都會添加到包含 try...catch 的函數(shù)作用域(或全局作用域)中。

4. 性能

JavaScript 引擎會在編譯階段進行數(shù)項性能優(yōu)化。其中有些優(yōu)化依賴于能夠根據(jù)代碼的詞法進行靜態(tài)分析,并預先確定所有變量和函數(shù)定義的位置,才能在執(zhí)行過程中快速找到標識符。

但如果引擎在代碼中發(fā)現(xiàn)了 eval(...)、with 和 try...catch ,它只能簡單的假設關于標識符位置的判斷都是無效的,因為無法在詞法分析階段明確知道 eval(...) 會接受到什么代碼,這些代碼會如何對作用域進行修改,也無法知道傳遞給 with 用來創(chuàng)建新詞法作用域的對象的內(nèi)容到底是什么。

最悲觀的情況是如果出現(xiàn)了這些動態(tài)添加作用域的代碼,所有的優(yōu)化可能都是無意義的,因此最簡單的做法就是完全不進行任何優(yōu)化。

如果代碼中大量使用 eval(...) 和 with,那么運行起來一定會變得非常緩慢。

5. 結論

很多時候我們對代碼的分析出錯,就是源于對詞法作用域的忽略,所以讓我們重新審視代碼,繼續(xù)努力!

歡迎關注我的公眾號

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/81599.html

相關文章

  • 再談閉包-詞法作用

    摘要:權威指南第六版關于閉包的說明采用詞法作用域,也就是說函數(shù)的執(zhí)行依賴于變量的作用域,這個作用域是在函數(shù)定義時決定的,而不是函數(shù)調(diào)用時決定的。閉包這個術語的來源指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來是函數(shù)將變量包裹了起來。 最近打算換工作,所以參加了幾次面試(國內(nèi)比較知名的幾家互聯(lián)網(wǎng)公司)。在面試的過程中每當被問起閉包,我都會說閉包是作用域的問題?令人驚訝的是幾乎無一例外的當我提到...

    Ku_Andrew 評論0 收藏0
  • JS腳丫系列】重溫閉包

    摘要:內(nèi)部的稱為內(nèi)部函數(shù)或閉包函數(shù)。過度使用閉包會導致性能下降。,閉包函數(shù)分為定義時,和運行時。循環(huán)會先運行完畢,此時,閉包函數(shù)并沒有運行。閉包只能取得外部函數(shù)中的最后一個值。事件綁定種的匿名函數(shù)也是閉包函數(shù)。而對象中的閉包函數(shù),指向。 閉包概念解釋: 閉包(也叫詞法閉包或者函數(shù)閉包)。 在一個函數(shù)parent內(nèi)聲明另一個函數(shù)child,形成了嵌套。函數(shù)child使用了函數(shù)parent的參數(shù)...

    MartinDai 評論0 收藏0
  • JS學習系列 01 - 編譯原理和作用

    摘要:的抽象語法樹中可能如下圖所示代碼生成將轉(zhuǎn)換為可執(zhí)行代碼的過程被稱為代碼生成。如果是,編譯器會忽略該聲明,繼續(xù)進行編譯,否則它會要求在當前作用域的集合中聲明一個新的變量,并命名為。 在學習 javascript 的過程中,我們第一步最應該了解和掌握的就是作用域,與之相關還有程序是怎么編譯的,變量是怎么查找的,js 引擎是什么,引擎和作用域的關系又是什么,這些是 javascript 這門...

    jkyin 評論0 收藏0
  • 一分鐘學習JavaScript系列 -- 閉包

    摘要:此時,中定義的局部變量就被保存在內(nèi)存中。所以當執(zhí)行的時候,其真正的作用域是運行時的作用域運行時作用域詞法作用域所以第一次調(diào)用時,由于是,所以返回而第二次返回是。因此在使用閉包時,需要非常注意內(nèi)存泄漏的問題。 說起閉包,相信寫前端的同學都知道,而且相信在實際的項目中或多或少都已經(jīng)用到了閉包。那到底什么才是閉包,閉包又是怎么產(chǎn)生的呢? 1. 什么是閉包在阮老師的文章中提到: 閉包就是能夠讀...

    keke 評論0 收藏0
  • 【Step-By-Step】一周面試題深入解析 / 周刊02

    摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發(fā)布一道面試題。即使這個時間周期內(nèi),小明取得多次滿分。創(chuàng)建作用域鏈在執(zhí)行期上下文的創(chuàng)建階段,作用域鏈是在變量對象之后創(chuàng)建的。這種一層一層的關系,就是作用域鏈。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發(fā)布一道面試題。每個周末我會仔細閱讀大家的答...

    ixlei 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<