سؤال

لقد فعلت بعض الميراث في JS من أجل فهمها بشكل أفضل، ووجدت شيئا يربأني.

أعلم أنه عند استدعاء "وظيفة منشئ" مع الكلمة الرئيسية الجديدة، يمكنك الحصول على كائن جديد مع إشارة إلى النموذج الأولي للعمل الوظيفة.

أعلم أيضا أنه من أجل جعل الميراث النموذجي يجب أن تحل محل النموذج الأولي وظيفة المنشئ مع مثيل الكائن الذي تريد أن تكون "Superclass".

لذلك فعلت هذا المثال السخيف لمحاولة هذه المفاهيم:

function Animal(){}
function Dog(){}

Animal.prototype.run = function(){alert("running...")};

Dog.prototype = new Animal(); 
Dog.prototype.bark = function(){alert("arf!")};

var fido = new Dog();
fido.bark() //ok
fido.run() //ok

console.log(Dog.prototype) // its an 'Object' 
console.log(fido.prototype) // UNDEFINED
console.log(fido.constructor.prototype == Dog.prototype) //this is true

function KillerDog(){};
KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")}

fido.prototype = new KillerDog();

console.log(fido.prototype) // no longer UNDEFINED
fido.deathBite(); // but this doesn't work!

(تم ذلك في وحدة التحكم في Firebug)

1) لماذا إذا كانت جميع الكائنات الجديدة تحتوي على مرجع إلى النموذج الأولي وظيفة الخالق، فيدو.

2) هي سلسلة الميراث [OBJ] -> [منشئ] -> [النموذج الأولي] بدلا من [OBJ] -> [النموذج الأولي]؟

3) هو خاصية "النموذج الأولي" لكائننا (FIDO) فحص أي وقت مضى؟ إذا كان الأمر كذلك ... لماذا هو "Deathbite" غير محدد (في الجزء الأخير)؟

هل كانت مفيدة؟

المحلول

1) لماذا إذا كانت جميع الكائنات الجديدة تحتوي على مرجع إلى النموذج الأولي وظيفة الخالق، فيدو.

تعقد جميع الكائنات الجديدة إشارة إلى النموذج الأولي الذي كان موجودا على منشئه في وقت البناء. ومع ذلك اسم العقار المستخدم لتخزين هذا المرجع ليس كذلك prototype كما هو في المنشئ وظيفة نفسها. تسمح بعض تطبيقات JavaScript بالوصول إلى هذه الخاصية "المخفية" عبر بعض اسم العقار مثل __proto__ حيث البعض الآخر لا (على سبيل المثال ملفات Microsoft).

2) هي سلسلة الميراث [OBJ] -> [منشئ] -> [النموذج الأولي] بدلا من [OBJ] -> [النموذج الأولي]؟

رقم إلقاء نظرة على هذا: -

function Base() {}
Base.prototype.doThis = function() { alert("First"); }

function Base2() {}
Base2.prototype.doThis = function() { alert("Second"); }

function Derived() {}
Derived.prototype = new Base()

var x = new Derived()

Derived.prototype = new Base2()

x.doThis();

هذه التنبيهات "الأولى" ليست الثانية. إذا ذهبت سلسلة الميراث عبر المنشئ، فسنرى "ثانيا". عند إنشاء كائن، يتم تحويل المرجع الحالي الذي تم تعليقه في وظائف خاصية النموذج الأولي إلى المرجع المخفي للكائن إلى النموذج الأولي.

3) هو خاصية "النموذج الأولي" لكائننا (FIDO) فحص أي وقت مضى؟ إذا كان الأمر كذلك ... لماذا هو "Deathbite" غير محدد (في الجزء الأخير)؟

تعيين كائن (بخلاف وظيفة) خاصية تسمى prototype لا يوجد لديه معنى خاص، كما هو موضح سابقا كائن لا يحتفظ بالإشارة إلى النموذج الأولي عبر اسم العقار.

نصائح أخرى

لا يمكنك تغيير النموذج الأولي للكائن بمجرد إنشاء مثيل له new.

في المثال الخاص بك أعلاه، خطوط مثل

fido.prototype = new KillerDog();

ما عليك سوى إنشاء سمة جديدة تسمى prototype على الكائن fido, وتضع هذه السمة إلى جديد KillerDog هدف. لا يختلف عن

fido.foo = new KillerDog();

كما تقف الكود الخاص بك ...

// Doesn't work because objects can't be changed via their constructors
fido.deathBite();

// Does work, because objects can be changed dynamically, 
// and Javascript won't complain when you use prototype 
//as an object attribute name
fido.prototype.deathBite();

