面对各种各样的JavaScript代码,我们有时候难免会犯错。可当自己仔细研究一下,哦原来是这么回事。有时候怎么会想为什么Javascript程序会是这样执行的呢?为什么没有得到自己预期的答案呢?自己到底是哪一步想错了呢?这时候就想如果我是JS执行流我会怎么执行?仔细想想这些问题,原来这其中还包含着很多自己不熟悉的知识点,今天就来总结下,同时也好加深印象。

首先我们来熟悉几个名称:

  • 执行上下文栈(Execution Context Stack)

    每一种代码的执行都需要依赖自身的上下文。函数的每一次调用,都会进入函数执行中的上下文,并且来计算函数中变量等的值。一个执行上下文可以激活另一个上下文,就好比一个函数调用了另一个函数(或者全局的上下文调用了一个全局函数),然后一层一层调用下去。逻辑上来说,这种实现方式是栈,我们可以称之为上下文堆栈。

    激活其它上下文的某个上下文被称为 调用者(caller) 。被激活的上下文被称为被调用者(callee) 。当一个caller激活了一个callee,那么这个caller就会暂停它自身的执行,然后将控制权交给这个callee. 于是这个callee被放入堆栈,称为进行中的上下文[running/active execution context]. 当这个callee的上下文结束之后,会把控制权再次交给它的caller,然后caller会在刚才暂停的地方继续执行。在这个caller结束之后,会继续触发其他的上下文。一个callee可以用返回(return)或者抛出异常(exception)来结束自身的上下文。

    当一段程序开始时,会先进入全局执行上下文环境[global execution context], 这个也是堆栈中最底部的元素。此全局程序会开始初始化,初始化生成必要的对象[objects]和函数[functions]. 在此全局上下文执行的过程中,它可能会激活一些方法(当然是已经初始化过的),然后进入他们的上下文环境,然后将新的元素压入堆栈。在这些初始化都结束之后,这个系统会等待一些事件(例如用户的鼠标点击等),会触发一些方法,然后进入一个新的上下文环境。ECMAScript运行时系统就是这样管理代码的执行。
  • 执行上下文(Execution Context)

    一个执行的上下文可以抽象的理解为object。每一个执行的上下文都有一系列的属性(变量对象(variable object),this指针(this value),作用域链(scope chain) )
  • 变量对象(variable object)

    变量对象是与执行上下文相关的 数据作用域(scope of data) 。它是与上下文关联的特殊对象,用于存储被定义在上下文中的变量(variables)和函数声明(function declarations) 。
  • 活动对象(activation object)

    在一个函数上下文中,变量对象被表示为活动对象。它包含变量(variables)和函数声明(function declarations) 普通参数(形参) 与特殊参数(arguments)对象(具有索引属性的参数映射表)。

知道了这几个名称后我们拿几个实例来分一下Javascript到底是如何执行的?

if (!("a" in window)) {
var a = 1;
}
alert(a);

这里如果不加思考,想当然的认为如果window不包含属性a,就声明一个变量a,然后赋值为1。所以答案为1。

可是正确答案是undefined,再仔细想想为什么?如果你就是JS程序你会如何运行?

详细分析:

当一段程序开始时,会先进入全局执行上下文环境并初始化生成必要的对象,在全局执行上下文环境中申明了变量a,根据变量声明提升,此时该上下文环境中的变量对象VO = {a:undefined}(全局上下文中VO还包括其他对象如Math,Date等对此题不构成影响,暂不考虑)。全局上下文环境初始化好后开始执行代码,首先判断变量a是否属于window对象,我们知道变量a已经在window对象中,所以,if条件语句不会被执行,直接执行alert,

该题可简化为:

var a;
if (!("a" in window)) {
a = 1;
}
alert(a); //undefined

第二题

function a(x) {
return x * 2;
}
var a;
alert(a);

我们知道变量对象中及包括函数声明也包括变量申明,在此题中有同名的函数和变量,此时函数声明会覆盖变量声明。但不会覆盖已经赋值的同名变量声明。所以此题中最后结果是函数

第三题

function b(x, y, a) {
arguments[2] = 10;
alert(a);
}
b(1, 2, 3);

在此题中,b函数被激活会开辟一个新的内存栈并添加到执行上下文栈中,执行流进入b函数上下文,此时变量对象表示为活动对象

AO = {

x : 1,

y : 2,

a : 3,

arguments:{0:1,1:2,2:3}

}

Arguments对象是活动对象的一个属性,它包括如下属性:

  1. callee — 指向当前函数的引用
  2. length — 真正传递的参数个数
  3. properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length. properties-indexes的值和实际传递进来的参数之间是共享的。这个共享其实不是真正的共享一个内存地址,而是2个不同的内存地址,使用JavaScript引擎来保证2个值是随时一样的.

