作用域的一些概念
任何声明在某个作用域内的变量,都将附属于这个作用域。
提升
先看两个例子
eg1
a = 2;
var a;
console.log( a ); //2
eg2
console.log(a);
var a=2; //undefined
对于eg2 js会将其看成两部分,var a ,a=2;所以eg2会变为
var a;
console.log(a);
a=2;
原因
引擎在解释js代码之前首先对其进行编译。编译阶段的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。
只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。
- 函数声明会被提升,但是函数表达式不会被提升
foo();
var foo=function bar(){
//...
};
这段中变量标识符foo()先被提升并分配给所在作用域,但是foo并没有被赋值,此时foo()是undefined,此时对undefined值进行操作而导致非法操作,抛出TypeError异常。
var foo;
foo();
foo=function bar(){};
- 即使是具名函数表达式,名称标识符在赋值之前也无法在所在作用域中使用;
foo();//TypeError;
bar();//ReferenceError;
var foo=function bar(){
//...
};
经过变量提升的形式:
var foo;
foo();
bar();
foo=function(){
var bar=...self...
//...
};
- 函数优先
函数声明和变量声明都会被提升,在多个重复声明的代码中,函数首先被提升,然后是变量;
且后面的可以覆盖前面的;
foo();//3
function foo(){
console.log(1);
}
var foo=function(){
console.log(2);
};
function foo(){
console.log(3);
}
总结
预编译:函数声明整体提升,变量声明提升(赋值不提升)。
几个概念:
1.暗示全局变量(implyglobal):变量未经声明就赋值,此变量属于全局变量。
2.一切声明的变量,都是全局的属性
作用域做题四部曲:
1.创建AO对象(作用域、执行器上下文)
2.找形参和变量声明,将其作为AO对象的属性名,值为undefined
3.形参和实参相统一
4.在函数体里找函数声明,将值赋予函数体。
(var b=function(){}叫函数表达式 不在考虑里)预编译出现在函数声明的前一刻
题:
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){}
console.log(a);
var b= function(){}
console.log(b);
function d(){}
}
fn(1);
//function a(){}
//123
//123
//function (){}
步骤三:
AO{
a:function a(){},
b:undefined,
d:function d(){}
}
再按照顺序执行。
参考:你不知道的JaveScript上卷——提升