2010-03-14 26 views
12

Içinde Douglas Crockford's JavaScript: The Good Parts o işlevsel devralma kullanmanızı öneririz. İşte bir örnek:Prototipler ile işlevsel işlevsel devralma

var mammal = function(spec, my) { 
    var that = {}; 
    my = my || {}; 

    // Protected 
    my.clearThroat = function() { 
     return "Ahem"; 
    }; 

    that.getName = function() { 
     return spec.name; 
    }; 

    that.says = function() { 
     return my.clearThroat() + ' ' + spec.saying || ''; 
    }; 

    return that; 
}; 

var cat = function(spec, my) { 
    var that = {}; 
    my = my || {}; 

    spec.saying = spec.saying || 'meow'; 
    that = mammal(spec, my); 

    that.purr = function() { 
     return my.clearThroat() + " purr"; 
    }; 

    that.getName = function() { 
     return that.says() + ' ' + spec.name + ' ' + that.says(); 
    }; 

    return that; 
}; 

var kitty = cat({name: "Fluffy"}); 

Bununla sahip ana konu her zaman mammal veya cat JavaScript yorumlayıcı içinde yeniden derlemek tüm fonksiyonlar zorunda olan bir yapmalarıdır. Yani, kodları örnekler arasında paylaşamazsınız.

Sorum şu: Bu kodu nasıl daha verimli hale getirebilirim? Örneğin, binlerce cat nesnesi yapıyor olsaydım, prototype nesnesinden yararlanmak için bu modeli değiştirmenin en iyi yolu nedir?

+0

"* İçinde yeniden derlemek tüm fonksiyonlar zorundadır. Yani, kodları örnekler arasında paylaşamazsınız * "- Hayır. Kod paylaşıldı, yalnızca farklı kapsam değerlerine sahip farklı işlev nesneleri oluşturulmalı. O kadar büyük bir yük değil. – Bergi

cevap

8

Pekala, mammal veya cat öğelerini yapmayı planlıyorsanız, bunu böyle yapamazsınız. Bunun yerine eski moda yolu (prototip) ve mülkiyete göre miras al. Hala yapıcıları Yukarıdaki ancak bunun yerine that ait olması ve my sen örtülü this ve temel sınıfını temsil eden bazı değişken (bu örnekte, this.mammal) kullanan yolla da.

cat.prototype.purr = function() { return this.mammal.clearThroat() + "purr"; } 

ben taban erişimi için my daha başka bir isim kullanmak ve cat oluşturucu içinde this depolar istiyorum. Bu örnekte ben mammal kullanılan ancak küresel mammal nesneye statik erişmesini istiyorsanız bu iyi olmayabilir. Başka bir seçenek base değişkenini adlandırmaktır.

+0

" 'this.mammal' aracılığıyla' my' nesne sonra birisi sadece 'cat.mammal.clearThroat (yapabileceği gibi), bunun gizliliğini kaybetmek' ve korumalı erişim yöntemini olması nedeniyledir. Yani gerçek – cdmckay

+2

. Eğer gizlilik o zaman bunu, örneğin, korunan yöntemlerinizi altçizgilerle önceleyen bir kurala göre yapmanız gerekir.Bu çok gevşek gözüküyorsa, bu "işlevsel miras" modelinin bile bir kongre olduğunu düşünün. Javascript "düzgün bir ucube" dili değildir. çok daha fazla "kaba ve hazır" Yaptığınız gibi, çılgın bir şekilde çalışmanız, performansta bir cezaya neden olur. Ancak, fazla ceza almayın - on binlerce nesne kreasyonunu önemli ölçüde etkiler. Genel koşu hızınız, e xample. – Plynx

+2

“birisi sadece cat.mammal.clearThroat()' ı yapabilir ve korunan yönteme erişebilir ”- her sorunun kodla çözülmesi gerekmez. En büyük saygıyla, Windows yazmıyorsunuz. Birisi kodunuzu kötüye kullanırsa, bu harikadır, çünkü birisinin gerçekten kodunuzu kullanması anlamına gelir. Kuramsal sorunlara karşı korumak yerine, kullanışlı ve doğru kullanımı kolay kod yapmaya odaklanmak çok daha iyidir. –

0

Gizlilik istiyorsanız ve size olabilir veya-olmayabilir bu yaklaşımı gibi protyping beğenmezseniz:

(not .: o jQuery.extend kullandığı)

var namespace = namespace || {}; 

// virtual base class 
namespace.base = function (sub, undefined) { 

    var base = { instance: this }; 

    base.hierarchy = []; 