所以当代码执行到arguments[2] = 10;时a也会变为10。

参考文献:

Javascript执行流总结的更多相关文章

  1. 一份最贴近真实面试的Java基础面试题

    这是一份Java基础知识的面试题.在网上的关于Java的面试题数不胜数,但认真看过感觉大多数都没有实用性,有很多是面试官根本就不会问到的,那些已经脱离了实际开发的技术问题.而这份资料来源自一份个人觉得 ...

  2. 一份贴近真实面试的Java面试题(基础部分)

    这是一份关于Java基础的面试题.在网上的关于Java的面试题数不胜数,但本人认真看过后觉得大多数都没有实用性,有很多是面试官根本就不会问到的,企业根本不会用到的,一些已经脱离了实际开发的技术问题.而 ...

  3. Javascript 的执行环境(execution context)和作用域(scope)及垃圾回收

    执行环境有全局执行环境和函数执行环境之分,每次进入一个新执行环境,都会创建一个搜索变量和函数的作用域链.函数的局部环境不仅有权访问函数作用于中的变量,而且可以访问其外部环境,直到全局环境.全局执行环境 ...

  4. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  5. 《高性能javascript》一书要点和延伸(上)

    前些天收到了HTML5中国送来的<高性能javascript>一书,便打算将其做为假期消遣,顺便也写篇文章记录下书中一些要点. 个人觉得本书很值得中低级别的前端朋友阅读,会有很多意想不到的 ...

  6. javascript的垃圾收集机制

    × 目录 [1]原理 [2]标记清除 [3]引用计数[4]性能问题[5]内存管理 前面的话 javascript具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存.在编写javascri ...

  7. 深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域

    × 目录 [1]图示 [2]概念 [3]说明[4]总结 前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关 ...

  8. JavaScript作用域链

    之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时 ...

  9. Professional JavaScript for Web Developers 3rd Edition ---读书笔记

    1. DOMContentLoaded DOM树构建完成时触发该事件 load 页面加载完毕触发 原生js document.addEventListener('DOMContentLoaded', ...

随机推荐

  1. Bash重定向

    1. 基础知识 文件描述符(File Descriptor),是进程对其所打开文件的索引,形式上是个非负整数.类 Unix 系统中,常用的特殊文件描述符如下: 文件描述符 名称 常用缩写 默认值 0 ...

  2. Spring Boot + Redis

    启动redis docker run --name redisServer -P -d redis redis自带客户端,启动客户端 docker run -it --link redisServer ...

  3. CodeSmith读取数据库

    这两天在看CodeSmith文档,因为官方文档在读数据库这一篇使用的是VB写的,对于C#使用者来说看起来很不方便,所以我改成C#的,顺便写下我自己的使用过程. 首先,要使用CodeSmith连接数据库 ...

  4. Javac中的方法

    例1: interface IA{ void m(int a); } abstract class AC implements IA{ // 这个抽象方法覆盖了 IA中的方法m public abst ...

  5. Vue源码翻译之渲染逻辑链

    本篇文章主要要记录说明的是,Vue在Vdom的创建上的相关细节.这也是描绘了Vue在界面的创建上的一个逻辑顺序,同时我也非常拜服作者编码的逻辑性,当然或许这么庞大复杂的编码不是一次性铸就的,我想应该也 ...

  6. 高可用的MongoDB集群-实战篇

    1.概述 最近有同学和网友私信我,问我MongoDB方面的问题:这里我整理一篇博客来赘述下MongoDB供大家学习参考,博客的目录内容如下: 基本操作 CRUD MapReduce 本篇文章是基于Mo ...

  7. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(七):集成 Druid 数据源

    数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏 ...

  8. 面试题----gcc的编译流程

    gcc编译流程 一.    编译与处理指令: gcc -E hello.c -o a.c 如果不使用-o指定输出的文件,会默认输出到终端.所以建议使用同时使用-o选项. 还要注意:编译时会保留#pra ...

  9. MVC应用程序,动态创建单选列表(RadioButtonList)

    单选列表是多个选项,让用户选择一个.MVC应用程序开发中,少之不了.下面就来练习之个小功能. 这个练习,Insus.NET想实现一个日期显示的格式.在MVC中,得需从model开始创建: 再创建一个E ...

  10. [C#]System.Timers.Timer(2)

    摘要 之前学习过c#中定时器Timer的基本用法,在使用过程中,有一个问题,一直困扰着自己,就是在初始化定时器的时候,如果设置的interval过小,或者每次执行的业务非常耗时的时候,这时候该怎么处理 ...