28 votes

(Open Source) Exemples d'OO prototypique JavaScript

Bounty Edit:

Je suis à la recherche pour le code écrit dans un pur prototype OO paradigme (pensez à Soi-même). Pas un mélange de prototypiques OO et classique OO. Je ne veux pas voir le générique OO wrappers, mais simplement l'utilisation de prototypiques OO techniques et seulement prototypique OO techniques.

Référence Liés À La Question:

Prototype de l'OO en JavaScript

Dans la question ci-dessus je me suis concentré principalement sur

Peut écrire prototypique OO comme ça?

Avons-nous besoin de constructeurs et de l'initialisation de la logique, Quelles sont les alternatives?

Nouvelle question:

Fondamentalement, y a de bons exemples de javascript prototype OO dans les grands projets open source?

Précisions:

Je vais préciser ce que je veux dire avec le prototype de l'OO :

  • Il n'y a pas de classes. Il y a seulement des Objets.
  • Il y a zéro de l'émulation des concepts de classes, c'est de nouveau les objets et le clonage des objets pour créer de nouveaux objets.

De plus amples explications, Prototype de l'OO:

La différence entre le prototype de l'OO en JavaScript et classique OO émulation est une zone très grise. Ce n'est pas que j'ai de la valeur en évitant classique OO. Je veux apprendre le prototype OO académique de la mode dans son propre droit, sans l'apprentissage de la (probablement plus optimale) combinaison de classique OO émulation et prototypique OO.

C'est pourquoi j'ai "l'interdiction" des classes, juste pour que je puisse voir ces techniques dans un mode pur et de prolonger ma propre OO trousse à outils.

Exemples:

Des exemples courants tels que jQuery ne répondent pas à la deuxième critère. L' jQuery objet est une grande classe de l'émulation. Il se concentre sur la création de nouveaux objets à partir d'une classe plutôt que le clonage d'objets existants.

Si en fait je savais que tous les exemples de l'aide "pure" prototypique OO je t'aurais montré. Je crois que 99% de JavaScript OO est trop fortement influencé par les classiques de l'émulation.

Les points de Bonus

Si

  • C'est bien comented / documenté
  • A des tests unitaires
  • Est sur github.

Je vais aussi accepter articles / tutoriels et des exemples sur la façon d'écrire prototypique OO code qui va au-delà de votre trivial de l'application hello world.

9voto

Sean McMillan Points 5096

Vous ne le trouverez pas.

Je suis allé à la recherche pour ce genre de chose il y a longtemps, et c'est ce que j'ai trouvé: l' Auto Papier dans l'Organisation des Programmes Sans Classes (Regardez Citeseer pour une version PDF.) Ce document présente les best practices pour l'Auto, l'original prototypes de la langue, et la meilleure pratique consiste à utiliser les traits de l'objet de l'idiome", qui est d'avoir de vos objets héritent de "traits des objets qui ne contiennent que des méthodes, et pas un objet de données spécifique. En d'autres termes, un objet qui est à s'y méprendre à une classe.

Même l'original prototypes langue émule des classes.

4voto

jordan Points 595

Avez-vous pris un coup d'oeil à OMeta/JS? OMeta est une recherche expérimentale pattern matching langage basé sur le langage Smalltalk et de l'Auto. OMeta/JS est une implémentation en javascript à l'aide de prototype de l'OO.

Il est bien commenté et documenté, avec de nombreux exemples. Il est également disponible sur Github.

http://tinlizzie.org/ometa-js/

https://github.com/alexwarth/ometa-js

Edit: OMeta est le résultat d'Alexandre Warth de Doctorat de disertation.

1voto

Tower Points 20180

Je ne suis pas exactement sûr de ce que vous cherchez, mais mon cadre actuel permet de programmer dans OO de mode de la sorte:

Cin.define({
    name: 'MyApp.Logger',
    extends: 'Cin.Component',
    implements: ['MyApp.ILogger'],
    mixes: {
        SomeMixin: 'MyApp.SomeMixin'
    },

    init: function() {
    },

    method: function() {
    },

    statics: {
        staticMethod: function() {}
    }
});

Et puis vous pouvez écrire du code comme:

var instance = new MyApp.Logger();
instance.method();

MyApp.Logger.staticMethod();

Je n'essaie pas d' imiter classique OO ici. Je suis en train de faire une façon pratique et utile pour déclarer l'héritage, mixin, des interfaces, et en général les concepts OO sorte qu'il devient facile pour le développeur d'écrire ces OO code. Cela me donne aussi la chance de finir mon auto chargement du composant, de sorte que vous n'avez plus à prendre soin de dépendances et vous pouvez faire personnalisé et profitez d'un développement plus rapide grâce à ne pas avoir besoin de charge de 100 scripts pour chaque chargement de la page.

