首页 > 请问下面这两种函数调用方式有什么区别的

请问下面这两种函数调用方式有什么区别的

都可以弹出alert,但是上面那个在控制台输出了undefined,是怎么回事?

-----update-----
多谢大家的耐心解答,起初我的疑惑就是foo这个对象定义的先后顺序问题,因为使用非hoisted的函数定义方式在chrome的控制台中仍然可以正常运行而没有报TypeError。后来我新建文件之后再测试是完全没问题的。所以这里导致困惑的主要就是chrome对undefined输出的解释了。再次感谢@BetaRabbit


foo();
var foo=function(){
...
}

变量foo提升到顶部

等价为==>

var foo;
foo();
foo=function(){
}

会执行报错 TypeError: foo is not a function. (In 'foo()', 'foo' is undefined)

你说的执行输出了undefined,要看下之前的代码是什么样的!
是不是之前已经定义过foo函数了

function foo(){
}

而对于

foo();
function foo(){
....
}

函数foo声明提升到顶部
等价为

function foo(){
....
}
foo();

执行正常~~~


function关键字声明的函数,会有一个函数声明提升的过程,所以你可以在声明函数之前进行调用。如果使用函数表达式来定义函数,只能在定义之后来调用,原因在于会存在一个变量声明提升。

//2. 报错,TypeError: foo is not a function
foo();
//1.在该执行环境下,声明的所有变量比如foo会被提升到顶部而未执行赋值语句,所以此时foo值为undefined而并非函数的引用。
var foo = function () {
    alert('hello world!');
}

变量定义提升与函数声明提升


楼上的答案都是错的,这个和hoist没有关系,和函数表达式和函数声明也没有关系。
真正的原因是,语言规范要求,返回undefined

Syntax

VariableStatement :
var VariableDeclarationList ;

Semantics

The production VariableStatement : var VariableDeclarationList ; is evaluated as follows:

Evaluate VariableDeclarationList.
Return (normal, empty, empty).

节选自http://www.ecma-international.org/ecma-262/5.1/#sec-12.2

最后的(normal, empty, empty)是什么意思呢,规范里面叫The Completion Specification Type

The Completion type is used to explain the behaviour of statements (break, continue, return and throw) that perform nonlocal transfers of control. Values of the Completion type are triples of the form (type, value, target), where type is one of normal, break, continue, return, or throw, value is any ECMAScript language value or empty, and target is any ECMAScript identifier or empty. If cv is a completion value then cv.type, cv.value, and cv.target may be used to directly refer to its constituent values.

综上所述,返回值的value是个empty,具体到javascript里面,就是undefined了。

PS: 类似会返回(normal, empty, empty)还有很多,比如,
{}: 空block(不是对象)
;: 空语句


虽然一般情况下Javascript的编译和运行时的界限很模糊,但为了回答你的问题,我在这里还是想区分一下这两个概念。

编译器在看到下面例子里的函数时,它知道你在定义一个函数,这个函数名叫foo,foo将在程序运行的始终绑定到这个函数(因为他是这个函数的名字)。所以foo是这个函数的名字,他绑定到这个函数
而上面这个例子,编译器定义了一个匿名函数,它没有名字,尔后的赋值将这个匿名函数对象赋值给了变量foo,所以foo是一个变量,里面储存的是这个函数

另, 他们有一个挺大的区别:

> function foo(){}
undefined
> foo.name
'foo'
> bar = function() {}
[Function]
> bar.name
''

最后提供一篇文章: http://davidbcalhoun.com/2011/different-ways-of-defining-functions-in-javascript-this-is-madness/, 作者总结的很详细,关于JS里各种函数定义的方法及其细微区别

UPDATE:
手机上码字写着写着就顺着自己思路来了,其实只回答了题主一半的问题:
1。 第一次foo()的调用应该是会失败的,有同学已经指出了,我们猜测题主之前可能已经定义过这个foo,所以第一次调用foo时才居然没有失败。非hoisted的函数定义方式只能先定义再使用,之前所答其实主要就是解释了这个
2。关于undefined的behavior,有同学从JS语言规范的角度解释了。但是简单理解的话,对于赋值语句和函数定义语句来说,他们是一种操作,没有值这是理所应该的吧。简单看看这个例子:

// in chrome dev console
var p = 13;    //题主的第一个例子是赋值一个函数对象,为了清晰我这里就i赋值一个整数
=> undefined
function foo() {}
=> undefined

另外题主第二个例子里没有undefined输出,我开始也很奇怪,所以在chrome里试了试,于是我想说题主你把那个alert对话框点掉后再看看呢 :-)


所有声明变量或声明函数都会被提升到当前函数的顶部。MDN链接地址

foo();
function foo() {
    alert(111);
}
//相当于
function foo() {
    alert(111);
}
foo();


//但是另外一个
foo();
var foo = function() {
    alert(111);
}
//相当于
var foo;
foo();
foo = function() {
    alert(111);
}


//Function declarations in JavaScript are hoisting the function definition. 
//你可以在函数声明之前使用函数:

hoisted(); // logs "foo"

function hoisted() {
  console.log("foo");
}
     
//注意 function expressions 不会提升:

notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function() {
   console.log("bar");
};
【热门文章】
【热门文章】