JavaScript高级JavaScript的运行原理:V8引擎,JS代码执行原理,作用域和作用域链面试题

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高级JavaScript的运行原理:V8引擎,JS代码执行原理,作用域和作用域链面试题相关的知识,希望对你有一定的参考价值。

文章目录

V8引擎

浏览器内核是由两部分组成的,以webkit为例:

  • WebCore:负责html解析、布局、渲染等等相关的工作
  • JavaScriptCore:解析、执行javascript代码。

有一个强大的JavaScript引擎就是V8引擎。

架构

Parse模块会将JavaScript代码转换成AST(抽象语法树),因为解释器并不直接认识JavaScript代码。如果函数没有被调用,是不会被转成AST的。

Ignition是一个解释器,会将AST转换成ByteCode(字节码)。同时会收集TurboFan优化所需要的信息(如函数参数的类型信息)。如果函数只调用一次,Ignition会解释执行ByteCode;

TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码

  • 如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能。
  • 机器码也会被还原为ByteCode,因为如果后续执行函数的过程中,类型发生了变化,之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码

如:

function sum(num1,num2)
	return num1+num2

若多次调用sum(10,20),则它会被标记为热点函数,且优化为机器码。这里的数据类型都是Number,执行的操作是加法。
但这里也可以传入两个字符串,执行的操作是字符串拼接,若如此,之前优化的机器码就要逆向地转换成字节码。

JavaScript代码执行原理

初始化全局对象

js引擎会在执行代码之前,会在内存中创建一个全局对象Global Object(GO)

  • 该对象 所有的作用域(scope)都可以访问
  • 里面会包含Date、Array、String、Number、setTimeout、setInterval等
  • 有一个window属性指向自己

执行上下文 Execution Contexts

js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈。执行的是全局的代码块:

  • 全局的代码块为了执行会构建一个全局执行上下文 Global Execution Context(GEC)
  • GEC会 被放入到ECS中 执行

GEC被放入到ECS中里面包含两部分内容:

  • 代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,但是并不会赋值——这个过程也称之为变量的作用域提升(hoisting)
  • 在代码执行中,对变量赋值,或者执行其他的函数

VO对象

每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中

举个例子:

var message = "Global Message"

function foo() 
    message = "foo message"


var num1 = 30
var num2 = 20
var res = num1 + num2
console.log(res);

这段代码被parse(即转化为AST)时,会有一个VO对象。这段代码里的message、foo、num1、num2、res的声明都会被放到这个VO对象中(注意,函数是提前声明的),但是还没赋值。相当于:

VO:
//创建函数对象,值(键值对的值)是指向函数对象的指针
	foo:0xa00,
	message:undefined,	
	num1:undefined,
	num2:undefined,
	res:undefined,

如果执行上下文是全局执行上下文的话,VO就是GO。

函数的执行

执行一个函数时,会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且压入到EC Stack中。

这里的VO是AO(Activation Object),它会使用arguments作为初始化,并且初始值是传入的参数,AO会存放变量的初始化。

作用域和作用域链的面试题

看看这个:Js作用域与作用域链详解

面试题:

1

var n = 100
function foo() 
    n = 200

foo()
console.log(n);

答:

200

2

function foo() 
    console.log(n);
    n = 200
    console.log(n);

var n = 100
foo()

答:

100
200

3

var n = 100
function foo1() 
   console.log(n);

function foo2() 
   var n = 200
   console.log(n);
   foo1()


foo2()
console.log(n);

答:

200
100
100

解析:

  1. foo2里的n是200
  2. 调用foo1,foo1里没有n,所以去它的上层作用域找:它的上层作用域是全局——与调用的位置无关,与定义的位置有关,所以全局里的n是100
  3. 全局里的n是100

4

var a = 100
function foo() 
   console.log(a);
   return
   var a = 100


foo()

答:

undefined

解析:foo里有a!代码其实相当于:

var a = 100
function foo() 
   var a
   console.log(a);
   //return 后的不会执行
   //return
   //a = 100


foo()

所以输出是undefined。

5

function foo() 
    var a = b = 100

foo()
console.log(a);
console.log(b);

解析:

  1. 访问不到a,因为a只在foo函数作用域里,在全局里没有
  2. 访问得到b,因为b没有用var声明,于是会被js解析成全局变量,b为100

参考

coderwhy的课
JavaScript代码到底是怎么执行的?
站在‘上帝 ’角度,透视v8 执行 js 的过程
Js作用域与作用域链详解

以上是关于JavaScript高级JavaScript的运行原理:V8引擎,JS代码执行原理,作用域和作用域链面试题的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript高级学习笔记目录(持续更新)

JavaScript的高级知识---词法分析

javascript一个初级程序 运行有问题!

JavaScript高级程序设计

Javascript高级应用与实践pdf

JavaScript高级程序设计-html中使用JavaScript