الخاص prototype ينطبق السلوك فقط على المنشور في جافا سكريبت، حيث يكون المنشئون functionS التي سيتم استدعاؤها مع new.

الإجابة بالأرقام لأسئلتك:

  1. لا يسمى خاصية نموذج الكائن prototype. وبعد الاستخدامات القياسية [[prototype]] لتعيينه. فايرفوكس يجعل هذه الخاصية الجمهور تحت اسم __proto__.
  2. سلسلة الميراث هي [obj][prototype object]. وبعد افتراضك الأصلي ([obj][constructor][prototype]) غير صحيح ويمكنك بسهولة دحضه عن طريق تعديل constructor و / أو constructor.prototype, ، والتحقق من طرق يمكن استدعاءها على [obj] - سوف تكتشف أن هذه التعديلات لا تغير أي شيء.
  3. prototype لم يتم التحقق من العقار على الكائنات ولم تستخدمها. يمكنك ضبطه على ما تريد. يستخدمه جافا سكريبت كائنات وظيفة فقط أثناء بناء الكائنات.

لإظهار رقم 3 هنا هو الرمز من دوجو:

dojo.delegate = dojo._delegate = (function(){
  // boodman/crockford delegation w/ cornford optimization
  function TMP(){}
  return function(obj, props){
    TMP.prototype = obj;
    var tmp = new TMP();
    if(props){
      dojo._mixin(tmp, props);
    }
    return tmp; // Object
  }
})();

كما ترون أنه يستفيد من حقيقة أن prototype يستخدم فقط في مكان واحد عن طريق إعادة استخدام نفس الوظيفة TMP لجميع الأشياء المفوضة مع النماذج النماطية المختلفة. حقيقة prototype يتم تعيينه مباشرة قبل استدعاء الوظيفة new, ، وسيتم تغييره بعد ذلك عدم التأثير على أي كائنات تم إنشاؤها.

يمكنك العثور على الكائن الذي تم إنشاؤه تسلسل في إجابتي العلاقة بين [[النموذج الأولي]] والنموذج الأولي في جافا سكريبت.

أعلم أنه تم الرد عليه بالفعل ولكن هناك طريقة أفضل للقيام بالميراث. استدعاء منشئ لغرض الميراث غير مرغوب فيه. واحدة من الآثار غير المرغوب فيها هي.

function Base() {this.a = "A"}
function Child() {this.b = "B"};

Child.prototype = new Base();

الآن أضفت خاصية "A" إلى النموذج الأولي للطفل الذي لم تنويه.

ها هي الطريقة الصحيحة (لم أقم بإنهاء هذا، ext-js وغيرها من Libs استخدم هذا)

// This is used to avoid calling a base class's constructor just to setup inheritance.
function SurrogateCtor() {}

/**
 * Sets a contructor to inherit from another constructor
 */
function extend(BaseCtor, DerivedCtor) {
  // Copy the prototype to the surrogate constructor
  SurrogateCtor.prototype = BaseCtor.prototype;
  // this sets up the inheritance chain
  DerivedCtor.prototype = new SurrogateCtor();
  // Fix the constructor property, otherwise it would point to the BaseCtor
  DerivedCtor.prototype.constructor = DerivedCtor;
  // Might as well add a property to the constructor to 
  // allow for simpler calling of base class's method
  DerivedCtor.superclass = BaseCtor;
}

function Base() {
  this.a = "A";
}

Base.prototype.getA = function() {return this.a}

function Derived() {
  Derived.superclass.call(this);  // No need to reference the base class by name
  this.b = "B";
}

extend(Base, Derived);
// Have to set methods on the prototype after the call to extend
// otherwise the prototype is overridden;
Derived.prototype.getB = function(){return this.b};
var obj = new Derived();

طريقة أسهل هي إضافة معلمة ثالثة إلى تمديد حيث تحدد طريقة الفئة المشتقة حتى لا تضطر إلى استدعاء تمديد ثم إضافة طرق إلى النموذج الأولي

extend(BaseCtor, DerivedCtor, {
  getB: function() {return this.b}
});

ثم هناك العديد من الأشياء الأخرى التي يمكنك القيام بها لسكر النحؤ.

مدون حول هذا الموضوع: http://js-bits.blogspot.com/0/javascript-inheritance-done-right.html.

تجدر الإشارة إلى أنه في Ecmascript 5 (أي أحدث إصدار من لغة JavaScript) يمكنك الوصول إلى الخاصية الداخلية [[Prototype]] الخاص بمثيل Object.getPrototypeOf:

Object.getPrototypeOf(fido) === Dog.prototype
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top