JavaScript 之执行上下文

瘢痂泛型
• 阅读 1283
本文共 1090 字,读完只需 4 分钟

概述

JavaScript 是函数式编程语言,作用域也是以函数为单位,那么,这些函数代码块是怎么样的顺序进行的呢, JS 的可执行代码又分为 3 种,不同类型的代码有不一样的执行环境。本文就梳理有关 JS 执行上下文(execution context),也叫执行环境的知识。

活动的执行代码的上下文在语言底层逻辑上构成一个执行上下文栈(excution context stack),我们知道,栈有两个行为,压入栈和弹出栈,并且有后进先出的特点, 最先进入的项会在栈底最后弹出。

不同执行环境的有其相应的变量对象(Variable Object),某个执行环境的所有可执行代码都执行完毕后,该环境中的变量对象和函数定义会被清除。

函数代码会在执行完后清除变量占用的内存,全局代码则会在关闭环境,比如关闭浏览器后清除。

一、代码类型

JS 中可执行的代码可分为三种类型:

  1. 全局代码
  2. 函数代码
  3. Eval 代码

全局代码

在 web 浏览器中,全局执行环境是 window 对象,所有的全局变量和函数都是作为 window 的属性和方法存在。

全局代码的执行上下文栈可以表示为:

ECStack = [
    globalContext
]

函数代码

当执行函数代码时,函数代码上下文被压入到执行上下文栈中。函数代码的执行环境中,有自己的内部的定义的变量和声明。

function foo1() {
    var name1 = "a";
    console.log(name1)
}

function foo2(){
    var name2 = "b";
    console.log(name2)
}

foo1();
foo2();

上面的执行上下文可以表示为:

ECStack = [
    globalContext
];

ECStack.push(<foo1> functionContext);
ECStack.pop();
ECStack.push(<foo2> functionContext);
ECStack.pop();

一个函数,可能有多个执行上下文,每个函数的调用都会产生新的上下文。

function foo() {
    ...
}

foo("a");
foo("b");
foo("c");

eval 代码

eval 关键字接受一个字符串作为参数,并将其作为书写文字上下文的代码。

function foo(str, a) {
    eval( str );  // 声明一个新变量
    console.log(a, b);
}
var b = 123;

foo("var b = 456 ", 123) // 123, 456

以上代码引用《你不知道的 JavaScript》的例子,eval 函数中的字符串,被当做可执行代码,最后在 foo 函数中声明了 b 变量,并覆盖了 foo 函数外部的 b 变量。

eval 函数和 JS 的词法作用域的行为,会造成变量环境和执行上下文的混乱,尽量别使用它。

二、执行上下文栈

前面其实已经提到很多关于执行上下文栈的内容,简而言之,JS 在遇到全局代码,函数代码,eval 代码时,会创建相应的执行上下文,不同的上下文,有其变量对象(variable object: VO)和作用域。

看这一段代码:

function func3() {
    console.log('fun3')
}

function func2() {
    func3();
}

function func1() {
    func2();
}

func1();

以上代码引用自冴羽的github,代码顺序用执行上下文栈来表示就是:

ECStack.push(globalContext);

// 调用 func1(), 进入 func1 执行环境
ECStack.push(<func1> functionContext);

// func1中调用了 func2,进入 func2 的执行上下文
ECStack.push(<func2> functionContext);

// func2 调用 func3, 进入 func3 的执行上下文
ECStack.push(<func3> functionContext);

// func3 执行完毕,退出 func3 执行环境
ECStack.pop();

// func2 执行完毕,退出 func2 执行环境
ECStack.pop();

// func1 执行完毕,退出 func1 执行环境
ECStack.pop();

函数代码执行完毕后,执行上下文栈底只剩下全局代码上下文 globalContext, 直到关闭浏览器才会彻底清空执行上下文栈。

总结

代码在执行时会创建由不同作用域构成的作用域链作用域链保证 JS 中的变量和函数都能够有序地访问和执行。

