在做面试题之前,我们先搞清楚两个概念

  1. 执行上下文(execution context)
  2. 变量对象(variable object)

执行上下文

我们都知道JavaScript的作用域一共分三种

  • 全局作用域
  • 函数作用域
  • eval作用域

实际上每一次的函数调用都会有一个对应的执行环境,这个执行环境也叫做执行上下文。执行上下文是一个抽象的概念,函数每调用一次就会产生一个新的执行上下文。

下面我们通过一段代码来理解下执行上下文的顺序:

var fun0 = 'global context';
console.log(fun0); function fun1(){
console.log('fun1');
function fun2(){
console.log('fun2');
function fun3(){
console.log('fun3');
}
fun3();
}
fun2();
}
fun1(); // global context fun1 fun2 fun3

从上面代码结构我们可以看到在fun1内包含fun2,fun2中包含fun3,实际上是一层一层嵌套的。在代码执行的过程中,会先进入全局的fun0,当我们在调用fun1的时候控制权会从全局的fun0到fun1的这样一个执行上下文中,然后再调用fun2的时候控制权会从fun1到fun2,以此类推。当fun3调用结束以后会退回到fun2,当fun2调用结束之后会退回到fun1,当fun1调用结束后退回到fun0,等到整个代码都结束后就会依次输出global context fun1 fun2 fun3。这几个函数的每一次调用的时候都会产生一个对应的新的执行上下文。

变量对象

变量对象是一个抽象概念中的“对象”,不是JavaScript中真正意义上的对象。它用于存储执行上下文中函数声明、函数参数和变量。

VO按照如下的顺序存储:

  • 函数参数(若未传入,初始化该参数值为undefined)
  • 函数声明(若发生命名冲突,会覆盖)
  • 变量(初始化变量值为undefined,若发生命名冲突,会被忽略)

在函数中有点特殊的是:函数中有一个概念叫做激活对象(AO),AO就是在函数调用的时候会有一个特殊的arguments,arguments在初始化阶段会被放置到AO对象中,初始化之后AO对象又会被叫做VO对象。

下面我们来看一个例子:

function textVo(x,f){
var a = 1;
var b = 2;
function b(){};
function f(){};
var c = 'VO';
var e = function(){};
}
textVo(3);

上面代码再解析过程当中,按照VO存储的顺序会先将函数的参数x存储到AO中,然后是参数f,然后是函数b和函数f,再然后是变量a、b、c、e。

注意:

由于函数声明若发生命名冲突会覆盖,所以参数f会被函数f所覆盖,最后存入到AO对象中的是函数f

由于变量命名冲突会被忽略,所以变量b不会被存储到AO中

解析过程如下:

AO(textVo)={
x:3,
b:<ref to func "b">,
f:<ref to func "f">,
a:undefined,
c:undefined,
e:undefined
}

执行过程如下:

VO['a'] = 1;
VO['b'] = 2;
VO['c'] = 'VO';
VO['e'] = function e(){}; AO(textVo)={
x:3,
b:2,
f:<ref to func "f">,
a:1,
c:'VO',
e:function e(){};
}

JavaScript的执行过程其实可以理解为赋值的过程,由于函数f已经在解析过程中处理完了,在执行的过程中我们就可以直接忽略掉。

注意:在执行过程也就是赋值的过程中,发现b的值是2,这个时候会把值直接赋给已经存在AO中的函数b,最后输出的结果是b为2,函数b被替换掉了,这一块要注意下。

概念搞清楚之后,我们来看下面这道面试题。

alert(a);
a();
var a=3;
function a(){
alert(a);
alert(1);
}
alert(a);
a=6;
a();

第一眼看到这道题的感觉是不是:???(((φ(◎ロ◎;)φ)));现在我们掌握了执行上下文和变量对象之后再来看这道题就会变的很清晰。

解析:代码在解析阶段首先去找参数,我们这里没有参数所以直接忽略,接下来找到函数声明a放进VO中,然后找到变量a被忽略;

执行:

  • 第一步:进入全局执行上下文,运行alert(a),因为在解析阶段a已经存在于VO中,所以弹出的是函数a的源代码(alert里面的a是函数a的引用,是一个指针指向函数a);然后弹出全局上下文。
  • 第二步:第二个a是调用函数a,进入函数a的执行上下文,执行函数a内部的alert(a),这个时候的a还是函数a的指针,所以还是弹出函数a的源代码,然后弹出1;弹出函数a的执行上下文。
  • 第三步:进入全局执行上下文,给a赋值为3。
  • 第四步:执行alert(a),这个时候VO中的a已经被赋值为3了,所以弹出3。
  • 第五步:给a赋值为6。
  • 第六步:由于现在VO中的a已经被赋值为6了,所以在调用a()的时候会报错:Uncaught TypeError: a is not a function。

最后执行的结果为

ƒ a(){
console.log(a);
console.log(1);
}
ƒ a(){
console.log(a);
console.log(1);
}
1
3
Uncaught TypeError: a is not a function

