Bonsoir toutes et tous,

Voici des observations que j’ai tirées de mes propres tests, en lien avec la conversation l’opérateur new, bonne ou mauvaise pratique ?
J’ai estimé que c’était suffisamment hors-sujet pour mériter l’ouverture d’un nouveau fil.

Ces observations mettent en lumière un certain nombre de curiosités et d’incohérences du langage.

Quand on crée une fonction avec le mot-clé function (même sans intention d’utiliser cette fonction comme un constructeur), cette fonction reçoit une propriété prototype. Cette propriété est un objet, héritant de Object.prototype, et possédant une propriété propre constructor qui référence la fonction qu’on vient de créer.

Pour lever toute ambiguïté, je rappelle que la propriété prototype de la fonction n’est pas le prototype de cette fonction ; il s’agit en réalité du prototype des objets qui sont créés par cette fonction.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
function f() {}
var o = new f();
console.log(f.prototype === Object.getPrototypeOf(o)); // true
Le prototype des fonctions, Function.prototype, n’a lui-même pas de propriété prototype. À force d’y réfléchir ça a fini par me coûter cher en aspirine me paraître logique si on admet l’explication suivante : les fonctions reçoivent leur propriété prototype au moment où elles sont construites. Le mot important ici est « construites », ce qui signifie qu’elles ont un constructeur, et donc que, d’une certaine façon, les fonctions sont des objets.
D’autres observations qui vont dans ce sens :
  • Les fonctions sont passées par référence, contairement aux types primitifs Number, Boolean, String, qui sont passés par copie.
  • instanceof marche avec les fonctions, alors que, par exemple, "" instanceof String renvoie false, ce qui est contre-intuitif.
  • Les types primitifs sont « encapsulables » :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var s = "abc";
    console.log(typeof s); // "string"
     
    var objS = new String("abc"); // chaîne encapsulée
    console.log(typeof objS); // "object"
    … Mais les fonctions, elles, ne sont pas encapsulables. (Pour la simple raison que leur constructeur ne fait pas d’encapsulation. Quand on passe une fonction à Function, elle est convertie en chaîne et traitée comme une fonction-instruction dans le corps de la nouvelle fonction, ce qui donne lieu à une situation côcasse : on est obligé de nommer la fonction qu’on passe, et on reçoit en retour une fonction anonyme.)

(Note : n’essayer pas d’encapsuler des valeurs chez vous les enfants, c’est extrêmement dangereux inutile et je décline toute responsabilité.)

Par contre les fonctions ont leur propre valeur pour typeof, contrairement à Array par exemple, ce qui penche plutôt en faveur de « les fonctions sont un type primitif ».

J’ai tenté d’instancier un objet à partir du prototype des fonctions, j’ai obtenu une erreur peu commune :
TypeError: Function.prototype is not a constructor
Il y a un seul autre cas où on peut obtenir cette erreur : quand on tente de créer un objet avec une fonction-flèche, () => {}. (Les fonctions-flèches sont apparues avec ECMAScript 2015.)

Quand on spécialise les fonctions, elles perdent leur caractère invocable.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
function SubFunction () {}
SubFunction.prototype = function () {};
 
var sf = new SubFunction();
sf(); // TypeError: sf is not a function
Même résultat avec Object.create :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
var sf2 = Object.create(function () {});
sf2(); // TypeError: sf2 is not a function
C’est un peu dommage quand on y pense, car ce serait intéressant de pouvoir invoquer n’importe quoi comme une fonction. Ça ajouterait encore plus de bordel souplesse au langage. PHP, par exemple, permet de faire ça en ajoutant une « méthode magique » __invoke à une définition de classe. JS pourrait faire ça en proposant, par exemple, l’ajout du symbole connu Symbol.invoke, ou en ajoutant une trappe invoke sur les proxys.


Voilà, c’est un peu décousu, désolé. En tout cas n’hésitez pas si vous avez des remarques à faire, des réflexions à partager ou des questions à poser