web-dev-qa-db-fra.com

Pourquoi les noms de mes fonctions JavaScript s'affrontent-ils?

J'ai écrit le script suivant juste pour voir ce qui se passe lorsqu'une variable et une fonction à laquelle une fonction lui est affectée voient leurs noms s'affronter:

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

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

f();

La sortie que j'obtiens est "Moi d'origine". 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 "Me original", suivi d'une erreur TypeError disant object is not a function. Quelqu'un peut-il expliquer?

97
dotslash

Les déclarations de fonctions 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, car les déclarations de fonction sont hissées:

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


f();

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 déclarations var sont autorisées en JavaScript - var x = 3; var x = 5 est parfaitement légal. Dans la nouvelle norme ECMAScript 6, les instructions let l'interdisent.

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

171

Si rien ne semble que quelqu'un ait répondu à votre question de suivi, je vais y répondre ici, bien que vous devriez généralement poser des questions de suivi en tant que questions distinctes.

Vous avez demandé pourquoi cela:

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 new fait que la fonction est utilisée comme constructeur. C'est donc l'équivalent de 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 d'un nouvel objet et son affectation à 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 lorsque cela s'exécute finalement:

f();

vous obtenez une erreur, car f n'est pas une fonction.

10
JLRishe

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'accueillerais favorablement une direction et/ou une critique constructive.

La réponse de Benjamin répond parfaitement à la question du PO, mais j'aimerais ajouter un Tweak qui nous donnera une visite complète 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 les instructions var et function sont hissées de manières légèrement différentes.

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

Pour functioninstructions, la définition de la déclaration et est hissée. La fonction expressions, telle qu'elle est utilisée dans la construction var f = function() {..., n'est pas hissée.

Ainsi, 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.

2
codelahoma