变量提升
当前上下文中,所有带var和function关键字的进行提前的声明或者定义。
- var:只声明不定义;
- function:声明+定义;
变量提升阶段处理过的事情,代码执行阶段不会重复处理。
总结:自执行函数不存在变量提升。因为自执行函数是匿名函数,所以无法在所在上下文中变量提升。
var foo = 'hello';
//自执行函数执行:创建一个函数并且立即把这个函数执行
(function(foo){
//形成私有上下文---作用域链、形参赋值、变量提升(var foo(已存在))
console.log(foo);
var foo = foo || 'world';
console.log(foo);
})(foo)
变量提升面试题
下面是最新收罗的关于变量提升的一些变态面试题,敢来挑战吗?
{
console.log(foo);
function foo(){};
foo=1;
console.log(foo);
}
console.log(foo);
/*解析:-----------------------------------------------------------------------------------------
* EC(G)
* 变量提升:function foo----只声明未定义
* 代码执行:
*/
{
/*
* EC(BLOCK) 块级上下文
* 作用域链:<EC(BLOCK),EC(G)>
* 变量提升:function foo(){}----AF0 声明+定义
* 代码执行
此时foo在全局和私有上下文中都声明了一次(兼容ES3/ES6),和两者都有关系:
a.在代码执行时,会把function foo(){}这行代码之前所有对foo的操作不仅认为是私有的,也会给全局映射一份。(不包含这行代码)
b.但是之后对foo操作的代码都认为是私有的。
*/
console.log(foo); //函数
function foo(){}; //---这行代码不执行,在变量提升阶段已处理 所以此时全局foo----AF0
foo=1;
console.log(foo); //1
}
console.log(foo); //f foo(){}
/*------------------------------------------------------------------------------------------
* EC(G)
* 变量提升:
* function foo;
* function foo; (两次一样,全局下只有一个foo,不重复声明)
*/
{
/*
* 块级上下文
* 变量提升:function foo(){1}
function foo(){2} => 结果
*/
console.log(foo); //f foo(){2}
console.log(window.foo); //undefined
function foo(){1} // => 此时不会映射给全局,等待最后一次再处理
foo=1;
function foo(){2} // => 此行之前对foo的操作都会映射给全局一份,全局foo----1
console.log(foo); //1
}
console.log(foo); //1
{
function foo(){}
foo=1;
function foo(){}
foo=2;
console.log(foo); //2
}
console.log(foo); //1
var a = 0;
if(true){
a = 1;
function a(){};
a = 21;
console.log(a);
}
console.log(a);
/*
* 最新版本浏览器
1.向前兼容ES3/5规范
1.1判断体和函数体不存在块级上下文,上下文只有全局和私有;
1.2不论条件是否成立,带function的都要声明+定义;
2.向后兼容ES6规范
2.1存在块级作用域,大括号中出现let/const/function...都会被认为是块级作用域;
2.2不论条件是否成立,带function的只提前声明,不会提前赋值了;
*/
/******************** 旧浏览器 *********************/
/*
全局作用域
1.变量提升 var a; function a(){}-----堆内存AF0;
2.代码执行
var a=0; //a -> 0
a=1; //a -> 1
function a(){}; //不执行
a=21; //a -> 21
console.log(a); //输出:21
console.log(a); //输出:21
*/
/******************** 新浏览器 *********************/
/*
全局作用域
1.变量提升 var a; function a;(只声明不赋值)
2.代码执行
var a=0; //a -> 0
if(true){ //遇到大括号并且里面有function,形成块级作用域
//变量提升 function a(){}-----AF0 声明加定义
a=1; // a -> 1(私有)
function a(){}; //因为要兼容ES3/ES6,function a在全局下声明过,在私有也处理过,遇到此行代码私有下不再处理,但是浏览器会把当前之前所有对a的操作都映射给全局一份,以此兼容ES3,但是它后面的代码和全局没有任何关系了! 此时全局a->1
a=21; // a -> 21(私有)
console.log(a); //输出:21
}
console.log(a);//输出:1
*/
总结:
- 代码执行时遇到{},除了函数和对象的大括号外,在看到{}中有let/const/function才会把其作为块级作用域,function产生块级作用域也是新版浏览器加入的;
- 代码未执行时不会形成块级作用域;
- 如果在全局和私有上下文中都声明了一次(兼容ES3/ES6),和两者都有关系: a.在代码执行时,会把function foo(){}这行代码之前所有对foo的操作不仅认为是私有的,也会给全局映射一份。(不包含这行代码) b.但是之后对foo操作的代码都认为是私有的。
感觉自己掌握差不多了哈,那再来一道让你怀疑人生,哈哈哈!
题目A:
var x = 1;
function func(x,y=function anonymous1(){x=2}){
x=3;
y();
console.log(x); //2
}
func(5);
console.log(x); //1
解析:
/*
* EC(G)
* 变量提升:var x; function func(x,y=...){...};
*/
var x = 1;
function func(x,y=function anonymous1(){x=2}){
/*
* EC(func) 作用域链<EC(func),EC(G)>
* 形参赋值:x=5 y=BF0
* 变量提升
* 代码执行
*/
x=3;
y();
/*
* EC(anonymous1) 作用域链<EC(anonymous1),EC(func)>
* 形参赋值
* 变量提升
* 代码执行
* x=2;-------非私有,找上级作用域,将EC(func)中x修改为2
*/
console.log(x); //2
}
func(5);
console.log(x); //1
题目B:
var x = 1;
function func(x,y=function anonymous1(){x=2}){
var x=3;
y();
console.log(x); //3
}
func(5);
console.log(x); //1
解析:
/*
* EC(G)
* 变量提升:var x; function func(x,y=...){...};
*/
var x = 1;
function func(x,y=function anonymous1(){x=2}){
/*
* EC(func) 作用域链<EC(func),EC(G)>
* 形参赋值:x=5 y=BF0
* 变量提升: var x;
*------------------------
* 形参赋值默认值+声明变量,所以会单独多形成一个私有的块级上下文(把函数体{}当作块级上下文)
* EC(BLOCK)
* 作用域链:<EC(BLOCK),EC(func)>
* 变量提升: var x; 会把私有上下文中形参x赋值的值给它
*/
var x=3; //EC(BLOCK)中x=3
y(); //EC(BLOCK)没有y,所以找上级,EC(func)中y执行
/*
* EC(anonymous1) 作用域链<EC(anonymous1),EC(func)>
* 形参赋值
* 变量提升
* 代码执行
* x=2;-------非私有,找上级作用域,将EC(func)中x修改为2
*/
console.log(x); //3----块级上下文x
}
func(5);
console.log(x); //1----全局x
//提示:可在代码前加debugger 断点调试,看执行过程
题目C:
var x = 1;
function func(x,y=function anonymous1(){x=2}){
y=function anonymous1(){x=4} //不会形成2个上下文
y();
console.log(x);
}
func(5);
console.log(x);
var x = 1;
function func(x,y=function anonymous1(){x=2}){
x=4;
function x(){}; //函数名和形参名相同,会形成2个上下文
y();
console.log(x); //4
}
func(5);
console.log(x); //1
总结:
A.块级上下文中没有自己的this,它用到的this是其上级上下文中的this;
B.函数执行会形成私有上下文,如果:
- 有形参赋值默认值;
- 函数体中有声明过自己的私有变量(var / let / const,function只有声明的名字和形参中的名字相同才会单独产生块级上下文);
(两个条件缺一不可)则会把函数体{}包起来的看作一个私有的块级上下文,此时函数执行就会有两个上下文;