变量提升
首先是用var定义一个变量的时候, 例如: var a = 10; 大部分的编程语言都是先声明变量再使用, 但是javascript有所不同, 上面的代码, 实际相当于这样执行:
var a;
a = 10;
因此有了下面这段代码的执行结果:
console.log(a); // 声明,先给一个默认值undefined;
var a = 10; // 赋值,对变量a赋值了10
console.log(a); // 10
函数声明提升
定义函数也有两种方法:
● 函数声明: function foo () {};
● 函数表达式: var foo = function () {}.
类型一:
console.log(f1) // function f1(){}
function f1() {} // 函数声明
console.log(f2) // undefined
var f2 = function() {} // 函数表达式
可以看到, 使用函数声明的函数会将整个函数都提升到作用域(后面会介绍到)的最顶部, 因此打印出来的是整个函数; 而使用函数表达式声明则类似于变量声明提升, 将var f2提升到了顶部并赋值undefined.
类型二:
console.log(f1) // function f1(){...}
f1(); // 1
function f1() { // 函数声明
console.log('1')
}
console.log(f2) // undefined
f2(); // 报错: Uncaught TypeError: f2 is not a function
var f2 = function() { // 函数表达式
console.log('2')
}
虽然f1()在function f1 () {...}之前,但是却可以正常执行; 而f2()却会报错, 原因:f2还只是undifined并没有被赋值为一个函数, 因此会报错.
类型三:
当遇到函数和变量同名且都会被提升的情况时, 函数声明的优先级是要大于变量声明的.
console.log(f1); // f f1() {...}
var f1 = "10";
function f1() {
console.log('我是函数')
}
小试牛刀
function test(arg) {
console.log(arg);
var arg = 10;
function arg() {
console.log('函数')
}
console.log(arg)
}
test('LinDaiDai');
- 函数里的形参arg被后面函数声明的arg给覆盖了, 所以第一个打印出的是函数;
- 当执行到var arg = 10的时候, arg又被赋值了10, 所以第二个打印出10.
执行上下文栈的变化
var scope = "global";
function checkScope () {
var scope = "local";
function fn () {
return scope;
}
return fn();
}
checkScope();
栈的变化是这样的
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
var scope = "global"
function checkScope () {
var scope = "local"
function fn () {
return scope
}
return fn;
}
checkScope()();
栈的变化是这样的
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
由于checkscope是先推入栈中且先执行的, 所以在fn被执行前就被推出了.