知识前提:
1. 变量提升:
原理: JS引擎的工作方式是①先解析代码,获取所有被声明的变量;②然后在运行。也就是专业来说是分为预处理和执行两个阶段。
变量提升有两种:
1.var变量提升:通过var定义(声明)的变量,再定义语句之前就可以访问到.此时他的值为undefined
2.函数声明提升:通过function声明的函数,在声明之前就可以直接调用,值为函数定义(函数对象)
//函数声明式
function bar () {}
//函数字面量式 (这种只是把foo当作一个变量提升而不是函数提升)
var foo = function () {}
//因此下面这段代码运行会报错fn is not a function
fn(1);
var fn=function (a) {
console.log(a)
}
当有多个同名变量声明的时候,函数声明会覆盖其他的声明。如果有多个函数声明,则是由最后的一个函数声明覆盖之前所有的声明。
代码示例如下
//函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖,也会被后声明的函数覆盖。
function a() {}
var a;
console.log(typeof a); //function
foo(); // 3 后面定义的函数覆盖了前面定义的函数
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}
var c = 1;
function c(c) {
console.log(c);
}
c(2); //报错:c is not a function
2. 代码分类:
- 全局代码
- 函数(局部)代码
先看下面试题,大家猜一下输出结果是怎样呢? 试题涉及了变量提升和执行上下文栈,作用域和递归,大家可以回忆一下
<script type="text/javascript">
console.log('gb'+i);
var i =1
foo(1);
function foo(i){
if(i==4){
return;
}
console.log('fb'+i);
foo(i+1);
console.log('fd'+i);
}
console.log('ge'+i);
</script>
控制台打印输出是不是和预想的有些许不一样. 这不得不引出我们今天说的执行上下文了
JS代码在执行前,JS引擎总要做一番准备工作,这份工作其实就是创建对应的执行上下文;
执行上下文
1. 全局执行上下文
-
在执行全局代码前将window确定为全局执行上下文
-
对全局数据进行预处理
var定义的全局变量设为undefined,添加为window的属性
function声明的全局函数赋值(fn),添加为window的方法.
this赋值window
-
开始执行全局代码
2. 函数执行上下文
-
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象
-
对局部数据进行预处理
形参变量赋值实参,添加为执行上下文的属性
arguments赋值实参列表,添加为执行上下文的属性
var定义的局部变量赋值为undefined,添加为执行上下文的属性
function声明的函数赋值(fun),添加为执行上下文的方法
this赋值调用函数的对象
-
开始执行函数体代码
执行上下文栈
栈的特点:先进后出,后进先出.可以和队列的特点(先进先出)进行对比
- 在全局代码执行前,js引擎就会创建一个栈来存储管理所有的执行上下文对象
- 在全局执行上下文(window)确定后,将其添加到栈(压栈)
- 在函数执行上下文创建后将其添加到栈中(压栈),函数执行上下文只有函数被调用时才会创建,调用多少次函数就会创建多少上下文。
- 在当前函数不执行完后,将栈顶的对象移除(出栈)
- 当所有的代码执行完后,栈中只剩下window
根据执行上下文栈的特点,现在我们可以分析上面的试题
<script type="text/javascript">
//首先根据js自上而下执行顺序
先打印 gb:1
执行foo(1)
i为1打印 fb:1
调用foo(i+1)此时i为1.即执行foo(2)
打印 fb:2
调用foo(i+1)此时i为2.即执行foo(3)
打印 fb:3
调用foo(i+1)此时i为4.即执行foo(4)
直接return不打印
由于执行上下文栈的特点,后进先出,即后进入的先执行.因此先继续执行foo(3)
打印 fd:3
依次往栈底部继续执行
打印 fd:2
打印 fd:1
最后打印ge:1
因此打印顺序为gb:1 fb:1 fb:2 fb:3 fd:3 fd:2 fd:1 ge:1
console.log('gb:'+i);
var i =1
foo(1);
function foo(i){
if(i==4){
return;
}
console.log('fb:'+i);
foo(i+1);
console.log('fd:'+i);
}
console.log('ge:'+i);
</script>
试题图解如下:
还有一些相关试题,大家阔以根据相关知识看看输出结果
//试题一
function f1() {
f2();
console.log(1);
};
function f2() {
f3();
console.log(2);
};
function f3() {
console.log(3);
};
f1();//3 2 1
//试题二
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
欢迎大佬指教,刚开始写,求轻喷