函数声明有三种:function a()、函数表达式以及 Function构造函数。前两种最为常见,用的最多。
(1)function 命令
function foo(s){
console.log(s);
}
(2)函数表达式
var f = function(s){
console.log(s);
}
函数表达式后面一般是匿名函数,但也可以是具名,如果加上函数名,则只对函数内部有效,对外部无效。意思就是在类似递归这样函数自己调用自己时有效,而在外部调用就不能通过函数名调用。
var f = function a(){
console.log(typeof a);
};
a()// ReferenceError: x is not defined
f()// function
在外部通过变量名调用。
提升
其实js在编译过程中表达式是分两步的,先是声明,再是赋值。例如 var a = 2
- 遇到 var a,编译器会询问作用域是否已经存在该变量。如果是,编译器会忽略这个声明;否则会在作用域中声明变量a。
- 在运行过程中运行 a = 2,引擎会询问作用域有没有叫做 a 的变量。如果有,就使用这个变量;如果没有,就往更大的作用域查找,如果找不到,在非严格模式下会自动声明一个全局变量 a,并让他等于2。
总结:只有声明会被提升,而赋值或其他运行逻辑会留在原地。 也就是说,声明和赋值是在不同的阶段,声明 var a是在编译阶段,被提升到了所在作用域的最顶端;而赋值 a = 2是在执行阶段,被留在原地等待执行。
foo()
function foo(){
console.log(2)
}
//2
foo()
var foo = function(){
console.log(2)
}
//Uncaught TypeError: foo is not a function
所以,function 声明的函数整个就是一个声明,编译时会被提升到最顶端,所以调用时在它声明之前也可以调用;而函数表达式则只提升左边声明部分,而右边函数体被留在原地,如果在其前面调用函数,则会报错。
函数覆盖
如果一个函数被重复声明,后面的声明会覆盖前面的。 但是有几种情况需要注意:
1. 都是用 function声明
function a(){
console.log(1);
}
a() //2
function a(){
console.log(2);
}
a() //2
由于“提升”,无论函数在哪调用都会调用后面的那一个,而前面的会被覆盖。
2. function声明又有函数表达式
var a = function(){
console.log(1);
}
function a(){
console.log(2);
}
a() //1
||
||相当于
||
var a
function a(){
console.log(2);
}
a = function(){
console.log(1);
}
a()
2的第一种情况,这时被“提升”的是第一个函数表达式的 var a 和 第二个函数function a()。而第一个函数表达式的右边部分被留在原地,这就覆盖了被提升的第二个函数function a()。所以调用时,执行的是第一个函数表达式。
function a(){
console.log(1);
}
a() //1
var a = function(){
console.log(2);
}
||
||相当于
||
function a(){
console.log(1);
}
var a
a()
a = function(){
console.log(2);
}
2的第二种情况,就是在调用 a()时,第二个函数表达式的右边部分被留在它的后面,所以执行第一个function a()。