javascript代码是怎么执行的?
var val = 1;
function foo() {
console.log(val);
}
foo();
// 控制台打印1
上段代码测试下来似乎javascript是顺序执行的,再看一段代码:
var val = 1;
function foo() {
console.log(val);
}
foo();
function foo() {
console.log(val + 'next');
}
// 最终控制台打印‘1next’
按照之前的猜测,代码执行是这样的:
声明变量val并赋值1;
声明函数foo;执行函数foo;
声明同名函数foo。
但结果却不是这样的,js引擎在解释执行js代码的时候并不是一行一行顺序执行的,而是一段一段解释执行的。那在执行一段代码之前是需要进行一些准备工作的,这个准备工作严谨点来说就称为执行上下文
执行上下文栈
我们前面所说的一段一段代码是怎么划分的呢?代码段包含全局代码、函数代码、eval代码,这些都称为可执行代码
通常我们的代码中会包含很多代码段,那引擎是怎么管理这么多代码段执行的呢?
其实js引擎会创建一个执行上下文栈来管理这么多执行上下文(我们都知道栈遵循后进先出)
通过一段代码来分析一下引擎是如何管理执行上下文的
function foo() {
console.log('output');
}
function bar() {
foo();
}
function baz() {
bar();
}
baz();
1、// baz()
创建baz执行上下文,并推入栈中
2、// baz函数中调用了bar函数
创建bar函数的执行上下文,并推入栈中
3、// bar函数中调用了foo函数
创建foo函数的执行上下文,并推入栈中
4、// foo() 执行完毕
将foo的执行上下文弹出栈外
5、// bar() 执行完毕
将bar的执行上下文弹出栈外
6、// baz() 执行完毕
将baz的执行上下文弹出栈外
(其实还少了一个:全局的执行上下文栈)
函数的执行上下文前面已经介绍了,是在函数执行之前所做的一些准备工作,那执行上下文到底又是什么呢?
个人觉得简单点理解可以这样认为: 执行上下文是包含这几个属性的: 变量对象、作用域链、this;
下面就分别看一下变量对象、作用域链是什么;
变量对象
变量对象其实就是函数执行时和函数执行上下文相关的作用域
变量对象包括下面的属性:
函数的形式参数
- 如果没有实际参数,则值为undefined
函数内部的函数声明
- 如果变量对象中已经包含同名属性,则函数声明会完全覆盖该属性
函数内部的变量声明
- 如果变量对象中已经包含同名属性,则变量声明会被忽略
结合代码:
var glo = 2;
function foo(val) {
var temp = 1;
function bar() {
return glo + temp + val;
}
return bar();
}
foo(3);
函数foo执行时的变量对象就是这样的:
AO =
{
arguments: {val: 3}, // 形式参数
bar: function, // 函数声明
temp: undefined, // 变量声明
}
接下来执行代码,会使用或者修改变量对象的值
作用域链
通俗得说作用域链就是当前环境能访问到的变量的集合组成的链条
var glo = 1;
function foo(val) {
var local = 2;
return val + local + glo;
}
函数foo在创建的时候就确定了其内部可访问的作用域范围: 函数作用域中包含local、形式参数val、上层作用域(全局作用域)中glo