Si vous voulez apprendre prototypique des concepts OO, je pense que vous devriez écrire une sorte de système d'héritage. Prendre un coup d'oeil au Dojo Toolkit ou ExtJS. Une bonne chose à retenir est que basé sur des Prototypes de systèmes de torsion et de mutilation, ils sont plus puissants que la Classe de base des langages à objets. À mon avis, il n'y a pas une seule bonne façon d'écrire des prototypes de code.

Je crains bien que la plupart, si pas tous les systèmes de la succession pourrait ressembler ils émuler classique OO. À mon avis, mon cadre n'est pas, mais alors il n'est pas encore terminée.

1voto

Ryan Points 387

Probablement JSLint (Crockford est un partisan de l'héritage prototypique, mais je n'ai pas passé au peigne fin). Il semble également plus fonctionnel qu'Object Oriented, mais je m'attends à ce que ce soit généralement le cas avec du code qui embrasse vraiment l'héritage prototypique.

1voto

Pacerier Points 15960

Dans mon cadre, tout est un objet ou une "interface".

Les Interfaces à la définition de fonctions (méthodes / property_gets / property_sets) que les objets peuvent avoir.

Vous créez une interface comme ceci: var some_interface = GetInterface(constructor, interface_setup, parent_interface..)

Vous pouvez spécifier n'importe quel nombre de parent_interfaces. Donc, si interface_A hérite à la fois interface_B et interface_C, vous pouvez créer interface_A en tant que telle: GetInterface(constructor, interface_setup, interface_B, interface_C);

Si interface_A hérite interface_X et interface_X hérite interface_Y, alors interface_A aurez toutes les fonctions à la fois interface_X et interface_Y a.

Les Interfaces qui ne nécessitent pas un constructeur de quitter l'argument du constructeur comme null. Le interface_setup est une fonction qui ressemble à ceci:

function(proto){
}

L'objet que le proto argument points à a 4 méthodes: SetM, ShadowM, SetP, et ShadowP.

Vous utilisez ces 4 méthodes pour l'installation de votre interface.

Ce cadre permet aussi paresseux instanciation d'interfaces. (en d'autres termes, le code de programme d'installation ne sera jamais réellement être exécuté jusqu'à ce qu'il est en fait d'abord nécessaire).

Les limites de ce cadre nécessite au moins en charge pour Object.keys, Object.getOwnPropertyDescriptor et Object.defineProperty. (En d'autres termes, il travaille dans les dernières versions de FireFox, IE, Chrome, Safari mais pas de l'Opéra)

TestPage2.html:

<!doctype html>
<script src="js.js"></script>
<script>
var human = GetInterface(function(){
},function(proto){
    //alert("trace: initing human");
    proto.$SetM("Sleep",function(){
        alert(this.Name+" is sleeping");
    });
    proto.$SetP("Name",function(){
        return this._name;
    },function(value){
        this._name=value;
    });
});

var female = GetInterface(function(){
},function(proto){
    //alert("trace: initing female");
    proto.$SetM("Dance",function(){
        alert(this.Name+" is dancing");
    });
},human);

var male = GetInterface(function(){
},function(proto){
    //alert("trace: initing male");
    proto.$SetM("Fight",function(){
        alert(this.Name+" is fighting");
    });
    proto.$ShadowP("Name",function(parent_get){
        return "Mr. "+parent_get();
    },function(parent_set,value){
        parent_set(value);
    });
},human);

var child = GetInterface(function(){
},function(proto){
    //alert("trace: initing child");
    proto.$SetM("Play",function(){
        alert(this.Name+" is playing");
    });
},human);

var adult = GetInterface(function(){
},function(proto){
    //alert("trace: initing adult");
    proto.$SetM("Work",function(){
        alert(this.Name+" is working");
    });
},human);

var mammal = GetInterface(function(){
},function(proto){
    //alert("trace: initing mammal");
    proto.$SetM("DoMammalStuff",function(){
        alert("doing mammal stuff");
    });
});


var john=new male();
john.Name="john";
john.Sleep();

var mary=new female();
mary.$IsA(child);
mary.$IsA(mammal);
mary.$Setup(function(proto){
    proto.$ShadowP("Name",function(parent_get){
        return "Miss "+parent_get.call(this);
    },function(parent_set,value){
        parent_set.call(this,value);
    });
});
mary.Name="mary";
mary.Play();
</script>

TestPage.html:

 <!doctype html>