    base.fn = { 

     // check to see if base is of a certain class (must be delegated) 
     is: function (constr) { 

      return (this.hierarchy[this.hierarchy.length - 1] === constr); 
     }, 

     // check to see if base extends a certain class (must be delegated) 
     inherits: function (constr) { 

      for (var i = 0; i < this.hierarchy.length; i++) { 

       if (this.hierarchy[i] == constr) return true; 
      } 
      return false; 
     }, 

     // extend a base (must be delegated) 
     extend: function (sub) { 

      this.hierarchy.push(sub.instance.constructor); 

      return $.extend(true, this, sub); 
     }, 

     // delegate a function to a certain context 
     delegate: function (context, fn) { 

      return function() { return fn.apply(context, arguments); } 
     }, 

     // delegate a collection of functions to a certain context 
     delegates: function (context, obj) { 

      var delegates = {}; 

      for (var fn in obj) { 

       delegates[fn] = base.fn.delegate(context, obj[fn]); 
      } 

      return delegates; 
     } 
    }; 

    base.public = { 
     is: base.fn.is, 
     inherits: base.fn.inherits 
    }; 

    // extend a sub-base 
    base.extend = base.fn.delegate(base, base.fn.extend); 

    return base.extend(sub); 
}; 

namespace.MyClass = function (params) { 

    var base = { instance: this }; 

    base.vars = { 
     myVar: "sometext" 
    } 

    base.fn = { 
     init: function() { 

      base.vars.myVar = params.myVar; 
     }, 

     alertMyVar: function() { 

      alert(base.vars.myVar); 
     } 

    }; 

    base.public = { 
     alertMyVar: base.fn.alertMyVar 
    }; 

    base = namespace.base(base); 

    base.fn.init(); 

    return base.fn.delegates(base,base.public); 
}; 

newMyClass = new namespace.MyClass({myVar: 'some text to alert'}); 
newMyClass.alertMyVar(); 

tek dezavantajı olduğunu, çünkü gizlilik kapsamının sadece sanal sınıfları genişletebilir ve sisteme girilemez sınıfları genişletebilirsiniz.

burada ı/unbind/yangın özel olaylar bağlamak için, namespace.base uzatmak nasıl bir örnektir.

// virtual base class for controls 
namespace.controls.base = function (sub) { 

    var base = { instance: this }; 

    base.keys = { 
     unknown: 0, 
     backspace: 8, 
     tab: 9, 
     enter: 13, 
     esc: 27, 
     arrowUp: 38, 
     arrowDown: 40, 
     f5: 116 
    } 

    base.fn = { 

     // bind/unbind custom events. (has to be called via delegate) 
     listeners: { 

      // bind custom event 
      bind: function (type, fn) { 

       if (fn != undefined) { 

        if (this.listeners[type] == undefined) { 
         throw (this.type + ': event type \'' + type + '\' is not supported'); 
        } 

        this.listeners[type].push(fn); 
       } 

       return this; 
      }, 

      // unbind custom event 
      unbind: function (type) { 

       if (this.listeners[type] == undefined) { 
        throw (this.type + ': event type \'' + type + '\' is not supported'); 
       } 

       this.listeners[type] = []; 

       return this; 
      }, 

      // fire a custom event 
      fire: function (type, e) { 

       if (this.listeners[type] == undefined) { 
        throw (this.type + ': event type \'' + type + '\' does not exist'); 
       } 

       for (var i = 0; i < this.listeners[type].length; i++) { 

        this.listeners[type][i](e); 
       } 

       if(e != undefined) e.stopPropagation(); 
      } 
     } 
    }; 

    base.public = { 
     bind: base.fn.listeners.bind, 
     unbind: base.fn.listeners.unbind 
    }; 

    base = new namespace.base(base); 

    base.fire = base.fn.delegate(base, base.fn.listeners.fire); 

    return base.extend(sub); 
}; 
1

beni prototype kullanır asla Klasik Kalıtım tanıştırayım. Bu kötü bir kodlama egzersizdir ama size her zaman prototypal miras kıyasla reel Klasik Inheritance öğretecek:

:

function Person(name, age){ 
    this.name = name; 
    this.age = age; 
    this.sayHello = function(){return "Hello! this is " + this.name;} 
} 

ondan devralan başka cunstructor olun:

bir custructor yapın

function Student(name, age, grade){ 
    Person.apply(this, [name, age]); 
    this.grade = grade 
} 

Çok basit! name ve age argümanlarla Student aramalar (geçerlidir) kendi üzerine Person kendisi tarafından grade argümanlar ilgilenir.

