web-dev-qa-db-fra.com

Fonction anonyme auto-exécutable vs prototype

En Javascript, il existe quelques techniques clairement importantes pour créer et gérer des classes/espaces de noms en javascript.

Je suis curieux de savoir quelles situations justifient l'utilisation d'une technique par rapport à l'autre. Je veux en choisir un et m'en tenir à aller de l'avant.

J'écris du code d'entreprise qui est maintenu et partagé entre plusieurs équipes, et je veux savoir quelle est la meilleure pratique lors de l'écriture de javascript maintenable?

J'ai tendance à préférer les fonctions anonymes auto-exécutables, mais je suis curieux de savoir quel est le vote de la communauté sur ces techniques.

Prototype:

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Fonction anonyme à fermeture automatique:

//Self-Executing Anonymous Function 
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }     
}( window.skillet = window.skillet || {}, jQuery ));   
//Public Properties      
console.log( skillet.ingredient ); //Bacon Strips  

//Public Methods 
skillet.fry(); //Adding Butter & Fraying Bacon Strips 

//Adding a Public Property 
skillet.quantity = "12"; console.log( skillet.quantity ); //12   

//Adding New Functionality to the Skillet 
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
     };     

}( window.skillet = window.skillet || {}, jQuery ));
//end of skillet definition


try {
    //12 Bacon Strips & 1 Cup of Grease
    skillet.toString(); //Throws Exception 
} catch( e ) {
    console.log( e.message ); //isHot is not defined
}

Je pense que je dois mentionner que la fonction anonyme auto-exécutable est le modèle utilisé par l'équipe jQuery.

pdate Quand j'ai posé cette question, je n'ai pas vraiment vu l'importance de ce que j'essayais de comprendre. Le vrai problème est de savoir s'il faut utiliser new pour créer des instances de vos objets ou utiliser des modèles qui ne nécessitent pas de constructeurs/utilisation du mot clé new.

J'ai ajouté ma propre réponse, car à mon avis, nous devrions utiliser des modèles qui n'utilisent pas le mot clé new.

Pour plus d'informations, veuillez consulter ma réponse.

26
Robotsushi

Mise à jour J'ai maintenant une bien meilleure compréhension de javascript et je pense pouvoir répondre correctement à la question. Je pense que c'était un sujet javascript mal rédigé mais très important à aborder.

Le modèle de fonction anonyme auto-exécutable n'est pas celui qui nécessite l'utilisation du nouveau mot-clé si vous évitez l'utilisation de cela en dehors des fonctions. Je suis d'accord avec l'idée que l'utilisation de new est une ancienne technique et nous devrions plutôt nous efforcer d'utiliser des modèles qui évitent l'utilisation de new.

La fonction anonyme auto-exécutable répond à ces critères.

La réponse à cette question est subjective, car il existe de nombreux styles de codage en javascript. Cependant, sur la base de mes recherches et de mon expérience, je recommanderais de choisir d'utiliser la fonction anonyme auto-exécutable pour définir vos API et éviter l'utilisation de nouvelles dans la mesure du possible.

0
Robotsushi

Les fonctions anonymes auto-exécutables sont utilisées pour automatiser l'exécution de script sans se connecter à des événements externes (c'est-à-dire window.onload).

Dans cet exemple, il est utilisé pour former le modèle de module classique, dont le but principal est d'introduire un espace de noms dans l'environnement global et de fournir encapsulation pour toutes les propriétés internes qui ne sont pas "exportées" ou attachées à l'espace de noms.

La modification d'un prototype d'objets, d'autre part, est utilisée pour établir héritage (ou étendre les natifs). Ce modèle est utilisé pour produire des objets 1: n avec des méthodes ou des propriétés communes.

Vous ne devez pas choisir un modèle dans la préférence à l'autre, car ils effectuent des tâches différentes. En termes d'espaces de noms, la fonction d'auto-exécution est un choix approprié.

22
sunwukung

Voici le modèle que je viens de commencer à utiliser (j'en ai utilisé des variantes jusqu'à hier):

function MyClass() {
    // attributes
    var privateVar = null;

    // function implementations
    function myPublicFunction() {
    }

    function myPrivateFunction() {
    }

    // public declarations
    this.myPublicFunction = myPublicFunction;
}

MyClass.prototype = new ParentClass(); // if required

