搜尋
首頁web前端js教程javascript作用域鏈(Scope Chain)用法實例解析_javascript技巧

本文實例分析了javascript作用域鏈(Scope Chain)用法。分享給大家參考,具體如下:

關於js的作用域鏈,早有耳聞,也曾看過幾篇介紹性的博文,但一直都理解的模棱兩可。近日又精心翻看了一下《悟透Javascript》這本書,覺得寫得太深刻,在「程式碼的時空」一節裡有一段介紹作用域鏈的地方寥寥數語,回味無窮(其實還是理解的模稜兩可^_^)。現在整理下自己的讀書筆記,順便藉鏡網路資源,寫下來。

一、從一個簡單的問題說起

下面的js程式碼在頁面中運行顯示什麼結果:

var arg = 1;
function fucTest(arg) {
  alert(arg);
  var arg = 2;
  //alert(arg);
}
fucTest(10);

您的答案是什麼?沒錯,就是彈出10。我的理解是這樣的,funTest函式有一個形參arg,funTest函式傳入實參10,alert方法把10彈出就是了,囧。
好,問題又來了:

var arg = 1;
function funcTest() {
  alert(arg);
  var arg = 2;
}
arg = 10;
funcTest();

答案是什麼?如果是5年前的我,一定不會再往下想了,還是10!這麼簡單的問題還用想什麼呀?我的理解是這樣的:funTest函數是一個無參數的函數,函數內部透過alert方法,呼叫外部(全域)的變數arg,在函數執行前,arg賦值為10,彈出arg值後改變arg值為2 ,所以彈出值為10。

真的是10嗎?是還是不是?

測試的結果:彈出「undefined」,瀑布汗.

二、理解作用域鏈,從javascript運作機制說起

1、js的運行順序

如果一個文檔流中包含多個script代碼段(用script標籤分隔的js代碼或引入的js文件),它們的運行順序是:

步驟1. 讀入第一個程式碼段(js執行引擎並非一行一行地執行程序,而是一段一段地分析執行的)
步驟2. 做語法分析,有錯則報語法錯誤(例如括號不符等),並跳到步驟5
步驟3. 對var變數和function定義做「預解析」(永遠不會報錯的,因為只解析正確的宣告)
步驟4. 執行程式碼段,有錯則報錯(如變數未定義)
步驟5. 如果還有下一個程式碼段,則讀入下一個程式碼段,重複步驟2
步驟6. 結束

上面的分析已經足夠清楚,步驟二、三和步驟四里的紅色字體可能是我們新手理解上的一個盲點,尤其是步驟三的“預解析”,如果不清楚什麼叫預解析,總覺得不踏實。而步驟四的「有錯則報錯」也是常碰到的。舉例來說:

function funcTest() {
  alert(arg);
  var arg = 2;
}
funcTest();

上面这段代码执行时,弹出“undefined”,也就是说arg没有定义,js的变量不是不用定义也可以吗?

2、语法分析和“预解析”

(1)、从解释型语言的编译过程说起

众所周知,javascript是解释型语言,它不同于c#和java等编译型语言。对于传统编译型语言来说,编译步骤分为:词法分析、语法分析、语义检查、代码优化和字节生成;但对于解释型语言来说,通过词法分析和语法分析得到语法树后,就可以开始解释执行了。

a、词法分析

简单地说,词法分析是将字符流(char stream)转换为记号流(token stream)。
但是这个转换过程并不是可以用一句话就可以概括的那么简单,我们可以试着用伪代码理解一段简单的程序:

代码var result=x-y;的转换大致可以表示如下:

NAME "result"
EQUALS
NAME "x"
MINUS
NAME "y"
SEMICOLON

b、语法分析

简单地说,语法分析就是为了构造合法的语法分析树,而语法分析树可以直观地表示出推导的过程。

那么什么是语法分析树?简单地说,就是程序推导过程的描述。但是到底什么是语法树,请参考专业文章,本篇略过。

c、其他

通过语法分析,构造出语法分析树后,接下来还可能需要进一步的语义检查。对于传统强类型语言来说,语义检查的主要部分是类型检查,比如函数的实参和形参类型是否匹配等等。
结论:通过上面的分析可以看出,对于javascript引擎来说,肯定有词法分析和语法分析,之后可能还有语义检查、代码优化等步骤,等这些编译步骤完成之后(任何语言都有编译过程,只是解释型语言没有编译成二进制代码),才会开始执行代码。

