浏览器内核是由两部分组成的,以webkit为例:
有一个强大的JavaScript引擎就是V8引擎。
Parse模块会将JavaScript代码转换成AST(抽象语法树),因为解释器并不直接认识JavaScript代码。如果函数没有被调用,是不会被转成AST的。
Ignition是一个解释器,会将AST转换成ByteCode(字节码)。同时会收集TurboFan优化所需要的信息(如函数参数的类型信息)。如果函数只调用一次,Ignition会解释执行ByteCode;
TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码。
如:
function sum(num1,num2){ return num1+num2 }'运行
若多次调用sum(10,20),则它会被标记为热点函数,且优化为机器码。这里的数据类型都是Number,执行的操作是加法。
但这里也可以传入两个字符串,执行的操作是字符串拼接,若如此,之前优化的机器码就要逆向地转换成字节码。

js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)
js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈。执行的是全局的代码块:
GEC被放入到ECS中里面包含两部分内容:
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作用域与作用域链详解
面试题:
var n = 100 function foo() { n = 200 } foo() console.log(n);'运行
答:
200'运行
function foo() { console.log(n); n = 200 console.log(n); } var n = 100 foo()'运行
答:
100 200'运行
var n = 100 function foo1() { console.log(n); } function foo2() { var n = 200 console.log(n); foo1() } foo2() console.log(n);'运行
答:
200 100 100'运行
解析:
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。
function foo() {
var a = b = 100
}
foo()
console.log(a);
console.log(b);
解析:
coderwhy的课
JavaScript代码到底是怎么执行的?
站在‘上帝 ’角度,透视v8 执行 js 的过程
Js作用域与作用域链详解