函数覆盖

288 阅读3分钟

函数声明有三种: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

  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 声明的函数整个就是一个声明,编译时会被提升到最顶端,所以调用时在它声明之前也可以调用;而函数表达式则只提升左边声明部分,而右边函数体被留在原地,如果在其前面调用函数,则会报错。

函数覆盖

如果一个函数被重复声明,后面的声明会覆盖前面的。 但是有几种情况需要注意:

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