一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))

在JavaScript的运行过程中,经常会遇到一些"奇怪"的行为,不理解为什么JavaScript会这么工作。

这时候可能就需要了解一下JavaScript执行过程中的相关内容了。

执行上下文

在JavaScript中有三种代码运行环境:

  • Global Code

    • JavaScript代码开始运行的默认环境
  • Function Code

    • 代码进入一个JavaScript函数
  • Eval Code

    • 使用eval()执行代码

为了表示不同的运行环境,JavaScript中有一个执行上下文(Execution context,EC)的概念。也就是说,当JavaScript代码执行的时候,会进入不同的执行上下文,这些执行上下文就构成了一个执行上下文栈(Execution context stack,ECS)

个人对于执行上下文的理解:

某个函数或全局代码的执行环境,该环境中包含执行代码需要的所有信息。

可以简单地理解为执行上下文是一个对象,对象中包含了执行代码的全局信息。

JS原理

原型链

执行上下文

事件循环

栈是什么

1、栈(Stack)是一种线性存储结构,它具有如下特点:

(1)栈中的数据元素遵守“先进后出"(First In Last Out)的原则,简称FILO结构。

(2)限定只能在栈顶进行插入和删除操作。

2、栈的相关概念:

(1)栈顶与栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底。

(2)压栈:栈的插入操作,叫做进栈,也称压栈、入栈。

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第1张图片

(3)弹栈:栈的删除操作,也叫做出栈。

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第2张图片

在此对栈不做过多解释,可以自行查阅资料学习。

变量对象

变量对象是与执行上下文相关的数据作用域。它是一个与上下文相关的特殊对象,其中存储了在上下文中定义的变量和函数声明。也就是说,一般VO中会包含以下信息:

  • 变量 (var, Variable Declaration);
  • 函数声明 (Function Declaration, FD);
  • 函数的形参

当JavaScript代码运行中,如果试图寻找一个变量的时候,就会首先查找VO。对于前面例子中的代码,Global Execution Context中的VO就可以表示如下:

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第3张图片

对象引用

对象引用:如果变量是一个对象,实际上变量中存放的是对象的地址

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第4张图片

对于这张图里面的对象引用可以有这两种说法:

user1持有用户对象的引用

user1指向用户对象

执行栈

上面说到了执行上下文栈,在这里说明一下执行上下文堆栈

Call Stack(执行上下文堆栈(执行栈)):组织管理程序运行过程中的执行上下文

var a=2;
        //函数说明
        function  bar() {
            var i=3;
            function foo(){
                var b=4;
                method();
            }
            foo();
            console.log("123");
        }

        function method(){
            console.log("abc");
        }
        //函数调用
        bar();

上述代码比较简单,这里说明一下函数运行的过程(自底向上看):

全局执行上下文  结束

Bar函数的执行上下文出栈

销毁Console.log函数的执行上下文

Console.log函数的执行上下文

Foo函数的执行上下文出栈

Method函数的执行上下文出栈

销毁Console.log函数的执行上下文

Console.log函数的执行上下文

Method函数的执行上下文

Foo函数的执行上下文

Bar函数的执行上下文

栈底:全局执行上下文  

执行上下文的内容

  1. VO:variable object  变量对象,存放的是函数或全局代码执行过程中需要用到的局部变量
  2. Scope:作用域
  3. This
{

    vo:...,

    scope:...,

    this:....

}

VO是一个对象,调用函数或执行全局代码时创建,创建一个VO,需要经过三步

  1. 确定函数形参的值(包括arguments对象)
function foo(a, b) {

}

foo(3, 4);

foo函数的执行上下文建立过程:

  1. 创建vo对象
  1. 确定形参值
function foo(a,b){
     vo:{
        a:3,
        b:4,
        arguments:{.......}
}
}

 

2.确定函数中所有的函数字面量声明

function foo(a, b) {
a();

function a() {
console.log("hello");
}

var i = 11;
var j = 22;
var bar = 100;
}
  1. 该函数必须是字面量声明,字面量声明提取到vo后,可以认为该声明失效
  2. 如果vo中出现同名属性,直接覆盖
  3. 确定函数中所有的变量声明(var变量声明),将其提取到上下文,值为undefined
  4. 如果当前vo出现同名属性,直接忽略
function foo(a,b){
    vo:{
        a:3,
        b:4,
        arguments:{.......},
        a:指向一个函数,
        i:undefined,
        j:undefined,
        bar:undefined
}
}

一个简单的小demo示例:

function test(a){
console.log(a); //fn

function a(){
console.log("hello");
};
var a = function(){
console.log("haha");
}

a(); //haha

var a = 34;

console.log(a); //34
}

test(5);

当上面的理解之后,对于为什么输出是这个结果,应该没有什么疑问。

面试真题

下面的文字解释都是以栈的角度入手,阅读时自底向上

Test1

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第5张图片

在函数中寻找数据时,会先从vo中查找,

vo:{

foo:undefined  -->  10

}

输出结果:undefined   10

Test2

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第6张图片

b的执行上下文入栈:

vo:{

a:指向函数 -->  10  函数结束

}

全局上下文:

vo:{

b:指向函数,

a:undefined  -->  1

输出:undefined 1

Test3

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第7张图片

全局上下文:

vo{

foo:指向C函数 --> “A”  -->指向函数B

-->  B

}

最终输出:fn(C)  A  fn(B)  B  fn(B)  B

Test4

一篇文章让你理解面试难点:执行上下文(干货满满(附面试题))_第8张图片`

a的上下文

vo{

}

bar的上下文

vo{

a:3  --> 指向函数 ,

a1: undefined  -->  指向函数a,

a:1(全局里面找的1)

}

全局上下文:

vo:{

bar:指向函数bar,

foo:undefined  -->  1

}

最终输出结果:1

 

你可能感兴趣的:(Javascript学习)