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

  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. 工作随笔——自动重发的凶手--feign

    公司使用的feign(https://github.com/OpenFeign/feign)作为http客户端. 开发时debug后端程序,发现同一个请求会多次收到. 为了判断是谁在搞鬼,在客户端和服 ...

  2. 数据分析与展示——Matplotlib基础绘图函数示例

    Matplotlib库入门 Matplotlib基础绘图函数示例 pyplot基础图表函数概述 函数 说明 plt.plot(x,y,fmt, ...) 绘制一个坐标图 plt.boxplot(dat ...

  3. iOS Swift基础知识代码

    推荐:Swift学习使用知识代码软件 //集合类型 数组 字典 func array1(){ var arr = [","dd"] //简单写法 var arr1 = [ ...

  4. 每周.NET前沿技术文章摘要(2017-06-07)

    汇总国外.NET社区相关文章,覆盖.NET ,ASP.NET等内容: .NET .NET Core and .NET Framework Working Together, Or: The Magic ...

  5. c#中treeview的使用方法(转 )

    本文主要介绍treeView控件中,添加,修改.删除节点的操作, 首先当窗体加载的时候,我们添加上图中所示的节点. 当点击“Delete the Selected”按钮时,被选中的节点将被删除. 当点 ...

  6. Intellj Idea使用tomcat部署不成功,死活也找不到解决办法的看这里

    Intellij 周六晚上开发一个简单web项目的,使用tomcat打包部署,死活也没法部署成功,和这个问题怼了6个小时,也没搞清楚具体为什么不能访问页面,但是好在最后还是找了个方法把问题解决了.以下 ...

  7. Java多线程之赛跑游戏

    在JavaSE中,多线程是一个重要的内容. 我们要了解多线程的概念,就要先了解进程的概念:要了解进程的概念,就离不开操作系统的概念. 在一台正常运行的电脑中,计算机硬件(如CPU.内存.硬盘.网卡.显 ...

  8. 解题思路:best time to buy and sell stock i && ii && iii

    这三道题都是同一个背景下的变形:给定一个数组,数组里的值表示当日的股票价格,问你如何通过爱情买卖来发家致富? best time to buy and sell stock i: 最多允许买卖一次 b ...

  9. 某次送温暖考试的 c题

    题目大意: 给定n个点的无根树,树上每个点都有一个非负的点权. 树上的路径的价值定义为树上路径的点权和-树上路径的点权最大值; 现在给定一个参数P询问有多少条路径的价值是P的倍数(注意单点也算路径,路 ...

  10. 【分治】peak find

    分治算法 算法设计中一种常用的优化方法就是分治的思想,它的解决思路就是将原始的问题划分为性质一样,但是规模减小的子问题,然后通过子问题的解和合并子问题的解得到最终的解,就是分治的思想: 比较常见的分治 ...