2012-08-02 10 views
9

Esta es una pregunta para el gurú de JavaScript. Estoy tratando de trabajar con el modelo de prototipo de JavaScript más elegante. Aquí está mi código de utilidad (que proporciona la cadena real de prototipos y el trabajo correcto con el operador instanceof):¿Cómo hago JavaScript Prototype Herencia (cadena de prototipos)

function Class(conf) { 
    var init = conf.init || function() {}; 
    delete conf.init; 

    var parent = conf.parent || function() {}; 
    delete conf.parent; 

    var F = function() {}; 
    F.prototype = parent.prototype; 
    var f = new F(); 
    for (var fn in conf) f[fn] = conf[fn]; 
    init.prototype = f; 

    return init; 
}; 

Se me permite hacer este tipo de thigns:

var Class_1 = new Class({ 
    init: function (msg) { // constructor 
    this.msg = msg; 
    }, 

    method_1: function() { 
    alert(this.msg + ' in Class_1::method_1'); 
    }, 

    method_2: function() { 
    alert(this.msg + ' in Class_1::method_2'); 
    } 
}); 

var Class_2 = new Class({ 
    parent: Class_1, 

    init: function (msg) { // constructor 
    this.msg = msg; 
    }, 

    // method_1 will be taken from Class_1 

    method_2: function() { // this method will overwrite the original one 
    alert(this.msg + ' in Class_2::method_2'); 
    }, 

    method_3: function() { // just new method 
    alert(this.msg + ' in Class_2::method_3'); 
    } 
}); 

var c1 = new Class_1('msg'); 
c1.method_1(); // msg in Class_1::method_1 
c1.method_2(); // msg in Class_1::method_2 

var c2 = new Class_2('msg'); 
c2.method_1(); // msg in Class_1::method_1 
c2.method_2(); // msg in Class_2::method_2 
c2.method_3(); // msg in Class_2::method_3 

alert('c1 < Class_1 - ' + (c1 instanceof Class_1 ? 'true' : 'false')); // true 
alert('c1 < Class_2 - ' + (c1 instanceof Class_2 ? 'true' : 'false')); // false 

alert('c2 < Class_1 - ' + (c2 instanceof Class_1 ? 'true' : 'false')); // true 
alert('c2 < Class_2 - ' + (c2 instanceof Class_2 ? 'true' : 'false')); // true 

Mi pregunta es: ¿hay forma más simple de hacer esto?

+0

http://codereview.stackexchange.com/ –

+1

Hay un muy buen ejemplo de herencia de clases por [John Resig] (http://ejohn.org/blog/simple-javascript-inheritance/). Proporciona súper y otras golosinas. – elclanrs

+0

Esto puede ser de interés: http://ejohn.org/apps/learn/ –

Respuesta

0

Después de algunas investigaciones he concluido no hay forma más sencilla de hacer esto.

0

Sí, hay una forma mejor de hacerlo.

var call = Function.prototype.call; 

var classes = createStorage(), 
    namespaces = createStorage(), 
    instances = createStorage(createStorage); 


function createStorage(creator){ 
    var storage = new WeakMap; 
    creator = typeof creator === 'function' ? creator : Object.create.bind(null, null, {}); 
    return function store(o, v){ 
    if (v) { 
     storage.set(o, v); 
    } else { 
     v = storage.get(o); 
     if (!v) { 
     storage.set(o, v = creator(o)); 
     } 
    } 
    return v; 
    }; 
} 

function Type(){ 
    var self = function(){} 
    self.__proto__ = Type.prototype; 
    return self; 
} 

Type.prototype = Object.create(Function, { 
    constructor: { value: Type, 
       writable: true, 
       configurable: true }, 
    subclass: { value: function subclass(scope){ return new Class(this, scope) }, 
       configurable: true, 
       writable: true } 
}); 

function Class(Super, scope){ 
    if (!scope) { 
    scope = Super; 
    Super = new Type; 
    } 

    if (typeof Super !== 'function') { 
    throw new TypeError('Superconstructor must be a function'); 
    } else if (typeof scope !== 'function') { 
    throw new TypeError('A scope function was not provided'); 
    } 

    this.super = Super; 
    this.scope = scope; 

    return this.instantiate(); 
} 

Class.unwrap = function unwrap(Ctor){ 
    return classes(Ctor); 
}; 

Class.prototype.instantiate = function instantiate(){ 
    function super_(){ 
    var name = super_.caller === Ctor ? 'constructor' : super_.caller.name; 
    var method = Super.prototype[name]; 

    if (typeof method !== 'function') { 
     throw new Error('Attempted to call non-existent supermethod'); 
    } 

    return call.apply(method, arguments); 
    } 

    var Super = this.super, 
     namespace = namespaces(Super), 
     private = instances(namespace) 

    var Ctor = this.scope.call(namespace, private, super_); 
    Ctor.__proto__ = Super; 
    Ctor.prototype.__proto__ = Super.prototype; 
    namespaces(Ctor, namespace); 
    classes(Ctor, this); 
    return Ctor; 
} 

ejemplo de uso:

var Primary = new Class(function(_, super_){ 
    var namespace = this; 
    namespace.instances = 0; 

    function Primary(name, secret){ 
    this.name = name; 
    _(this).secret = secret; 
    namespace.instances++; 
    } 

    Primary.prototype.logSecret = function logSecret(label){ 
    label = label || 'secret'; 
    console.log(label + ': ' + _(this).secret); 
    } 

    return Primary; 
}); 


var Derived = Primary.subclass(function(_, super_){ 

    function Derived(name, secret, size){ 
    super_(this, name, secret); 
    this.size = size; 
    } 

    Derived.prototype.logSecret = function logSecret(){ 
    super_(this, 'derived secret'); 
    } 

    Derived.prototype.exposeSecret = function exposeSecret(){ 
    return _(this).secret; 
    } 

    return Derived; 
}); 

var Bob = new Derived('Bob', 'is dumb', 20); 
Bob.logSecret(); 
console.log(Bob); 
console.log(Bob.exposeSecret()); 
+0

super propiedades de trabajo y privadas –

Cuestiones relacionadas