为什么我的 JavaScript 函数名冲突?

我编写了下面的脚本,只是为了看看当一个变量和一个函数的名称发生冲突时会发生什么:

var f = function() {
console.log("Me original.");
}


function f() {
console.log("Me duplicate.");
}


f();

我得到的输出是“我原创的”。为什么没有调用另一个函数?

另外,如果我将原始赋值改为 var f = new function() {,我会得到“ Me first”,后面跟着一个 TypeError,表示 object is not a function。有人能解释一下吗?

10870 次浏览

Function declarations are hoisted (moved to the top) in JavaScript. While incorrect in terms of parsing order, the code you have is semantically the same as the following since function declarations are hoisted:

function f() {
console.log("Me duplicate.");
}
var f = function() {
console.log("Me original.");
}




f();

Which in turn, with the exception of the function's name is the same as:

var f = function() {
console.log("Me duplicate.");
}
var f = function() {
console.log("Me original.");
}




f();

Which in turn, because of variable hoisting is the same as:

var f;
f = function() {
console.log("Me duplicate.");
}
f = function() {
console.log("Me original.");
}


f();

Which explains what you're getting, you're overriding the function. More generally, multiple var declarations are allowed in JavaScript - var x = 3; var x = 5 is perfectly legal. In the new ECMAScript 6 standard, let statements forbid this.

This article by @kangax does a fantastic job in demystifying functions in javascript

If doesn't look like anyone answered your follow-up question so I'll answer it here, though you should generally ask follow-up questions as separate questions.

You asked why this:

var f = new function() {
console.log("Me original.");
}


function f() {
console.log("Me duplicate.");
}


f();

prints out "Me original." and then an error.

What is happening here is that the new causes the function to be used as a constructor. So this is equivalent to the following:

function myConstructor() {
console.log("Me original.");
}
var f = new myConstructor();


function f() {
console.log("Me duplicate.");
}


f();

And thanks to the function hoisting that Benjamin explained, the above is essentially equivalent to this:

var myConstructor = function() {
console.log("Me original.");
};
var f = function() {
console.log("Me duplicate.");
};


f = new myConstructor();


f();

This expression:

var f = new function() {
console.log("Me original.");
}

causes a new object to be constructed and assigned to f, using an anonymous function as the constructor. "Me original." is printed out as the constructor executes. But the object that is constructed is not itself a function, so when this eventually executes:

f();

you get an error, because f is not a function.

Forgive me if this is the wrong way to approach adding a point. I haven't been around here all the much, and would welcome constructive direction and/or criticism.

Benjamin's answer addresses the OP's question excellently, but I'd like to add one tweak that'll give us a full tour of hoisting and its oddities.

If we begin the original code with a call to f, like so:

f();


var f = function() {
console.log("Me original.");
};


function f() {
console.log("Me duplicate.");
}


f();

The output will then be:

Me duplicate.
Me original.

The reason being that var and function statements are hoisted in slightly different ways.

For var the declaration is moved to the top of the current scope*, but any assignment is not hoisted. As far as the value of the declared var goes, it's undefined until the original assignment line is reached.

For functionstatements, both the declaration and definition are hoisted. Function expressions, as used in the var f = function() {... construct, are not hoisted.

So after hoisting, execution is as if the code were:

var f; // declares var f, but does not assign it.


// name and define function f, shadowing the variable
function f() {
console.log("Me duplicate.");
}


// call the currently defined function f
f();


// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() {
console.log("Me original.");
}


// calls the function referenced by the var f
f();

*All JavaScript scope is lexical, or function, scope, but it seemed like it would just confuse things to use the f word at that point.