以上就是我对执行上下文和变量对象的理解,不合理的地方望指出。

透过一道面试题来探探JavaScript中执行上下文和变量对象的底的更多相关文章

  1. javascript执行上下文和变量对象

    执行上下文(execution context): 执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念. js语言是一段一段的顺序执行,这个“段”其实就是我们说的这个执行上 ...

  2. JavaScript学习系列之执行上下文与变量对象篇

    一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...

  3. 前端学习 第二弹: JavaScript中的一些函数与对象(1)

    前端学习 第二弹: JavaScript中的一些函数与对象(1) 1.apply与call函数 每个函数都包含两个非继承而来的方法:apply()和call(). 他们的用途相同,都是在特定的作用域中 ...

  4. 深入理解javascript中执行环境(作用域)与作用域链

    深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...

  5. 再看javascript执行上下文、变量对象

    突然看到一篇远在2010年的老文,作者以章节的形式向我们介绍了ECMA-262-3的部分内容,主要涉及到执行上下文.变量对象.作用域.this等语言细节.内容短小而精悍,文风直白而严谨,读完有酣畅淋漓 ...

  6. JavaScript内部原理系列-变量对象(Variable object)

    概要 我们总是会在程序中定义一些函数和变量,之后会使用这些函数和变量来构建我们的系统.然而,对于解释器来说,它又是如何以及从哪里找到这些数据的(函数,变量)?当引用一个对象的时候,在解释器内部又发生了 ...

  7. JavaScript中如何判断两变量是否“相等”?

    1 为什么要判断? 可能有些同学看到这个标题就会产生疑惑,为什么我们要判断JavaScript中的两个变量是否相等,JavaScript不是已经提供了双等号“==”以及三等号“===”给我们使用了吗? ...

  8. javascript中执行环境和作用域(js高程)

    执行环境(execution context,为简单起见,有时也成为“环境”)是javascript中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境 ...

  9. javascript中函数声明、变量声明以及变量赋值之间的关系与影响

    javascript中函数声明.变量声明以及变量赋值之间的关系与影响 函数声明.变量声明以及变量赋值之间有以下几点共识: 1.所有的全局变量都是window的属性 2.函数声明被提升到范围作用域的顶端 ...

随机推荐

  1. reference file contains errors

    一,问题分析 在学习 JavaWeb 的开发,很多时候我们会引用许多 JAR ,以为版本的问题,有时候就会出现这个问题:reference file contains errors (引用文件包含错误 ...

  2. CentOS 7 学习(一) 配置LAMP和Nginx

    CentOS 7 学习(一) 配置LAMP和Nginx CentOS是RedHat Linux企业版的代码编译版本,属于比较通用的服务器Linux版本,据说Ubuntu Server更通用,呵呵,不过 ...

  3. JavaScript基础2——关于变量

    变量的声明                            变量的定义:使用var关键字来声明,区分大小写的.注意:不用var,会污染全局变量.        变量的命名规范是:字母,数字,$符 ...

  4. http中的get和post(二)

    博客园精华区有篇文章< GET 和 POST 有什么区别?及为什么网上的多数答案都是错的 >,文中和回复多是对以下两个问题进行了深究: 长度限制 Url 是否隐藏数据 在我看来这两者都不是 ...

  5. SQL基本查询_子查询(实验四)

    SQL基本查询_子查询(实验四) 1.查询所有员工中薪水低于"孙军"的员工姓名和薪水: 2.查询与部门编号为"01"的岗位相同的员工姓名.岗位.薪水及部门号: ...

  6. Git安装和使用(谨记)

    刚开始用git的小白适用,,转自http://www.cnblogs.com/qijunjun/p/7137207.html 实际项目开发中,我们经常会用一些版本控制器来托管自己的代码,今天就来总结下 ...

  7. 我是这样学习使用google学术的

    本科期间一直在cnki上面检索论文,随着科研能力的需要,部分论文在cnki的局限性就体现出来了,我就开始培养自己的文献检索能力.现在的各种开发工具,各种论文检索网站再加上文献检索的形式越来越复杂,我们 ...

  8. 手机端rem如何适配_rem详解及使用方法2

    作为一个前端开发人员,我们的任务是将UI设计师的图稿运用计算机语言呈现在用户面前.而现在的设备大小尺寸不一,近年来智能手机的普及更是让网页的用户大部分来源与手机,所以让不同大小的移动端屏幕都能较好的还 ...

  9. CPP--正码,反码,补码~附整数溢出的探讨

    之前说到了long的争议(http://www.cnblogs.com/dotnetcrazy/p/8059210.html),这边就不用long来举例了,用int吧 可以看一下这篇文章(http:/ ...

  10. Qt仿win7自动顶部最大化左侧右侧半屏效果

    Win7系统不得不说是非常好用的,也是目前为止占用份额最大的操作系统,其中win7有个效果,将窗体拖动到顶部时会自动最大化,拖动到左侧右侧时会自动半屏显示,再次拖动窗体到其他位置,会重新恢复之前的大小 ...