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


97

我编写了以下脚本,目的是查看变量和为其分配了函数的函数名称发生冲突时会发生什么情况:

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

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

f();

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

另外,如果我将原始分配更改为,则会var f = new function() {得到“ Me original”,然后输入TypeError object is not a function。有人可以解释一下吗?


26
@ Dean.DePue — JavaScript方面没有混淆。处理它们的规则非常明确(本杰明在回答中对此进行了解释)。
昆汀2014年

4
好奇心仍然是学习某种语言的最好方法。:-D
Cerbrus 2014年

2
另外,我想像“ JavaScript”这样不重要的东西也很难“感到”困惑(或任何情感);-)
Cerbrus 2014年

2
在第二个示例中,为什么要吊起相反的顺序?
Cerbrus 2014年

5
掌握javascript知识的步骤:1)使用“严格使用” 2)始终使用jslint或jshint 3)查找jslint或jshint抱怨的内容4)冲洗并重复
steve-er-rino

Answers:


170

函数声明在JavaScript中被提升(移至顶部)。尽管解析顺序不正确,但是由于悬挂了函数声明,因此您拥有的代码在语义上与以下代码相同:

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


f();

反过来,除了函数名称之外,该名称与以下内容相同:

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


f();

由于可变吊装,这又与以下内容相同:

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

f();

这就解释了您所得到的,您正在重写该功能。一般来说,varJavaScript中允许使用多个声明- var x = 3; var x = 5完全合法。在新的ECMAScript 6标准中,let声明禁止这样做。

@kangax的这篇文章在javascript中的函数解密方面做得非常出色


2
你能真正简化function f()var f = function()那么多吗?吊装和功能名称真的是唯一的区别吗?
djechlin

6
@djechlin在这个问题的背景下-是的。通常,它比较隐蔽-请参见stackoverflow.com/questions/336859/…。从编译器的角度来看,它们是不同的-但从程序员的角度来看,我们距离足够近,可以声称这一点。这就是为什么我加了这么长的代码:“虽然解析顺序不正确,但您拥有的代码在语义上却与”相同,而不是说“与...相同”。好点子。
本杰明·格林鲍姆

1
@dotslash请不要编辑原始问题并更改它,因为这在这里被认为是不礼貌的-另外,在一个问题中混入多个问题在这里也被认为是不礼貌的。您可以改问一个新问题,或者,如果您认为它太小,请在评论中进行说明(无论如何,这就是它们的意思)。在上面的代码中,两个版本的都f被吊起,而稍后"Me Original"才被吊起,它们分别以相同的顺序移至顶部。我只想补充一点,您不应该以相同的方式命名几个函数:)
Benjamin Gruenbaum 2014年

5
在严格模式下,您不能var在同一范围内两次使用相同的名称。
霍夫曼2014年

4
“那应该是显而易见的”- 也许对您来说,但这一点对我来说并不明显,而当他问及命名和命名时,对OP来说并不明显,更一般地说,如何在JavaScript中管理词汇环境是一个第一次对我学习JavaScript时最难掌握的东西。我不会那么快地侮辱那些不了解它的人。
本杰明·格林鲍姆

10

如果看起来没有人回答您的后续问题,那么我将在这里回答,尽管您通常应该将后续问题作为单独的问题提出。

您问为什么:

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

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

f();

打印出“我的原件”。然后是一个错误。

这里发生的事情是,new导致该函数用作构造函数。因此,这等效于以下内容:

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

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

f();

并感谢本杰明(Benjamin)解释的函数提升,以上内容基本上等效于此:

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

f = new myConstructor();

f();

该表达式:

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

f使用匿名函数作为构造函数,导致构造一个新对象并将其分配给。“我是原始人。” 在构造函数执行时打印出来。但是构造的对象本身并不是一个函数,因此当该对象最终执行时:

f();

您收到错误,因为f不是函数。


哦,太好了!非常感谢您不厌其烦地回答它!:) :)
ankush981 2014年

2

如果这是加分的错误方法,请原谅我。我并不是一直都在这里,我会欢迎建设性的指导和/或批评。

本杰明的回答很好地解决了操作员的问题,但我想添加一个调整,使我们可以全面了解吊装及其奇特之处。

如果我们以调用开头原始代码f,如下所示:

f();

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

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

f();

输出将是:

Me duplicate.
Me original.

原因是,varfunction语句以稍微不同的方式悬挂。

对于var声明被移动到当前范围*顶部,但任何分配不悬挂。就声明的var的值而言,直到到达原始赋值行之前它才是未定义的。

对于functionstatement,将同时声明定义。函数表达式,如var f = function() {...构造中不会被提升。

因此,在吊起之后,执行就好像代码是:

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

*所有JavaScript作用域都是词法作用域或函数作用域,但似乎在那时会混淆使用f词的事物。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.