11484 sujets

JavaScript, DOM et API Web HTML5

Bonjour à tous,

Depuis quelques temps je me suis beaucoup intéressé à l'héritage par prototype en JS. Je m'en sors plutôt bien je trouve, mais aujourd'hui je suis confronté à un petit problème.

En effet, j'essaie de créer une classe "Collection" qui hérite de Array et qui permet de spécialisé celui-ci en spécifiant un type. Jusque là je n'ai aucun problème.
Celui-ci survient lorsque j'essaie en fin de compte de chaîner plusieurs fois la méthode "push" de ma collection. Celle-ci renvoie un objet undefined alors que je fais un "return this;" à la fin de celle-ci.

J'ai fait plusieurs console.log et tester un peu tous les états de "this" dans la méthode "push" et la valeur de celle-ci est toujours valide. Mais voilà, à la sortie de la fonction, je récupère toujours une valeur undefined.

Je vous remercie si vous pouviez m'éclairer un petit peu plus.
Je poste à la suite mon code ainsi qu'un JSFiddle. (le code est par ailleurs beaucoup simplifier par rapport à ma version de base qui contient d'autre éléments pour gérer des canvas).

Liens JSFiddle: http://jsfiddle.net/08azs78t/4/
/**
 * Kookie Namespace
 */
var kk, kookie;
;(function(window, document) {
    'use strict';
    
    // Definition of kk namespace
    kk = {};
    
    // Object Detection Enhancing
    (function() {
        var types = {}, toString = types.toString, hasOwn = types.hasOwnProperty;
        
        /**
         * Util namespace of kookie
         */
        kk.util = {};
        
        /**
         * Iterates over an array and executing the callback passed for each iteration
         * @param {Array} array The array iterated
         * @param {Function} fn The callback to execute for each iteration
         */
        kk.util.each = function(array, fn) {
            for (var i = 0, l = array.length; i < l; ++i) {
                fn(array[ i ], i, array);
            }
        };
        
        /**
         * Get the type of an object in a more accurate way than the basic "typeof" keyword
         * @param {mixed} mixed The variable
         * @return {string} The type of the mixed variable passed
         */
        kk.util.getType = function(mixed) {
            if (mixed === null)
                return mixed +'';
            return typeof mixed === 'object' || typeof mixed === 'function' ?
                types[toString.call(mixed)] || 'object' :
                typeof mixed;
        };
        
        // Feed types map/hash
        kk.util.each('Boolean Number String Function Array Date RegExp Object Error'.split(' '), function(name) {
            types['[object '+ name +']'] = name.toLowerCase();
        });
    })();
    
    // Object Inheritance
    (function() {
        var slice = Array.prototype.slice, inherit, copyProto, mixin;
        
        copyProto = function(klass, source, parent) {
            for (var property in source) {
                if (kk.util.getType(parent) !== 'null' &&
                    property in klass.prototype &&
                    typeof klass.prototype[ property ] === 'function' &&
                    typeof source[ property ] === 'function') {
                    
                    var __super__ = parent.prototype;
                    klass.prototype[ property ] = (function(property, __super__) {
                        return function() {
                            var tmpSuper = this.__super__;
                            this.__super__ = (function(){
                                return function() {
                                    var args = slice.call(arguments, 0);
                                    var methodName = args[0] in __super__ ? args.shift() : property;
                                    __super__[methodName].apply(this, args);
                                };
                            })();
                            source[ property ].apply(this, arguments);
                            
                            (tmpSuper === undefined) ? delete this.__super__ : this.__super__ = tmpSuper;
                        };
                    })(property, __super__);
                }
                else {
                    klass.prototype[ property ] = source[ property ];
                }
            }
        };
        
        mixin = function(properties) {
            for (var property in properties) {
                this.prototype[ property ] = properties[ property ];
            }
        };
        
        inherit = function() {
            var parent = null,
                abstract = false,
                properties = slice.call(arguments, 0);
            
            if (typeof properties[0] === 'function') {
                parent = properties.shift();
            }
            if (typeof properties[0] === 'boolean') {
                abstract = properties.shift();
            }
            
            // Create KKClass constructor
            var KKClass = (function(abstractClass) {
                return function KKClass() {
                    if (abstractClass === true) {
                        throw {
                            name:'Abstract Class Error',
                            message: 'Cannot instanciate an abstract class',
                            toString: function() {
                                return this.name +': '+ this.message;
                            }
                        };
                    }
                    else {
                        this.initialize.apply(this, arguments);
                    }
                };
            })(abstract);
            
            // KKClass ultra functionnalities (like mixing protos)
            KKClass.mixin = mixin;
            
            var KKPrototype = function(){};
            if (parent) {
                KKPrototype.prototype = parent.prototype;
            }
            KKClass.prototype = new KKPrototype();
            
            // Now, we're copying the new protot into the child class
            for (var i = 0, l = properties.length; i < l; ++i) {
                copyProto(KKClass, properties[ i ], parent);
            }
            
            // If no initialize method is present then create a default one
            if (typeof KKClass.prototype.initialize === 'undefined') {
                KKClass.prototype.initialize = function(){};
            }
            
            KKClass.prototype.constructor = KKClass;
            return KKClass;
        };
        
        kk.util.createClass = inherit;
    })();
    
    // Collection
    (function() {
        var Collection, push = Array.prototype.push;
        
        Collection = kk.util.createClass(Array, {
            initialize: function(type) {
                this.type = type;
            },
            push: function() {
                for (var i = 0, l = arguments.length; i < l; ++i) {
                    if (!(arguments[ i ] instanceof this.type) && arguments[ i ] !== new this.type(arguments[ i ]).valueOf()) {
                        throw {
                            name: 'Collection Type Error',
                            message: 'Could not insert a value of "'+ typeof arguments[ i ] +'" on collection',
                            toString: function() {
                                return this.name +': '+ this.message;
                            }
                        }
                    }
                    else {
                        push.call(this, arguments[ i ]);
                    }
                }
                return this;
            },
            add: function() {
                return this.push.apply(this, arguments);
            },
            getObjects: function(type) {
                if (typeof type === 'undefined' || type == this.type) {
                    return this;
                }
                
                // If the specified type is another type than the collection type
                // Then we create a new Collection from this type
                var collection = new this.constructor(type);
                Collection.prototype.push.apply(collection, this.filter(function(o) {
                    return (o instanceof type || o === new type(o).valueOf());
                }));
                return collection;
            },
            
            item: function(index) {
                return this.getObjects()[ index ];
            },
            isEmpty: function() {
                return this.getObjects().length === 0;
            },
            contains: function(object) {
                return this.getObjects().indexOf(object) > -1;
            }
        });
        
        kk.Collection = Collection;
    })();
})(window, window.document);

var collec = new kk.Collection(String);
collec.push('lol', 'lol2'); // Sans chainage, ca fonctionne
console.log(collec);
collec.push('lol', 'lol2').push('lol3'); // Avec, une erreur est déclanchée.
console.log(collec);

Modifié par ChibiKookie (02 Sep 2014 - 13:51)
Je reviens vers vous pour m'excuser.
Après avoir débugger à nouveau tout mon code, je me suis rendu compte que je ne renvoyait pas la valeur de retour lorsque je wrappais ma fonction lors de l'héritage.

JSFiddle: http://jsfiddle.net/08azs78t/5/

Et maintenant, ça fonctionne.