js 中三种函数定义的差别以及函数声明提升
Function 类型
在 ECMAScript 中函数实际上是对象,每个函数都是 Function 类型的实例,且与其他引用类型一样具有属性和方法,由于函数的本质是对象,那么函数名应该是指向对象的指针。一个对象可以有多个引用,所以一个函数可以有多个函数名(指针)。函数的参数也不是必要的(有 arguments 对象),也没有函数签名的说法,这也是为什么 js 的函数不能重载。
定义函数的方式
- 通过函数语法声明定义
function a(x, y) {
return x + y;
}
- 通过函数表达式定义
var a = function(x, y) {
return x + y;
};
这看起来和上面才不多,实际在函数的功能上是一样的。 2. 使用 Function 的构造函数
var a = new Function("x", "y", "return x + y");//不常用
以上 3 种定义函数的方式我们都能使用同样的方式去调用 a。
a(1,2);//3
那么 3 种方式的差别又是什么?先说用构造函数的形式:
第三种写法会导致解析两次代码,第一次是解析常规的 ecmascript 代码,第二次是解析传入构造函数中的字符串。1 和 2 的区别得涉及到函数声明提升的过程。
函数声明提升
解析器在向执行环境中加载数据的时候,对于函数的声明和函数表达式的处理方法并不是一样的。解析器会 先读取函数的声明 ,并使其在执行任何代码前可用。而函数表达式则必须 等待解析器执行到所在代码行 ,才会执行:
console.log(a(1, 2));
function a(x, y) {
return x + y;
}
以上代码是可以正常执行的,因为在代码执行前,解析器已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中去了。在对代码求值的时候,js 引擎在第一遍会声明函数并将他们放到源代码树的顶部。所以,即使声明函数的代码在调用函数的代码之后,js 引擎也能把函数声明提升到顶部,如果像下面的例子将函数声明变为函数表达式就会出错:
console.log(a(1, 2));
var a = function(x, y) {
return x + y;
};
以上错误的原因是函数在一个初始化语句中,而不是声明,在执行到var a
之前,a 没有函数的引用/地址,执行到第一行的时候就会找不到 a 标识符。
所以相对函数表达式语句来定义函数来说,通过函数声明的方式会有一个函数声明提升的过程,而表达式没有。