2012-08-02 41 views
9

這是JavaScript的古茹問題。我正在嘗試使用更加優雅的JavaScript原型模型。這裏是我的工具代碼(它提供原型的真正的連鎖,並與instanceof運算符正確的工作):我該如何做JavaScript原型繼承(原型鏈)

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; 
}; 

它讓我做這樣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 

我的問題是:是否有更簡單的方法來做到這一點?

+0

http://codereview.stackexchange.com/ – 2012-08-02 04:23:16

+1

[John Resig](http://ejohn.org/blog/simple-javascript-inheritance/)有一個非常好的類繼承例子。它提供超級和其他好吃的東西。 – elclanrs 2012-08-02 04:26:11

+0

這可能是有趣的:http://ejohn.org/apps/learn/ – 2012-08-02 04:58:19

回答

0

一些研究,我已經得出結論有沒有更簡單的方法來做到這一點後。

0

是的,還有更好的方法來做到這一點。

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; 
} 

用法示例:

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

工作超級和私人物業 – 2012-08-02 16:11:19