(2)、执行过程

a、javascript的作用域机制

通过编译,javascript代码已经翻译成了语法树,然后会立刻按照语法树执行。

进一步的执行过程,需要理解javascript的作用域机制:词法作用域(lexcical scope)。通俗地讲,就是javascript变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,编译器通过静态分析就能确定,因此词法作用域也叫做静态作用域(static scope)。但需要注意,with和eval的语义无法仅通过静态技术实现,所以只能说javascript的作用域机制非常接近词法作用域(lexical scope).

javascript引擎在执行每个函数实例时,都会创建一个执行环境(execution context)。执行环境中包含一个调用对象(call object), 调用对象是一个scriptObject结构(scriptObject是与函数相关的一套静态系统,与函数实例的生命周期保持一致),用来保存内部变量表varDecls、内嵌函数表funDecls、父级引用列表upvalue等语法分析结构(注意varDecls和funDecls等信息是在语法分析阶段就已经得到,并保存在语法树中。函数实例执行时,会将这些信息从语法树复制到scriptObject上)。

b、javascript作用域机制的实现方法

词法作用域(lexical scope)是javascript的作用域机制,还需要理解它的实现方法,就是作用域链(scope chain)。作用域链是一个name lookup机制,首先在当前执行环境的scriptObject中寻找,没找到,则顺着upvalue到父scriptObject中寻找,一直lookup到全局调用对象(global object)。

现在回过头来分析第二个问题:

var arg = 1;
function funcTest() {
  alert(arg);
  var arg = 2;
}
arg = 10;
funcTest();

在执行funcTest函数时,也即进入了funcTest对应的作用域,js引擎在执行时,当遇到对变量名或者函数名的使用时,会首先在当前作用域(也即funcTest对应的作用域)查找变量或者函数(显然,arg变量在funcTest对应的作用域里被定义为var arg=2 所以alert方法的参数采用的是当前作用域的arg,但是因为arg被定义在alert方法后,所以arg变量默认值为undefined)。当然,如果没有找到就到上层作用域查找,依此类推(作用域范围可以持续到javascript运行环境的根:window对象)。

最后,让你看的更清楚,上面的代码其实可以等价于:

var arg = 1;
function funcTest() {
  var arg; //默认值undefined
  alert(arg);
  arg = 2;
}
arg = 10;
funcTest();

c、閉包(closure)

當一個函數實例執行時,會建立或關聯到一個閉包。 (關於閉包,打算另寫一篇學習筆記)

scriptObject用來靜態保存與函數相關的變數表,而閉包則在執行期間動態保存這些變數表及其運行值;

閉包的生命週期有可能比函數實例長。函數實例在活動引用為空後會自動銷毀;

閉包則要等要資料引用為空後,由javascript引擎回收(有些情況下不會自動回收,就導致了記憶體洩漏)。

ps:關於「執行過程」這一段比較拗口,名詞很多,不過別被它們嚇住,一旦理解了執行環境(execution context)、調用對象(call object)、詞法作用域(lexical scope)、作用域鏈(scope chain)、閉包(closure)等這些概念,javascript的許多現像都能迎刃而解。

三、結語

透過第二段的分析,對照第一段筆者曾經做出的判斷(你是不是也覺得筆者曾經的分析和結論很幼稚(哪怕有時結果碰巧也對!)?!不是一般的膚淺啊,^_^),你會發現原來javascript還有這麼多“玄機”,而要真正理解精通又談何容易?先「悟透」再說吧。

希望本文所述對大家JavaScript程式設計有所幫助。

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

JavaScript的Symbol类型、隐藏属性及全局注册表详解JavaScript的Symbol类型、隐藏属性及全局注册表详解Jun 02, 2022 am 11:50 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

原来利用纯CSS也能实现文字轮播与图片轮播!原来利用纯CSS也能实现文字轮播与图片轮播!Jun 10, 2022 pm 01:00 PM

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

JavaScript面向对象详细解析之属性描述符JavaScript面向对象详细解析之属性描述符May 27, 2022 pm 05:29 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

整理总结JavaScript常见的BOM操作整理总结JavaScript常见的BOM操作Jun 01, 2022 am 11:43 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach是es6里的吗foreach是es6里的吗May 05, 2022 pm 05:59 PM

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
1 個月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)