如果是函数代码,则将其 活动对象 作为变量对象,活动对象最开始时只包含一个对象,即 arguments 对象。

后面的文章会介绍变量对象(VO)和作用域链的具体内容,敬请期待吧。

博客内容源自个人公众号,专注分享原创文章,欢迎关注。

JavaScript 之执行上下文

点赞
收藏
评论区
推荐文章
Python进阶者 Python进阶者
3年前
手把手教会你JavaScript引擎如何执行JavaScript代码
JavaScript在运行过程中与其他语言有所不一样,如果不理解JavaScript的词法环境、执行上下文等内容,很容易会在开发过程中产生Bug,比如this指向和预期不一致、某个变量不知道为什么被改了,等等。所以今天我们就来聊一聊JavaScript代码的运行过程。大家都知道,JavaScript代码是需要在JavaScript引擎中运行
Karen110 Karen110
3年前
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript有函数作用域:这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域:它们只能在函数中访问。JS://codeherecann
菜园前端 菜园前端
2年前
为你解惑JS作用域和作用域链知识
原文链接:变量作用域一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都是可以访问的。然而在函数内声明的变量只能在函数体内访问,它们是局部变量,作用域是局部性的。函数参数也是局部变
Jacquelyn38 Jacquelyn38
4年前
你所知道的JS变量作用域
变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内
Stella981 Stella981
3年前
JavaScript易错知识点整理
前言本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一些ES6的知识点。JavaScript知识点1.变量作用域vara1;functio
Stella981 Stella981
3年前
JavaScript之函数
    玩js自然要和函数打交到。函数嘛简单来说就是给代码分个块,方便调用、信息隐藏和代码复用,还可以用于指定对象的行为。另外函数还可以玩出很多花样来。。。JavaScript使用关键字function定义函数。定义一个函数://函数声明//这种定义函数的好处是可以在当前作用域内任何位置调用,因为变量的声明和函数的
Stella981 Stella981
3年前
JavaScript的入门简介
什么是JavaScriptJavaScript,我们一般简称为JS,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。JavaScript现在已经被用到了很多非浏览器环境中,JavaScript基于原型编程、多范式的动态脚本语言,并支持面向对象、命令式和声明式风格。HTML、CSS、JavaScript三者不同的功能:
Stella981 Stella981
3年前
Babel总结
什么是babel?babel是一个JavaScript编译器。Babel是一个工具链,主要用于将ECMAScript2015代码转换为向后兼容的旧浏览器或环境中JavaScript版本。注解:传统的编译是指转化成可执行的代码,也就是二进制代码。但是对于前端来说,因为JS是解释性语言,对于浏览器或者Node来说就是可执行的代码。
Stella981 Stella981
3年前
JavaScript基础2
普通的JavaScript对象是命名值的无序集合,JavaScript同样定义了一种特殊的对象数组array,表示带编号的值的有序集合,JavaScript为数组定义了专用的语法,使得数组具有区别于普通对象而独有的行为特性JavaScript还定义了另一种特殊对象函数,函数是具有与它相关联的可执行代码的对象,通过调用函数来运行可执行代码并返回运算结
Stella981 Stella981
3年前
JavaScript函数式编程,“香”吗?
总说函数是JavaScript的一等公民,很多人就问了,它凭什么?其实凭的就是对于JS这种没有明确归类的“多范式语言”,函数式编程拥有着天然的优势。在JS里,函数本身就被视作对象,可以有属性,能作为参数传给函数,也能作为函数的返回结果,十分便利。而这种特性对于代码日益庞大,业务逻辑逐渐复杂的前端来说称得上是至关重要。
Wesley13 Wesley13
3年前
Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序
静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行。执行顺序优先级:静态块,main(),构造块,构造方法。构造函数publicHelloA(){//构造函数}关于构造函数,以下几点要注意:1.对象一建立,就会调用与之相应的构造