Quelques réflexions à ce sujet:

  1. Vous ne devriez pas obtenir de (anonymous) traces dans votre pile de débogueur traces comme tout est nommé (pas de fonctions anonymes).
  2. C'est le motif le plus propre que j'ai jamais vu
  3. Vous pouvez facilement regrouper votre API exposée sans que leurs implémentations soient couplées à la déclaration (ce qui signifie que quelqu'un peut facilement bloquer votre interface de classe publique sans avoir à faire défiler)

La seule fois où j'utiliserais prototype est vraiment de définir l'héritage.

4
Demian Brecht

J'utilise prototypes parce qu'ils sont plus propres et suivent des modèles d'héritage standard. Les fonctions auto-appelantes sont idéales pour le développement d'un navigateur ou dans une situation où vous ne savez pas où le code est exécuté, mais sinon c'est juste du bruit.

Exemple:

var me;

function MyObject () {
    this.name = "Something";
}

MyObject.prototype.speak = function speak () {
    return "Hello, my name is " + this.name;
};

me = new MyObject();
me.name = "Joshua";
alert(me.speak());
3
Josh K

J'irais avec la fonction auto-exécutable, mais avec une légère différence:

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

     return function MyClass() {
         this.methodOne = methodOne;
         this.methodTwo = methodTwo;
         this.publicProperty = publicProperty;
     };
})();

Si je trouve cette approche beaucoup plus propre, car je sépare la variable globale renvoyée de tout paramètre d'entrée (tel que jQuery) (la façon dont vous l'écrivez équivaut à renvoyer void et à utiliser un paramètre ref en C #, ce que je trouve un peu décalé, ou en passant un pointeur sur un pointeur et en le réaffectant en C++). Si je devais ensuite attacher des méthodes ou des propriétés supplémentaires à la classe, j'utiliserais l'héritage prototypique (exemple avec la méthode $ .extend de jQuery, mais il est assez facile de rouler votre propre extend ()):

var additionalClassMethods = (function () {
    var additionalMethod = function () { alert('Test Method'); };
    return { additionalMethod: additionalMethod };
})();

$.extend(MyClass.prototype, additionalClassMethods);

var m = new MyClass();
m.additionalMethod(); // Pops out "Test Method"

De cette façon, vous avez une distinction claire entre les méthodes ajoutées et les méthodes originales.

1
Ed James

exemple en direct

(function _anonymouswrapper(undefined) {

    var Skillet = {
        constructor: function (options) {
            options && extend(this, options);
            return this; 
        },
        ingredient: "Bacon Strips",
        _isHot: true,
        fry: function fry(oliveOil) {
            this._addItem("\t\n Butter \n\t");
            this._addItem(oliveOil);
            this._addItem(this.ingredient);
            console.log("Frying " + this.ingredient);
        },
        _addItem: function addItem(item) {
            console.log("Adding " + item.toString().trim());
        }
    };

    var skillet = Object.create(Skillet).constructor();

    console.log(skillet.ingredient);
    skillet.fry("olive oil");

    var PrintableSkillet = extend(Object.create(Skillet), {
        constructor: function constructor(options) {
            options && extend(this, options);
            return this;
        },
        _amountOfGrease: "1 Cup",
        quantity: 12,
        toString: function toString() {
            console.log(this.quantity + " " +
                        this.ingredient + " & " +
                        this._amountOfGrease + " of Grease");
            console.log(this._isHot ? "Hot" : "Cold");
        }
    });

    var skillet = Object.create(PrintableSkillet).constructor();

    skillet.toString();

    function extend(target, source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            var pd = Object.getOwnPropertyDescriptor(source, name);
            Object.defineProperty(target, name, pd);
        });
        return target;
    }
}());

Vous pouvez utiliser un IIFE pour émuler la "portée du module" autour de votre code. Ensuite, vous pouvez simplement utiliser des objets comme vous le faites normalement.

Ne pas "émuler" l'état privé à l'aide de fermetures car cela a une grande pénalité de mémoire.

Si vous écrivez une application d'entreprise et que vous souhaitez conserver votre utilisation de la mémoire sous 1 Go, évitez d'utiliser inutilement des fermetures pour stocker l'état.

1
Raynos

Voici comment je procéderais à ce sujet IIFESelfExecutingFunction pour étendre le Myclass avec Myclass.anotherFunction();

MyClass = (function() {
     var methodOne = function () {};
     var methodTwo = function () {};
     var privateProperty = "private";
     var publicProperty = "public";

    //Returning the anonymous object {} all the methods and properties are reflected in Myclass constructor that you want public those no included became hidden through closure; 
     return {
         methodOne = methodOne;
         methodTwo = methodTwo;
         publicProperty = publicProperty;
     };
})();

@@@@@@@@@@@@@@@@@@@@@@@@
//then define another IIFE SEF to add var function=anothermethod(){};
(function(obj){
return obj.anotherfunction=function(){console.log("Added to Myclass.anotherfunction");};
})(Myclass);

//the last bit : (function(obj){})(Myclass); obj === Myclass obj is an alias to pass the Myclass to IIFE

var myclass = new Myclass();
myclass.anotherfunction(); //"Added to Myclass.anotherfunction"
0
oneLINE