当函数执行的时候,会创建一个称为执行上下文(execution context)的环境,分别为创建阶段和执行阶段两个阶段;
创建阶段:指函数被调用但还没有执行任何代码时,此时创建了一个拥有三个属性的对象:
executionContext = {
scopeChain: {}, // 创建作用域链{scope chain}
variableObject: {}, // 初始化变量、函数、形参等
this: {} // 指定this
}
举个栗子:
function demo () {
/*executionContext = {
scopeChain: {}, // 创建作用域链{scope chain}
variableObject: {
name: undefined,
getData: undefined,
num: undefined,
arguments: [],
c: undefined
}, // 初始化变量、函数、形参等
this: {} // 指定this
}*/
var name = '';
var getData = function () {};
function c() {
}
}
那么问题来了:
function demo (num) {
var getData = function () {};
console.log('name', name);
function c() {}
}
function outer() {
var name = 'loisly'
demo(100);
}
outer()
上面代码的执行结果会是什么呢?
答案:结果会报错ReferenceError: name is not defined;为什么会是这个结果呢?outer中不是定义了name了吗?怎么不是像外层作用域链找吗?
原因是:我们的函数在向外找作用域域的时候,是在定义时向外找,比如:
function demoOuter(params) {
function demo (num) {
var getData = function () {};
console.log('name', name);
function c() {}
}
}
比如上面的外层作用域链是demoOuter,而demoOuter的在外层就是window;
函数作用域向外找,找的是定义的时候的作用域链;
栗子:
function c() {
console.log('c');
}
function a() {
function b() {
c();
}
b();
}
a();
类似上面的例子的执行上下文栈是这样的:
这样其实会存在一个问题,看下面的demo:
function demo(num) {
var name = 'xiaotao';
var getData = function getData() {}
function c() {}
if (num > 100000000000000000000000000000) {
return;
}
demo(num + 1);
}
demo(0)
上面代码爆栈,RangeError: Maximum call stack size exceeded
原因,当一个函数调用另一个函数的时候,就会在栈里面塞另一个函数的执行上下文,每次执行demo的时候,都会塞一次这个函数的执行上下文,然而外层函数的执行航下文,变量那些还没有删除,所以会越累计越多;
栈的大小每个浏览器每个版本都不一样;
假如需要执行百万节点的递归遍历,一般用什么方法好?不建议用递归,用栈模拟,尾调用优化;
执行阶段:
代码执行阶段的主要任务是:1、分配变量、函数的引用、赋值,2、执行代码。
注意区分:作用域链和执行上下文栈的区别;
如果递归任意次数那电脑内存就不够用了呀,浏览器分配内存也不能占用全部的电脑内存,有个临界值(最大值);