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

  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. 【java提高】Serializable(一)--初步理解

    Serializable(一)--初步理解 一 序列化是干什么的? 我们知道,在jvm中引用数据类型存在于栈中,而new创建出的对象存在于堆中.如果电脑断电那么存在于内存中的对象就会丢失.那么有没有方 ...

  2. 配置SQL Server on Linux(2)

    1. 前言 前一篇配置SQL Server on Linux(1),地址:http://www.cnblogs.com/fishparadise/p/8125203.html ,是关于更改数据库排序规 ...

  3. JAVA中的集合与排序

    一:常见的集合类 Collection接口  和   Map接口 Collection ①:collection是最常见的集合的上级接口. ②:继承自collection的常用接口有List,Set, ...

  4. PE格式第三讲扩展,VA,RVA,FA(RAW),模块地址的概念

    PE格式第三讲扩展,VA,RVA,FA的概念 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶VA概念 VA (vi ...

  5. 视觉SLAM的数学表达

    相机是在某些时刻采集数据的,所以只关心这些时刻的位置和地图. 就把这一段时间的运动变成了李三时刻 t=1,2,...K当中发生的事情. 在这些事可,x表示机器自身的位置. x1,x2,x3,x4... ...

  6. 写给小白的JAVA链接MySQL数据库的步骤(JDBC):

    作为复习总结的笔记,我罗列了几个jdbc步骤,后边举个简单的例子,其中的try块请读者自行处理. /* * 1.下载驱动包:com.mysql.jdbc.Driver;网上很多下载资源,自己找度娘,此 ...

  7. 第三节 - centos 内核启动、救援模式、 ls 、目录结构

    Linux 第三节一.CentOS 启动: 1.内核引导: 1.win/linux 通电,2.BISO自检(CPU,内存,硬盘等 | U盘.光驱.网卡.硬盘启动 通过MBR知道内核内存硬件驱动位置并加 ...

  8. 如何减轻ajax定时触发对服务器造成的压力和带宽的压力?ajax-长轮训

    AJAX长轮询的方法来解决频繁对后台的请求,进一步减小压力 在实现过程发现AJAX的多次请求会出现多线程并发的问题又使用线程同步来解决该问题 个人对ajax长轮询的一点愚见 ajax请示后台时,后台程 ...

  9. Java后端程序员都做些什么?

    这个问题来自于QQ网友,一句两句说不清楚,索性写个文章. 我刚开始做Web开发的时候,根本没有前端,后端之说. 原因很简单,那个时候服务器端的代码就是一切:接受浏览器的请求,实现业务逻辑,访问数据库, ...

  10. jq 中each的用法 (share)

    each的使用方法 在jQuery里有一个each方法,用起来非常的爽,不用再像原来那样写for循环,jQuery源码里自己也有很多用到each方法.其实jQuery里的each方法是通过js里的ca ...