Pourquoi mes noms de fonctions JavaScript sont-ils en conflit?

97

J'ai écrit le script suivant juste pour voir ce qui se passe lorsqu'une variable et une fonction à laquelle une fonction est assignée ont leur nom en conflit:

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

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

f();

Le résultat que j'obtiens est "Moi original". Pourquoi l'autre fonction n'a-t-elle pas été appelée?

De plus, si je change mon affectation d'origine en var f = new function() {, j'obtiens "Moi original", suivi d'un message TypeError object is not a function. Quelqu'un peut-il expliquer?

ankush981
la source
26
@ Dean.DePue - Il n'y a pas de confusion de la part de JavaScript. Les règles de traitement sont assez claires (et expliquées par Benjamin dans sa réponse).
Quentin
4
La curiosité, toujours le meilleur moyen d'apprendre une langue. :-D
Cerbrus
2
Aussi, j'imagine qu'il est assez impossible pour quelque chose d'aussi immatériel que "JavaScript" de "se sentir" confus (ou toute émotion, d'ailleurs) ;-)
Cerbrus
2
Pourquoi le levage devrait-il inverser l'ordre dans le deuxième exemple?
Cerbrus
5
Étapes pour approfondir vos connaissances en javascript: 1) Utilisez 'use strict' 2) Utilisez toujours jslint ou jshint 3) Recherchez les choses dont jslint ou jshint se plaint 4) Rincez et répétez
steve-er-rino

Réponses:

170

Les déclarations de fonction sont hissées (déplacées vers le haut) en JavaScript. Bien qu'il soit incorrect en termes d'ordre d'analyse, le code que vous avez est sémantiquement le même que le suivant puisque les déclarations de fonction sont levées:

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


f();

Ce qui à son tour, à l'exception du nom de la fonction, est le même que:

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


f();

Ce qui à son tour, en raison du levage variable, est le même que:

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

f();

Ce qui explique ce que vous obtenez, vous remplacez la fonction. Plus généralement, plusieurs vardéclarations sont autorisées en JavaScript - var x = 3; var x = 5c'est parfaitement légal. Dans la nouvelle norme ECMAScript 6, les letdéclarations l'interdisent.

Cet article de @kangax fait un travail fantastique dans la démystification des fonctions en javascript

Benjamin Gruenbaum
la source
2
Pouvez - vous simplifier vraiment function f()à var f = function()ce point? Les noms de levage et de fonction sont-ils vraiment la seule différence?
djechlin
6
@djechlin dans le contexte de cette question - oui. Généralement, c'est plus subtil - voir stackoverflow.com/questions/336859/… . Du point de vue du compilateur, ils sont différents - mais du point de vue du programmeur - nous en sommes suffisamment proches pour le prétendre. C'est pourquoi j'ai ajouté ce long "bien que incorrect en termes d'ordre d'analyse, le code que vous avez est sémantiquement le même que" au lieu de dire "est le même que". Bon point.
Benjamin Gruenbaum
1
@dotslash s'il vous plaît ne modifiez pas votre question d'origine et ne la changez pas, c'est considéré comme de mauvaise manière ici - aussi, mélanger plusieurs questions en une est également considéré comme de mauvaise manière ici. Vous pouvez poser une nouvelle question à la place ou, si vous pensez que c'est trop mineur, demander des éclaircissements dans les commentaires (c'est à cela qu'ils servent de toute façon). Dans le code ci-dessus, les deux versions de fsont hissées, et la "Me Original"version est simplement hissée plus tard , elles sont chacune déplacées vers le haut mais dans le même ordre. Je voudrais juste ajouter qu'en général, il ne faut pas nommer plusieurs fonctions de la même manière :)
Benjamin Gruenbaum
5
En mode strict, vous ne pouvez pas avoir varle même nom deux fois dans la même portée.
Hoffmann
4
"Cela devrait être évident" - pour vous peut-être, mais ce n'était pas évident pour moi à un moment donné, et ce n'était pas évident pour OP quand il l'a demandé, et le nommage, et plus généralement la façon dont l'environnement lexical est géré en JavaScript en était un des choses les plus difficiles à comprendre lors de l'apprentissage de JavaScript pour moi. Je ne serais pas si prompt à insulter les gens qui ne le comprennent pas.
Benjamin Gruenbaum
10

Si personne ne semble avoir répondu à votre question de suivi, je vais donc y répondre ici, mais vous devriez généralement poser des questions de suivi sous forme de questions distinctes.

Vous avez demandé pourquoi ceci:

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

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

f();

imprime «Moi original». puis une erreur.

Ce qui se passe ici, c'est que la newfonction est utilisée comme constructeur. Cela équivaut donc à ce qui suit:

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

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

f();

Et grâce à la fonction de levage expliquée par Benjamin, ce qui précède est essentiellement équivalent à ceci:

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

f = new myConstructor();

f();

Cette expression:

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

provoque la construction et l'affectation d'un nouvel objet f, en utilisant une fonction anonyme comme constructeur. "Moi original." est imprimé lors de l'exécution du constructeur. Mais l'objet qui est construit n'est pas lui-même une fonction, donc quand cela s'exécute finalement:

f();

vous obtenez une erreur, car ce fn'est pas une fonction.

JLRishe
la source
Oh, merveilleux! Merci beaucoup d'avoir pris la peine d'y répondre! :) :)
ankush981
2

Pardonnez-moi si ce n'est pas la bonne façon d'aborder l'ajout d'un point. Je n'ai pas beaucoup été ici et j'accepterais une direction constructive et / ou des critiques.

La réponse de Benjamin répond parfaitement à la question du PO, mais j'aimerais ajouter une modification qui nous donnera un tour complet du levage et de ses bizarreries.

Si nous commençons le code d'origine par un appel à f, comme ceci:

f();

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

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

f();

La sortie sera alors:

Me duplicate.
Me original.

La raison étant que varetfunction déclarations sont soulevées de manière légèrement différente.

Pour var la déclaration est déplacée en haut de la portée actuelle *, mais toute affectation n'est levée. En ce qui concerne la valeur de la variable déclarée, elle n'est pas définie tant que la ligne d'affectation d'origine n'est pas atteinte.

Pour function déclarations , la déclaration et la définition sont hissées. Expressions de fonction , telles qu'utilisées dansvar f = function() {... construction, ne sont pas hissées.

Donc, après le levage, l'exécution est comme si le code était:

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

* Toute la portée JavaScript est lexicale, ou fonction, portée, mais il semblait que cela ne ferait que confondre les choses pour utiliser le mot f à ce stade.

codélahome
la source