<script src="js.js"></script>
<script>
var human_interface = GetInterface(function(){
},function(proto){
    alert("trace: initing human");
    proto.$SetM("Sleep",function(){
        alert(this.Name+" is sleeping");
    });
    proto.$SetP("Name",function(){
        return this._name;
    },function(value){
        this._name=value;
    });
});

var female_interface = GetInterface(function(){
},function(proto){
    alert("trace: initing female");
    proto.$SetM("Dance",function(){
        alert(this.Name+" is dancing");
    });
},human_interface);

var male_interface = GetInterface(function(){
},function(proto){
    alert("trace: initing male");
    proto.$SetM("Fight",function(){
        alert(this.Name+" is fighting");
    });
},human_interface);

var child_interface = GetInterface(function(){
},function(proto){
    alert("trace: initing child");
    proto.$SetM("Play",function(){
        alert(this.Name+" is playing");
    });
},human_interface);

var adult_interface = GetInterface(function(){
},function(proto){
    alert("trace: initing adult");
    proto.$SetM("Work",function(){
        alert(this.Name+" is working");
    });
},human_interface);

var mammal_interface = GetInterface(function(){
},function(proto){
    alert("trace: initing mammal");
    proto.$SetM("DoMammalStuff",function(){
        alert("doing mammal stuff");
    });
});

var john={};
john.$IsA(adult_interface);
//the above 2 lines are equal to simply doing:
//var john=new adult_interface();
//you can think of it as a shortcut
john.$IsA(mammal_interface);
john.DoMammalStuff();
john.Name="john";
john.Sleep();

var mary=new female_interface();
mary.$IsA(child_interface);
mary.$IsA(mammal_interface);
mary.DoMammalStuff();
mary.Name="mary";
mary.Play();
mary.Dance();
</script>

Js.js:

