JavaScript 作用域链解析
JavaScript 中有 Scope( 作用域 ) , Scope chain( 作用域链 ) , Execute context( 执行上下文 ) , Active Object ( 活动对象 ),Dynamic Scope( 动态作用域 ) , Closure( 闭包 ) 这些概念,要理解这些概念,我们从静态和动态两个方面去分析一下。
首先我们写一个简单的 function 来做一个例子:
function add(num1, num2){
var sum = num1 + num2;
return sum;
}
我们定义了一个具有两个形参的 add 函数。
静态方面:
当创建 add 函数的时候, Javascript 引擎会创建 add 函数的 Scope chain, 这个作用域链指向了 Global Context( 全局上下文 ) 。如果用图形形象化的表述如下图所示:
从上图可以看出,当 add 函数创建的时候,作用域链就已经创建了,因此可以得出一个结论,函数的作用域链是创建函数的时候就已经创建了,而不是动态运行期。下面就来看看动态运行期的时候会发生什么事情。
动态方面:
当执行 add 函数的时候, JavaScript 会创建一个 Execute context (执行上下文),执行上下文中就包含了add 函数运行期所需要的所有信息。 Execute context 也有自己的 Scope chain, 当函数运行的时候, JavaScript引擎会首先从用 add 函数的作用域链来初始化执行上下文的作用域链,然后 JavaScript 引擎又会创建一个 Active Object, 这个对象里面包含了函数运行期的所有局部变量,参数以及 this 等变量。
如果形象的描述 add 函数动态运行期会发生什么,可以用如下图来描述:
从上图可以看出,执行上下文是一个动态的概念,它是当函数运行的时候创建的,同时 Active Object 对象也是一个动态的概念,它是被执行上下文的作用域链引用的。因此可以得出一个结论:执行上下文和活动对象都是动态概念,并且执行上下文的作用域链是由函数作用域链初始化的。
上面说了函数作用域和执行上下文作用域,下面接着说一下动态作用域的问题,当在 JavaScript 通过 with 语句, try-catch 的 catch 子句,以及 eval 方法的时候, JavaScript 引擎就会动态的改变执行上下文的作用域。下面还是通过一个例子来看看:
- function initUI(){
- with (document){ //avoid!
- var bd = body,
- links = getElementsByTagName("a"),
- i= 0,
- len = links.length;
- while(i < len){
- update(links[i++]);
- }
- getElementById("go-btn").onclick = function(){
- start();
- };
- bd.className = "active";
- }
当执行上面的 initUI 函数的时候, JavaScript 会动态的创建一个 with 语句对应的作用域放到执行上下文作用域链的最前端,通过下图可以形象的描述上述过程,下图红色标注的区域就显示了 with 语句产生的作用域。
最后,我们来看看 JavaScript 最神秘的 Closure (闭包),闭包在 JavaScript 其实就是一个函数,闭包是在函数运行期被创建的,下面还是以一个实例来看看:
- function assignEvents(){
- var id = "xdi9592";
- document.getElementById("save-btn").onclick = function(event){
- saveDocument(id);
- };
- }
当上面的 assignEvents 函数被执行的时候,会创建一个闭包,而这个闭包会引用 assignEvents 作用域中的 id 变量,如果按照传统的编程语言的方式, id 是存储在堆栈上的一个变量,当函数执行完了以后 id 就消失,那么怎么可能再次引用呢?显然这里 JavaScript 采用了另外的方式。下面就来看看 JavaScript 是如何来实现闭包的。当执行 assignEvents 函数的时候, JavaScript 引擎会创建assignEvents函数执行上下文的作用域链,这个作用域链包含了 assignEvents 执行时的活动对象,而同时 JavaScript 引擎也会创建一个闭包,而闭包的作用域链也会引用assignEvent 执行时候的活动对象,这样当 assignEvents 执行完的时候,虽然它本身执行上下文的作用域链不再引用活动对象了,但是闭包还是引用着 assignEvents 运行期对应的活动对象,这就解释了 JavaScipt 内部的闭包机制。可以用下图形象的表述上面 assignEvents 函数运行期的情形:
从上面可以看出,当 assignEvents 函数执行完毕以后, document.getElementById("save-btn").onclick 引用了闭包,这样当用户点击 save-btn 的时候,就会触发闭包的执行,那么下面就来看看闭包执行时的情形。前面也说了 JavaScript 中闭包其实就是函数,因此闭包执行和函数执行时的情形是一致的,通过下图来形象的描述上述onclick 事件所关联的闭包。
从上图可以看出 JavaScript 引擎首先创建了闭包的执行上下文,然后用闭包作用域链来初始化闭包的执行上下文作用域链,最后再将闭包执行时对应的活动对象放入到作用域的最前端,这也进一步验证了闭包就是函数的论断。
JavaScript 作用域链解析的更多相关文章
- JavaScript作用域链的理解
前言 作用域是JavaScript一个很重要的概念,想要学好JavaScript就需要理解javascript作用域和作用域链的工作原理.这篇文章对JavaScript作用域链和作用域链做一个简单的介 ...
- JavaScript 作用域链图具体解释
<script type="text/javascript"> /** * 作用域链: */ var a = "a"; function hao94 ...
- JavaScript中作用域和作用域链解析
学习js,肯定要学习作用域,js作用域和其他的主流语言的作用域还存在很大的区别. 一.js没有块级作用域. js没有块级作用域,就像这样: if(){ : console.log(a) //输出100 ...
- JavaScript作用域链
之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时 ...
- 个人理解的javascript作用域链与闭包
闭包引入的前提个人理解是为从外部读取局部变量,正常情况下,这是办不到的.简单的闭包举例如下: function f1(){ n=100; function f2(){ alert(n); } retu ...
- javascript作用域链学习笔记
作用域链 "JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里." --权威指南 在JavaScript中,一切皆对象,包括函数.函数对象和其它对象 ...
- JavaScript作用域链详解
JavaScript的作用域链还是很有味道的,搞懂了这个知识点,闭包的问题也就迎刃而解咯 1.JavaScript的全局变量和局部变量 首先,先来看看js的全局变量和局部变量,js不是块级作用域,所以 ...
- [JavaScript] JavaScript作用域深度解析
JavaScript作用域 JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里. -- JS权威指南 在JS里,一切皆对象,函数也是. 一.有什么用 什么时候会用到它? ...
- JavaScript作用域链和垃圾回收机制
作用域链 基本概念: 在了解作用域链和内存之前,我们先了解两个概念,分别是执行环境和变量对象. 执行环境:定义变量或者函数有权访问的其他数据,决定了它们各自的行为.每个对象都有自己的执行环境. 变量对 ...
随机推荐
- 如何使你的Android应用记住曾经使用过的账户信息
原文:http://android.eoe.cn/topic/android_sdk 当您记住他们的名字时,每个人都会很喜欢.最简单的一个例子,您能够做的,让您的应用更加受人喜爱的,最有效的方法是记住 ...
- 解决git pull时出现的几个问题
第1个问题: 解决GIT代码仓库不同步 今天在执行git pull时出现: 解决方法:执行git checkout -f,然后再执行git pull重新checkout 再执行git pull时就可以 ...
- Android Studio 1.1.0 向导页(首页) 解析,以及版本控制 (SVN 和 GIT 的检出)
使用Android Studio首先要理清楚, Android Studio 的 project 相当于Eclipse的 Workspace Android Studio 的 module 相当于E ...
- eclipse下SVN subclipse插件
本文目的 让未使用过版本控制器软件或者未使用过subversion软件的人员尽快上手. subversion的使用技巧很多,这里只总结了最小使用集,即主要的基本功能,能够用来应付日常工作. 因此不涉及 ...
- jstypeof方法判断undefined类型
有关js判断undefined类型,使用typeof方法,typeof 返回的是字符串,其中就有一个是undefined. js判断undefined类型if (reValue== undefined ...
- 浏览器自动转换URL编码产生乱码问题
如果网址中含有汉字,浏览器会自动将其转换成URL编码,而在接收端,它又自动转换回来. 大部分时候都没什么问题,但有时候就很讨厌.因为生成这种URL编码,编码时的字符集和解码字符集不一定相同,比如编码时 ...
- CAS无锁实现原理以及ABA问题
CAS(比较与交换,Compare and swap) 是一种有名的无锁算法.无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(N ...
- .NET MVC5+ EF+AutoFac自动注入框架
1.创建一个MVC系统 VIEW显示页面代码: <link href="~/Content/bootstrap.css" rel="stylesheet" ...
- Specular Aliasing与Specular Leaking
最近做高质量实时HDR PBR渲染中碰到了2个关键问题,若干思考如下: 问题1: 极高的动态范围HDR+高级BRDF+相对较低的采样率(比方说不考虑子像素的原始分辨率),在这3项因素的综合作用下,Sp ...
- 搭建kafka源码开发环境时使用"gradle idea"命令构建源码失败
我的环境: JDK: 1.8.0_131 Gradle: Gradle 3.1 Kafka源码包: kafka-0.10.0.1-src.tgz Zookeeper安装包: zookeeper-3.4 ...