`nouvelle fonction ()` avec "f" minuscule en JavaScript

106

Mon collègue a utilisé "new function ()" avec un "f" minuscule pour définir de nouveaux objets en JavaScript. Il semble bien fonctionner dans tous les principaux navigateurs et il semble également être assez efficace pour masquer les variables privées. Voici un exemple:

    var someObj = new function () {
        var inner = 'some value';
        this.foo = 'blah';

        this.get_inner = function () {
            return inner;
        };

        this.set_inner = function (s) {
            inner = s;
        };
    };

Dès que "this" est utilisé, il devient une propriété publique de someObj. Donc someObj.foo, someObj.get_inner () et someObj.set_inner () sont tous disponibles publiquement. De plus, set_inner () et get_inner () sont des méthodes privilégiées, elles ont donc accès à "interne" grâce aux fermetures.

Cependant, je n'ai vu aucune référence à cette technique nulle part. Même JSLint de Douglas Crockford s'en plaint:

  • construction étrange. Supprimer «nouveau»

Nous utilisons cette technique en production et elle semble bien fonctionner, mais je suis un peu inquiète à ce sujet car elle n'est documentée nulle part. Est-ce que quelqu'un sait si c'est une technique valide?

Johnny Oshika
la source
6
Je préfère votre construction à l'IIFE ('Immediately-Invoked Function'). 1: Vous n'avez pas besoin d'un objet «instance» explicite, c'est exactement ce que «ceci» est en JavaScript. 2: Vous n'avez rien à retourner, ce qui signifie que vous n'avez pas besoin de vous en souvenir. Même l'auteur de la réponse acceptée a oublié de renvoyer l'objet d'instance au départ! Les gens préfèrent généralement utiliser un IIFE s'ils détestent le nouveau et ceci, pour une bonne raison - Si vous avez une fonction gérant un événement DOM, thisse référera à l'élément qui a déclenché l'événement, pas à votre objet, mais vous pourriez simplement l'avoir à la var instance = thisplace.
Lee Kowalkowski
1
Pourquoi est-il important pour la question de spécifier «f minuscule»?
ClearCloud8
7
Car en Javascript, il existe aussi la fonction 'Function' (avec F majuscule), qui est différente: Function est une fonction constructeur qui peut créer de nouveaux objets fonction, alors que function est un mot-clé.
Stijn de Witt
@Bergi J'ai lu vos liens. Je ne vois aucune raison de discréditer ce modèle. C'est valable. C'est simple. Alors, qu'est-ce qui ne va pas. JSLint se plaint de tout BTW :)
Stijn de Witt

Réponses:

64

J'ai déjà vu cette technique, elle est valide, vous utilisez une expression de fonction comme s'il s'agissait d'une fonction constructeur .

Mais à mon humble avis, vous pouvez obtenir la même chose avec une expression de fonction à invocation automatique, je ne vois pas vraiment l'intérêt d'utiliser l' newopérateur de cette manière:

var someObj = (function () {
    var instance = {},
        inner = 'some value';

    instance.foo = 'blah';

    instance.get_inner = function () {
        return inner;
    };

    instance.set_inner = function (s) {
        inner = s;
    };

    return instance;
})();

Le but de l' newopérateur est de créer de nouvelles instances d'objet, de configurer la [[Prototype]]propriété interne, vous pouvez voir comment cela est fait par la [Construct]propriété interne.

Le code ci-dessus produira un résultat équivalent.

CMS
la source
1
La spécification ECMAScript 262 de la section 13 explique cela un peu plus formellement. Quelque chose comme function foo () {}renvoie le résultat de la création d'un Functionobjet [vraisemblablement avec new Function ()]. C'est du sucre de syntaxe.
Clinton Pierce
3
Je pense qu'il vous manque un return instance;à la fin. Sinon, someObjsera juste undefined. :-)
Matthew Crumley
1
Puis-je suggérer que si vous vous souciez de la modularité et de la dissimulation d'informations, abandonnez-vous et commencez à utiliser quelque chose comme require.js? Vous êtes à mi-chemin, pourquoi vous arrêter ici? La définition de module asynchrone (qui est ce que require.js implémente) prend en charge ce cas d'utilisation et vous donne un ensemble d'outils complet pour gérer la portée, l'espace de noms et la gestion des dépendances.
Stijn de Witt
Notez que les parenthèses entourant la déclaration de fonction ne sont pas nécessaires car l'instruction est déjà une expression en raison de la présence de=
Explosion Pills
@StijndeWitt à mi-chemin signifie que vous devrez faire deux fois plus de travail pour utiliser require.js, mais cela pourrait être tout ce dont vous avez besoin dans des cas simples.
xr280xr
15

Votre code est juste similaire à la construction la moins étrange

function Foo () {
    var inner = 'some value';
    this.foo = 'blah';

    ...
};
var someObj = new Foo;
KennyTM
la source
9
Ce n'est pas seulement similaire, cela fait exactement la même chose ... à la seule exception qu'ils ne pourront pas réutiliser Foo pour créer un autre objet.
kikito
3
La version de l'OP pourrait être réutilisée via le nouveau someObj.constructor . Ici, le constructeur est ajouté explicitement à l'espace de noms; le bon style dépend de l'objectif de la fonction. De plus, ce style - bien que certainement le standard - permet à quelqu'un de remplir l'espace de noms global s'il oublie le nouveau avant Foo .
J Bryan Price
@kikito que voulez-vous dire, cela ne permet pas de réutiliser Foo pour créer un autre objet? var newObj = new Foo () devrait créer une nouvelle instance.
Bill Yang
1
@BillYang C'était il y a 5 ans. Aucune idée. Depuis, je n'ai plus touché au javascript.
kikito
12

Pour clarifier certains aspects et empêcher JSLint de Douglas Crockford de se plaindre de votre code, voici quelques exemples d'instanciation:

1. o = new Object(); // normal call of a constructor

2. o = new Object;   // accepted call of a constructor

3. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
})(); // normal call of a constructor

4. var someObj = new (function () {  
    var inner = 'some value';
    this.foo = 'blah';

    this.get_inner = function () {
        return inner;
    };

    this.set_inner = function (s) {
        inner = s;
    };
}); // accepted call of a constructor

Dans l'exemple 3. expression dans (...) as value est une fonction / constructeur. Cela ressemble à ceci: new (function () {...}) (). Donc, si nous omettons les crochets de fin comme dans l'exemple 2, l'expression est toujours un appel de constructeur valide et ressemble à l'exemple 4.

JSLint de Douglas Crockford "pense" que vous vouliez assigner la fonction à someObj, pas à son instance. Et après tout, ce n'est qu'un avertissement, pas une erreur.

DUzun
la source