js中函数不是简单的从上往下执行的过程,它会有一个声明提升的过程
什么是变量提升呢?
变量提升是指,当栈内存形成的时候也就是作用域形成的时候,浏览器会将带var 和function的关键字提前进行声明和定义,这种预处理机制就是声明提升
举个例子:
console.log(a);//undefined
var a = 12;
var a=12;
这其中其实包含两个步骤
1.var a; 声明一个变量a(声明)
2.a=12; 给a赋值12(定义,也就是赋值操作)
根据上文所述的变量提升机制上面代码可以等价于
var a;
console.log(a); //undefined
a = 12;
这里记住带var之声明未定义,带function声明和赋值都完成了,全局作用域执行:先变量提升后以此执行,局部作用域执行:先形参赋值,在变量提升,最后代码依次执行
关于window和var
console.log(a);//undefined
console.log('a'in window);//false
a=12;
console.log(a);12
console.log(window.a);12
不加var本质就是window属性,在私有作用域中不带var向上级查找直到window,这就是作用域链
在看一个例子:
function fn() {
b = 13;
console.log(b);
//console.log("b" in window)=>true,沿着作用域链在window也找不到b的声明,相当于给window设置一个属性b
}
fn();
console.log(b);
说了变量的声明提升再来说说函数的声明提升
首先我们知道的是定义函数的方法有两种一种是函数声明一种是函数表达式,而函数声明同样拥有函数声明提升
eat()
function eat() {
console.log("food");
}
这样不会出现错误
然而使用函数表达式(匿名函数)
eat()
var eat = function() {
console.log("food");
}
此时会报错,因为没有函数声明提升的过程,执行eat这个函数,浏览器无法识别
所以我们得出结论,函数声明有声明提升过程,匿名函数没有
在这里有一点需要注意
老版本浏览器function声明和定义都完成
新版本只完成声明不定义所以报错
再看一个例子
f = function() {
return true
};
g = function() {
return false
};
~ function() {
if (g() && [] == ![]) {
f = function() {
return false
};
function g() {
return true
}
}
}()
乍一看不好理解,其实还是按照之前的步骤进行
我们可以看到首先在window下设置了两个属性a,b,各存储了一个函数
接下来进入自执行函数,形成私有作用域,这里记住有if的时候无论是否为真都先进行变量提升,我们都知道要if为true才能执行其中的代码其中根据变量提升在if之前应该有function g然而这里赋值了吗?没有所以这里g()无法执行报错g is not a function ,如果我们假设if为ture的话接下来的代码根据作用域链的关系,将全局的f进行改变。
这里补充一下
[]=![]
空数组变成布尔的true,只有0,空字符串,nan,null,undefined为false
[]=flase
对象和布尔比较变成数字
0==0
True
关于重名
如果此时
var fn=12;
function fn(){};
请问此时的window.fn是什么
此时我们关于重名的处理:不会重新声明,但会重新定于,重新赋值
fn();
function fn() {
console.log(1);
}
fn();
function fn() {
console.log(2);
}
fn();
var fn = 10
fn();
function fn() {
console.log(3);
}
fn();
function fn() {
console.log(4);
}
为什么呢,根据前面说的关于重名的处理,不会重新声明但会重新赋值
fn=。。console.log(1)
。。console.log(2)
。。console.log(3)
。。console.log(4)
fn被最终赋值打印4所以前3个fn()执行结果打印4,而带var的在提升阶段只把声明处理了,没有赋值操作所以fn被赋值为100
也就是fn=100;所以fn()就是100()此刻报错,后面fn()都不在执行。
以上说了这么多,但是因为有了es6的let存在,其实都等于白说了,let的存在一劳永逸的解决了多个用var所产生的问题,而js的作者也说过var应该被彻底的套淘汰了