Şimdi Student bir örneğini yapalım.pete nesne Out

var pete = new Student('Pete', 7, 1); 

şimdi name, age, grade ve sayHello özelliklerini içerecektir. Bu özelliklerin tümü 'un ürününe sahiptir. Prototip yoluyla Person'a bağlanmazlar. Bu hale Person değiştirirseniz:

function Person(name, age){ 
    this.name = name; 
    this.age = age; 
    this.sayHello = function(){ 
    return "Hello! this is " + this.name + ". I am " this.age + " years old"; 
    } 
} 

pete olacak hiçbir recieve güncellemesi. pete.sayHello'u ararsak, ti ​​Hello! this is pete'u döndürür. Yeni güncellemeyi almayacak. Uygun kullanım Javascript prototip tabanlı miras için

0

kullanabilirdin fastClasshttps://github.com/dotnetwise/Javascript-FastClass

Sen basit inheritWith lezzet:

var Mammal = function (spec) { 
    this.spec = spec; 
}.define({ 
    clearThroat: function() { return "Ahem" }, 
    getName: function() { 
     return this.spec.name; 
    }, 
    says: function() { 
     return this.clearThroat() + ' ' + spec.saying || ''; 
    } 
}); 

var Cat = Mammal.inheritWith(function (base, baseCtor) { 
    return { 
     constructor: function(spec) { 
      spec = spec || {}; 
      baseCtor.call(this, spec); 
     }, 
     purr: function() { 
      return this.clearThroat() + " purr"; 
     }, 
     getName: function() { 
      return this.says() + ' ' + this.spec.name + this.says(); 
     } 
    } 
}); 

var kitty = new Cat({ name: "Fluffy" }); 
kitty.purr(); // Ahem purr 
kitty.getName(); // Ahem Fluffy Ahem 

Ve performansı hakkında çok endişe varsa o zaman fastClass lezzet:

var Mammal = function (spec) { 
    this.spec = spec; 
}.define({ 
    clearThroat: function() { return "Ahem" }, 
    getName: function() { 
     return this.spec.name; 
    }, 
    says: function() { 
     return this.clearThroat() + ' ' + spec.saying || ''; 
    } 
}); 

var Cat = Mammal.fastClass(function (base, baseCtor) { 
    return function() { 
     this.constructor = function(spec) { 
      spec = spec || {}; 
      baseCtor.call(this, spec); 
     }; 
     this.purr = function() { 
      return this.clearThroat() + " purr"; 
     }, 
     this.getName = function() { 
      return this.says() + ' ' + this.spec.name + this.says(); 
     } 
    } 
}); 

var kitty = new Cat({ name: "Fluffy" }); 
kitty.purr(); // Ahem purr 
kitty.getName(); // Ahem Fluffy Ahem 

Btw, ilk kodunuz ama anlamıyla buna saygı duydum.

fastClass yarar:

Function.prototype.fastClass = function (creator) { 
    var baseClass = this, ctor = (creator || function() { this.constructor = function() { baseClass.apply(this, arguments); } })(this.prototype, this) 

    var derrivedProrotype = new ctor(); 

    if (!derrivedProrotype.hasOwnProperty("constructor")) 
     derrivedProrotype.constructor = function() { baseClass.apply(this, arguments); } 

    derrivedProrotype.constructor.prototype = derrivedProrotype; 
    return derrivedProrotype.constructor; 
}; 

inheritWith yarar:

Function.prototype.inheritWith = function (creator, makeConstructorNotEnumerable) { 
    var baseCtor = this; 
    var creatorResult = creator.call(this, this.prototype, this) || {}; 
    var Derrived = creatorResult.constructor || 
    function defaultCtor() { 
     baseCtor.apply(this, arguments); 
    }; 
    var derrivedPrototype; 
    function __() { }; 
    __.prototype = this.prototype; 
    Derrived.prototype = derrivedPrototype = new __; 

    for (var p in creatorResult) 
     derrivedPrototype[p] = creatorResult[p]; 

    if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead 
     Object.defineProperty(derrivedPrototype, 'constructor', { 
      enumerable: false, 
      value: Derrived 
     }); 

    return Derrived; 
}; 

define yarar:

Function.prototype.define = function (prototype) { 
    var extendeePrototype = this.prototype; 
    if (prototype) 
     for (var p in prototype) 
      extendeePrototype[p] = prototype[p]; 
    return this; 
} 

[* Yasal Uyarı Ben açık kaynak paketi ve isimlerin yazarıyım yöntemlerin kendileri yeniden adlandırılabilir future` *]