函数覆盖

函数声明有三种:function a()、函数表达式以及 Function构造函数。前两种最为常见,用的最多。函数

(1)function 命令ui

function foo(s){
    console.log(s);
}
复制代码

(2)函数表达式spa

var f = function(s){
    console.log(s);
}
复制代码

函数表达式后面通常是匿名函数,但也能够是具名,若是加上函数名,则只对函数内部有效,对外部无效。意思就是在相似递归这样函数本身调用本身时有效,而在外部调用就不能经过函数名调用。code

var f = function a(){
  console.log(typeof a);
};

a()// ReferenceError: x is not defined

f()// function
复制代码

在外部经过变量名调用。递归

提高

其实js在编译过程当中表达式是分两步的,先是声明,再是赋值。例如 var a = 2作用域

  1. 遇到 var a,编译器会询问做用域是否已经存在该变量。若是是,编译器会忽略这个声明;不然会在做用域中声明变量a。
  2. 在运行过程当中运行 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 声明的函数整个就是一个声明,编译时会被提高到最顶端,因此调用时在它声明以前也能够调用;而函数表达式则只提高左边声明部分,而右边函数体被留在原地,若是在其前面调用函数,则会报错。it

函数覆盖

若是一个函数被重复声明,后面的声明会覆盖前面的。 可是有几种状况须要注意:io

1. 都是用 function声明console

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()。