"use strict";
var GetInterface;
(function(){
    //================================================================================//
    //(constructor:Function, setup:Function?, parent_interfaces:Function..):Function
    GetInterface = function (constructor, setup) {
        var parent_classes = GetParray(arguments, 2);
        var output = function () {
            output.$Init();
            for (var x = parent_classes.length - 1; x >= 0; --x) {
                parent_classes[x](this);
            }
            if(constructor===null){
                constructor.apply(this, arguments);
            }
        };
        output.$Init = Mize(function () {
            var output_proto = output.prototype;
            parent_classes.forEach(function (parent_class) {
                parent_class.$Init();
                Infect(output_proto, parent_class.prototype);
            });
            init_proto(output_proto,setup);
            if(setup!==undefined){
                setup(output_proto);
            }
        });
        return output;
    };
    var init_proto=function(proto){
        $defineProperty(proto, "$SetM", { value: set_m, writable: true, configurable: true });
        $defineProperty(proto, "$ShadowM", { value: shadow_m, writable: true, configurable: true });
        $defineProperty(proto, "$SetP", { value: set_p, writable: true, configurable: true });
        $defineProperty(proto, "$ShadowP", { value: shadow_p, writable: true, configurable: true });
    };
    var set_m = function (method_name, method) {
        this[method_name] = method;
    };
    var set_p = function (property_name, getter, setter) {
        $defineProperty(this, property_name, { get: getter, set: setter, enumerable: true, configurable: true });
    };
    var shadow_m = function (method_name, supplied_method) {
        var old_method = this[method_name];
        this[method_name] = function () {
            var args = GetParray(arguments);
            args.unshift(old_method.bind(this));
            supplied_method.apply(this, args);
        };
    };
    var shadow_p = function (property_name, getter, setter) {
        var old_descriptor = $getOwnPropertyDescriptor(this, property_name);
        var old_get = old_descriptor.get;
        var old_set = old_descriptor.set;
        $defineProperty(this, property_name, { get: function () {
            return getter.call(this, old_get.bind(this));
        }, set: function (value) {
            setter.call(this, old_set.bind(this), value);
        }, enumerable: true, configurable: true
        });
    };
    var $slice=Array.prototype.slice;
    var $defineProperty=Object.defineProperty;
    var $getOwnPropertyDescriptor=Object.getOwnPropertyDescriptor;
    if($defineProperty===undefined){
        throw "Object.defineProperty, Object.getOwnPropertyDescriptor, Object.keys are required";
    }
    //================================================================================//
    //(victim:Object, disease:Object):void
    var Infect=function (victim, disease, excludes) {
        var keys=Object.keys(disease);
        if(excludes!==undefined){
            excludes.forEach(function(exclude){
                ForEach(keys,function(key,x){
                    if(key===exclude){
                        keys.splice(x,1);
                        return false;
                    }
                });
            });
        }
        keys.forEach(function(key){
            $defineProperty(victim, key, $getOwnPropertyDescriptor(disease, key));
        });
    };
    //================================================================================//
    //(args:Object # arguments object #, start_index:int?):Array
    var GetParray = function (args, start_index) {
        if (start_index === undefined) {
            start_index = 0;
        }
        return $slice.call(args, start_index);
    };
    //================================================================================//
    //(array:Array, f:Function(item:Object|null, index:pint):boolean?):Object
    var ForEach=function(array,f){
        for (var x = 0, xx = array.length, last_index=xx-1; x < xx; ++x) {
            var result = f(array[x], x, last_index);
            if (result !== undefined) {
                return result;
            }
        }
    };
    //================================================================================//
    //provides memoization.
    //(f:Function, arity_fixed:boolean?true):Function
    //caching is done according to the inputs. the results of calling function(undefined) and function() are cached differently.
    //if arity is fixed, optimizations can be done
    var Mize=function(f, arity_fixed) {
        if (arity_fixed === undefined) {
            arity_fixed = true;
        }
        var used; //for 0 arg
        var result; //for 0 arg
        var results; //for >0 args
        var used_params; //for 1 arg
        var used_param_sets; //for >1 args
        var f_length = f.length;
        var use_generic = !arity_fixed || f_length > 3;
        if (use_generic) { //if `f_length` <= 3, it will be optimized (i.e. not using generic function)
            results = [];
            used_param_sets = [];
            return function () {
                var params = GetParray(arguments);
                var result_found = false;
                var result = ForEach(used_param_sets,function (used_param_set, x) {
                    if (used_param_set.length === params.length) {
                        var params_match = true;
                        ForEach(params,function (param, y) {
                            if (used_param_set[y] !== param) {
                                params_match = false;
                                return false;
                            }
                        });
                        if (params_match) {
                            result_found = true;
                            return results[x];
                        }
                    }
                });
                if (!result_found) {
                    used_param_sets.push(params);
                    result = f.apply(null, params);
                    results.push(result);
                }
                return result;
            };
        }
        if (f_length === 0) {
            used = false;
            return function () {
                if (!used) {
                    result = f();
                    used = true;
                }
                return result;
            };
        }
        if (f_length === 1) {
            used_params = [];
        } else {
            used_param_sets = [];
        }
        results = [];
        switch (f_length) {
            case 1:
                return function (arg) {
                    var result_found = false;
                    var result = ForEach(used_params,function (used_param, x) {
                        if (arg === used_param) {
                            result_found = true;
                            return results[x];
                        }
                    });
                    if (!result_found) {
                        used_params.push(arg);
                        result = f(arg);
                        results.push(result);
                    }
                    return result;
                };
                break;
            case 2:
                return function (arg1, arg2) {
                    var result_found = false;
                    var result = ForEach(used_param_sets,function (used_param_set, x) {
                        if (arg1 === used_param_set[0] && arg2 === used_param_set[1]) {
                            result_found = true;
                            return results[x];
                        }
                    });
                    if (!result_found) {
                        used_param_sets.push([arg1, arg2]);
                        result = f(arg1, arg2);
                        results.push(result);
                    }
                    return result;
                };
                break;
            case 3:
                return function (arg1, arg2, arg3) {
                    var result_found = false;
                    var result = ForEach(used_param_sets,function (used_param_set, x) {
                        if (arg1 === used_param_set[0] && arg2 === used_param_set[1] && arg3 === used_param_set[2]) {
                            result_found = true;
                            return results[x];
                        }
                    });
                    if (!result_found) {
                        used_param_sets.push([arg1, arg2, arg3]);
                        result = f(arg1, arg2, arg3);
                        results.push(result);
                    }
                    return result;
                };
                break;
            default:
                throw "Invalid `f_length`: " + f_length;
        }
    };
    //================================================================================//
    Object.prototype.$Setup=function(setup){
        setup(Object.getPrototypeOf(this));
    };
    //================================================================================//
    Object.prototype.$IsA=function(_interface){
        var excludes=GetParray(arguments,1);
        if(this.$SetM===undefined){
            this.$SetM=set_m;
            this.$SetP=set_p;
            this.$ShadowM=shadow_m;
            this.$ShadowP=shadow_p;
        }
        _interface.$Init();
        /*var this_proto={};
        init_proto(this_proto);
        Infect(this_proto,Object.getPrototypeOf(this));
        this.__proto__=this_proto;*/
        Infect(Object.getPrototypeOf(this),_interface.prototype,excludes);
    };
    //================================================================================//
})();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X