首先,JavaScript中函数有两种创建方式,即函数声明、函数表达式两种。
1、函数声明。
function boo(){ console.log(123); } boo()
2、函数表达式。
var boo = function(){ console.log(123) } boo()
现在来说说函数声明提升。还是以例子来说明吧。
boo(123) function boo(x){ console.log(x); // 123 }
运行后可知,在函数声明中,函数创建前就可以先调用函数。
由于函数声明提升,其实上述语句相当于这样:
function boo(x){ console.log(x); // 123 } boo(123)
而在函数表达式中,则会是另一番结果。
hoo(456) var hoo = function(y){ console.log(y) // Uncaught TypeError: hoo is not a function }
运行后,发现会报错,为什么呢?这是因为在函数表达式中,函数声明并不会提前,它只是变量会提升而已。所以上述语句相当于:
var hoo; hoo(456); hoo = function(y){ console.log(y) // Uncaught TypeError: hoo is not a function }
讲到函数声明提升与变量声明提升,这里来举另外一个例子。
var foo = function(){ console.log(123) } function foo(){ console.log(456) } foo()
最初我一看到这个,毫不犹豫的就说这个结果是456,然而很遗憾,是错的。下面来分析一下,虽然两个函数的函数名是一样的,但是第一种方法是函数表达式,第二种是函数声明,鉴于变量声明提升和函数声明提升,所以上述语句其实相当于:
var foo; // 变量声明提升 function foo(){ // 函数声明提升 console.log(456) } foo = function(){ // 变量赋值依然保留在原来位置 console.log(123) } foo()
所以结果显而易见,是123喽。
最后补一个容易混淆的。
function Foo(){ getName = function(){ console.log(1); } return this; } Foo.getName = function(){ console.log(2) } Foo.prototype.getName = function(){ console.log(3) } var getName = function(){ console.log(4) } function getName(){ console.log(5) } Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1 foo()执行完成后,将全局的getName也就是window.getName给更改后返回this,而在这里this执行的就是window,所以最后执行的就是window.getName,所以输出1
getName(); // 1 上面已经更改全局的getName,所以依然是1
new Foo.getName(); // 2 new 操作符在实例化构造器的时候,会执行构造器函数,也就是说,foo.getName会执行,输出2
new Foo().getName(); // 3 先new foo()得到一个实例,然后再执行实例的getName方法,这个时候,实例的构造器里没有getName方法,就会执行构造器原型上的getName方法
new new Foo().getName() // 3