كيف يعمل JavaScript.prototype؟




prototype-oriented (14)

أنا لست كذلك في لغات البرمجة الديناميكية ، ولكني قمت بكتابة نصيبي العادل من شفرة جافا سكريبت. لم أضع رأسي حول هذا البرنامج القائم على النموذج الأولي ، هل يعرف أحد كيف يعمل هذا؟

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

أتذكر الكثير من النقاشات التي أجريتها مع الناس منذ فترة (أنا لست متأكدة تمامًا مما أفعله) لكن كما أفهم ، لا يوجد مفهوم للفصل. إنه مجرد كائن ، وحالات هذه الكائنات هي نسخة من النسخة الأصلية ، أليس كذلك؟

ولكن ما هو الغرض بالضبط من هذه الخاصية .prototype في JavaScript؟ كيف ترتبط بتحويل الأشياء؟

تصحيح

لقد ساعدت هذه slides كثيرًا في فهم هذا الموضوع.


what is the exact purpose of this ".prototype" property?

The interface to standard classes become extensible. For example, you are using the Array class and you also need to add a custom serializer for all your array objects. Would you spend time coding up a subclass, or use composition or ... The prototype property solves this by letting the users control the exact set of members/methods available to a class.

Think of prototypes as an extra vtable-pointer. When some members are missing from the original class, the prototype is looked up at runtime.


بعد قراءة هذا الموضوع ، أشعر بالارتباك مع سلسلة نماذج جافا سكريبت ، ثم وجدت هذه المخططات

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance

إنه مخطط واضح لإظهار توارث JavaScript بواسطة سلسلة النموذج الأولي

و

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

هذا واحد يحتوي على مثال مع رمز والعديد من الرسوم البيانية لطيفة.

سلسلة النموذج الأولي في نهاية المطاف يعود إلى Object.prototype.

يمكن تمديد سلسلة النموذج من الناحية الفنية طالما تريد ، في كل مرة عن طريق تعيين النموذج الأولي للفئة الفرعية يساوي كائن من الفئة الأصل.

نأمل أنه من المفيد أيضًا أن تفهم سلسلة نموذج جافا سكريبت JavaScript.


كل كائن له خاصية داخلية ، [[Prototype]] ، تربطه بكائن آخر:

object [[Prototype]] -> anotherObject

في javascript التقليدي ، الكائن المرتبط هو الخاصية prototype للدالة:

object [[Prototype]] -> aFunction.prototype

بعض البيئات تعرض [[Prototype]] كـ __proto__ :

anObject.__proto__ === anotherObject

يمكنك إنشاء رابط [[النموذج الأولي]] عند إنشاء كائن.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

إذن هذه العبارات متكافئة:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

لا يظهر بيان new هدف الارتباط ( Object.prototype ) نفسه ؛ بدلاً من ذلك الهدف من قِبل المُنشئ ( Object ).

تذكر:

  • كل كائن له رابط ، [[Prototype]] ، يتعرض أحيانًا لـ __proto__ .
  • كل وظيفة لها خاصية prototype .
  • ترتبط الكائنات التي تم إنشاؤها باستخدام new بخاصية prototype .
  • إذا لم يتم استخدام دالة أبدًا كمنشئ ، فسيتم استخدام خاصية prototype الخاصة بها.
  • إذا لم تكن بحاجة إلى مُنشئ ، فاستخدم Object.create بدلاً من new .

لا تملك جافاسكريبت ميراثًا بالمعنى المعتاد ، ولكنها تحتوي على سلسلة النموذج الأولي.

سلسلة النموذج الأولي

إذا تعذر العثور على عضو في كائن في الكائن يبحث عنه في سلسلة النموذج الأولي. تتكون السلسلة من كائنات أخرى. يمكن الوصول إلى النموذج الأولي لمثيل معين باستخدام متغير __proto__ . كل كائن له واحد ، حيث لا يوجد فرق بين الطبقات وحالات في جافا سكريبت.

ميزة إضافة دالة / متغير إلى النموذج الأولي هو أنه يجب أن يكون في الذاكرة مرة واحدة فقط ، وليس لكل مثيل.

كما أنه مفيد للوراثة ، لأن سلسلة النموذج الأولي يمكن أن تتكون من العديد من الأشياء الأخرى.


يحتوي كل كائن JavaScript على خاصية داخلية تسمى [[Prototype]] . إذا بحثت عن خاصية عبر obj.propName أو obj['propName'] ولم يكن للكائن خاصية مثل - والتي يمكن التحقق منها عبر obj.hasOwnProperty('propName') - يبحث وقت التشغيل عن الخاصية في الكائن المشار إليها بواسطة [[Prototype]] بدلاً من ذلك. إذا لم يكن جسم النموذج الأولي يحتوي على مثل هذه الخاصية ، يتم فحص النموذج الأولي الخاص به بدوره ، وبالتالي السير في سلسلة النموذج الأولي للكائن الأصلي حتى يتم العثور على تطابق أو يتم الوصول إلى نهايته.

تسمح بعض عمليات تنفيذ جافا سكريبت بالوصول المباشر إلى الخاصية [[Prototype]] ، على سبيل المثال عن طريق خاصية غير قياسية باسم __proto__ . بشكل عام ، من الممكن فقط تعيين النموذج الأولي للكائن أثناء إنشاء الكائن: إذا قمت بإنشاء كائن new Func() عبر new Func() ، فسيتم تعيين الخاصية [[Prototype]] للكائن إلى الكائن المشار إليه بواسطة Func.prototype .

وهذا يسمح بمحاكاة الطبقات في JavaScript ، على الرغم من أن نظام الوراثة JavaScript - كما رأينا - نموذجي وليس قائمًا على الفصل:

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


يسمح لك prototype بصنع دروس. إذا كنت لا تستخدم prototype فإنه يصبح ثابت.

هنا مثال قصير.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

في الحالة المذكورة أعلاه ، لديك اختبار استدعاء funcation ثابت. هذه الوظيفة يمكن الوصول إليها فقط من خلال obj.test حيث يمكنك تخيل أن يكون obj صنف.

حيث كما في الكود أدناه

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

أصبح obj فئة يمكن الآن إنشاء مثيل. يمكن أن توجد حالات متعددة من obj وأنهم جميعا لديهم وظيفة test .

ما ورد أعلاه هو فهمي. إنني أصنعه من ويكي المجتمع ، لذلك يمكن للناس تصحيح لي إذا كنت مخطئا.


ألعب دورًا كمدرسًا لجافا سكريبت وكان مفهوم النموذج الأولي موضوعًا مثيرًا للجدل يغطيه عندما أقوم بالتدريس. استغرق الأمر بعض الوقت للتوصل إلى طريقة جيدة لتوضيح المفهوم ، والآن في هذا النص سأحاول شرح كيفية عمل جافا سكريبت.

هذا نموذج كائن بسيط للغاية يعتمد على النموذج والذي يعتبر عينة خلال التفسير ، بدون تعليق حتى الآن:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

هناك بعض النقاط الهامة التي يتعين علينا النظر فيها قبل المرور بمفهوم النموذج الأولي.

1- كيف تعمل وظائف جافا سكريبت بالفعل:

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

لنفترض أننا نريد إنشاء نموذج كائن Person . ولكن في هذه الخطوة ، سأحاول أن أفعل الشيء نفسه دون استخدام prototype والكلمة الرئيسية new .

إذن ، في هذه functions ، تكون objects ، this الكلمة ، كل ما لدينا.

سيكون السؤال الأول كيف يمكن أن تكون this الكلمة الرئيسية مفيدة دون استخدام كلمة رئيسية new .

إذاً للإجابة على ذلك لنفترض أن لدينا كائنًا فارغًا ، ووظفين مثل:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

والآن بدون استخدام كلمة رئيسية new كيف يمكننا استخدام هذه الوظائف. لذلك ، لدى جافا سكريبت 3 طرق مختلفة للقيام بذلك:

ا. الطريقة الأولى هي فقط استدعاء الوظيفة كوظيفة عادية:

Person("George");
getName();//would print the "George" in the console

في هذه الحالة ، قد يكون هذا هو كائن السياق الحالي ، والذي عادة ما يكون كائن window في المستعرض أو GLOBAL في Node.js هذا يعني أنه سيكون لدينا ، window.name في المتصفح أو GLOBAL.name في Node.js ، مع "جورج" كقيمة.

ب. يمكننا إرفاقها بجسم ، كخصائصه

- أسهل طريقة لإجراء ذلك هي تعديل كائن person الفارغ ، مثل:

person.Person = Person;
person.getName = getName;

بهذه الطريقة يمكننا الاتصال بهم مثل:

person.Person("George");
person.getName();// -->"George"

والآن يبدو جسم person :

Object {Person: function, getName: function, name: "George"}

- الطريقة الأخرى لإرفاق خاصية إلى كائن هي استخدام prototype لهذا الكائن الذي يمكن العثور عليه في أي كائن جافا سكريبت باسم __proto__ ، وقد حاولت شرح ذلك قليلاً على جزء التلخيص. حتى نتمكن من الحصول على نتيجة مماثلة عن طريق القيام بما يلي:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

ولكن بهذه الطريقة ما نقوم به بالفعل هو تعديل Object.prototype ، لأنه عندما نقوم بإنشاء كائن JavaScript باستخدام القيم الحرفية ( { ... } ) ، يتم Object.prototype بناءً على Object.prototype ، مما يعني أنه يتم إرفاقه Object.prototype حديثًا object as a named named __proto__ ، لذلك إذا قمنا بتغييره ، كما فعلنا في مقتطف الشفرة السابق ، ستتغير جميع كائنات جافا سكريبت ، وليست ممارسة جيدة. إذن ما يمكن أن يكون الممارسة الأفضل الآن:

person.__proto__ = {
    Person: Person,
    getName: getName
};

والآن أشياء أخرى في سلام ، ولكن لا يبدو أنها ممارسة جيدة. لذلك لا يزال لدينا حل آخر ، ولكن لاستخدام هذا الحل ، يجب أن نعود إلى هذا الرمز من الكود حيث تم إنشاء كائن person ( var person = {}; ) ثم تغييره مثل:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

ما يفعله هو إنشاء Object JavaScript جديد وإرفاق propertiesObject __proto__ . لذلك تأكد من أنك تستطيع القيام بما يلي:

console.log(person.__proto__===propertiesObject); //true

لكن النقطة الصعبة هنا هي أن لديك إمكانية الوصول إلى جميع الخصائص المحددة في __proto__ على المستوى الأول لكائن person (اقرأ جزء التلخيص لمزيد من التفاصيل).

كما ترون باستخدام أي من هذين الطريقين this شأنه أن يشير بالضبط إلى كائن person .

ج. جافا سكريبت لديه طريقة أخرى لتوفير وظيفة مع this ، والذي يستخدم call أو apply لاستدعاء الدالة.

تستدعي الدالة apply () دالة مع هذه القيمة والحجج المقدمة كصفيف (أو كائن شبيه بالمصفوفة).

و

استدعاء الأسلوب call () دالة مع هذه القيمة والحجج المقدمة على حدة.

بهذه الطريقة التي هي المفضلة لدي ، يمكننا بسهولة الاتصال بمهامنا مثل:

Person.call(person, "George");

أو

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

هذه الطرق الثلاثة هي الخطوات الأولية الهامة لمعرفة وظيفة .prototype.

2- كيف تعمل الكلمة الرئيسية new ؟

هذه هي الخطوة الثانية لفهم وظيفة .prototype هذا ما أستخدمه لمحاكاة العملية:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

في هذا الجزء ، سأحاول اتخاذ جميع الخطوات التي تتخذها جافا سكريبت ، دون استخدام الكلمة الرئيسية prototype new ، عند استخدام كلمة رئيسية new . لذلك عندما نقوم بعمل new Person("George") ، تعمل وظيفة Person كمنشئ ، هذه هي وظيفة JavaScript ، واحدة تلو الأخرى:

ا. أولا وقبل كل شيء يجعل كائن فارغ ، في الأساس تجزئة فارغة مثل:

var newObject = {};

ب. الخطوة التالية التي تأخذها جافا سكريبت هي إرفاق جميع الكائنات النموذجية بالكائن الذي تم إنشاؤه حديثًا

لدينا my_person_prototype هنا مشابهًا لكائن النموذج الأولي.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

وليست الطريقة التي تربط بها جافا سكريبت في الواقع الخصائص المحددة في النموذج الأولي. ترتبط الطريقة الفعلية بمفهوم سلسلة النموذج الأولي.

ا. & ب. بدلاً من هاتين الخطوتين ، يمكنك الحصول على النتيجة نفسها بالضبط من خلال:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

الآن يمكننا استدعاء وظيفة getName في my_person_prototype بنا:

newObject.getName();

ج. ثم يعطي هذا الكائن إلى المنشئ ،

يمكننا القيام بذلك مع عينة لدينا مثل:

Person.call(newObject, "George");

أو

Person.apply(newObject, ["George"]);

ثم يمكن منشئ القيام بكل ما يريد ، لأن هذا داخل هذا المنشئ هو الكائن الذي تم إنشاؤه للتو.

الآن النتيجة النهائية قبل محاكاة الخطوات الأخرى: Object {name: "George"}

ملخص:

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

new FunctionName()

جافا سكريبت داخليًا يصنع كائنًا ، تجزئة فارغة ومن ثم يعطي ذلك الكائن إلى المُنشئ ، ثم يستطيع المُنشئ فعل كل ما يريد ، لأن هذا داخل هذا المُنشئ هو الكائن الذي تم إنشاؤه للتو ، ثم يعطيك ذلك الكائن بالطبع إذا لم تكن قد استخدمت بيان الإرجاع في وظيفتك أو إذا قمت return undefined; في نهاية جسمك الوظيفي.

لذلك عندما تذهب جافا سكريبت للبحث عن خاصية على كائن ، فإن أول شيء يفعله ، هو أنها تبحث عنه على هذا الكائن. ثم هناك خاصية سرية [[prototype]] والتي عادة ما __proto__ مثل __proto__ و تلك الخاصية هي ما تبدو عليه جافا سكريبت في التالي. وعندما تنظر من خلال __proto__ ، بقدر ما هي مرة أخرى كائن جافا سكريبت آخر ، فإن لها سمة __proto__ الخاصة بها ، فإنها ترتفع وتصل إلى أن تصل إلى النقطة التي يكون فيها __proto__ التالي فارغًا. النقطة هي الكائن الوحيد في JavaScript الذي تكون به السمة __proto__ خالية كائن Object.prototype :

console.log(Object.prototype.__proto__===null);//true

وهذه هي الطريقة التي تعمل بها الميراث في جافا سكريبت.

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


0) يمكن تسمية شيئين مختلفين "النموذج الأولي":

  • الخاصية النموذج الأولي ، كما هو الحال في obj.prototype

  • الخاصية النموذجية الداخلية ، يشار إليها باسم [[Prototype]] في ES5 .

    يمكن استرجاعها عبر ES5 Object.getPrototypeOf() .

    يجعل Firefox الوصول إليها من خلال خاصية __proto__ . يشير ES6 الآن إلى بعض المتطلبات الاختيارية لـ __proto__ .

1) توجد هذه المفاهيم للإجابة على السؤال:

عندما أفعل obj.property ، أين تبحث JS عن .property ؟

حدسي ، الميراث الكلاسيكي يجب أن يؤثر على البحث عن الممتلكات.

2)

  • يُستخدم __proto__ للنقطة . البحث عن الممتلكات كما هو الحال في obj.property .
  • لا يتم استخدام .prototype للبحث مباشرة ، بشكل غير مباشر فقط لأنه يحدد __proto__ عند إنشاء الكائن بـ new .

ترتيب البحث هو:

  • تمت إضافة خصائص obj مع obj.p = ... أو Object.defineProperty(obj, ...)
  • خصائص obj.__proto__
  • خصائص obj.__proto__.__proto__ ، وهكذا
  • إذا كانت بعض __proto__ null ، __proto__ بإرجاع undefined .

هذا هو ما يسمى سلسلة النموذج الأولي .

يمكنك تجنبها . البحث باستخدام obj.hasOwnProperty('key') و Object.getOwnPropertyNames(f)

3) هناك طريقتان رئيسيتان لتحديد obj.__proto__ :

  • new :

    var F = function() {}
    var f = new F()
    

    ثم حدد new :

    f.__proto__ === F.prototype
    

    هذا هو المكان الذي يستخدم فيه .prototype .

  • Object.create :

     f = Object.create(proto)
    

    موعات:

    f.__proto__ === proto
    

4) الكود:

var F = function() {}
var f = new F()

يناظر الرسم البياني التالي:

(Function)       (  F  )                                      (f)
 |  ^             | | ^                                        |
 |  |             | | |                                        |
 |  |             | | +-------------------------+              |
 |  |constructor  | |                           |              |
 |  |             | +--------------+            |              |
 |  |             |                |            |              |
 |  |             |                |            |              |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |
 |  |             |                |            |              |
 |  |             |                | +----------+              |
 |  |             |                | |                         |
 |  |             |                | | +-----------------------+
 |  |             |                | | |
 v  |             v                v | v
(Function.prototype)              (F.prototype)
 |                                 |
 |                                 |
 |[[Prototype]]                    |[[Prototype]]
 |                                 |
 |                                 |
 | +-------------------------------+
 | |
 v v
(Object.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

يعرض هذا الرسم البياني العديد من العقد الكائن المعرفة مسبقًا بلغة: null و Object و Object.prototype و Function و Function.prototype . لدينا خطوط 2 من التعليمات البرمجية فقط إنشاء F و F و F F.prototype .

5) .constructor يأتي عادة من F.prototype . F.prototype من خلال . ابحث عن:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

عندما نكتب f.constructor ، جافا سكريبت يفعل . البحث كما يلي:

  • f لم يكن لديك
  • f.__proto__ === F.prototype has .constructor === F ، لذا .constructor === F

والنتيجة f.constructor == F صحيحة بشكل حدسي ، حيث يتم استخدام F لبناء f ، على سبيل المثال ، تعيين الحقول ، يشبه إلى حد كبير في لغات OOP الكلاسيكية.

6) يمكن تحقيق بنية الوراثة الكلاسيكية عن طريق التلاعب في سلاسل النماذج الأولية.

يضيف ES6 class extends الكلمات الرئيسية ، التي هي ببساطة عبارة عن سكر للنوع الأول من التلاعب في النموذج الأولي.

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

مخطط مبسط بدون جميع الكائنات المحددة مسبقًا:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype

Consider the following keyValueStore object :

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

I can create a new instance of this object by doing this :

kvs = keyValueStore.create();

Each instance of this object would have the following public properties :

  • data
  • get
  • set
  • delete
  • getLength

Now, suppose we create 100 instances of this keyValueStore object. Even though get , set , delete , getLength will do the exact same thing for each of these 100 instances, every instance has its own copy of this function.

Now, imagine if you could have just a single get , set , delete and getLength copy, and each instance would reference that same function. This would be better for performance and require less memory.

That's where prototypes come in. A prototype is a "blueprint" of properties that is inherited but not copied by instances. So this means that it exists only once in memory for all instances of an object and is shared by all of those instances.

Now, consider the keyValueStore object again. I could rewrite it like this :

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

This does EXACTLY the same as the previous version of the keyValueStore object, except that all of its methods are now put in a prototype. What this means, is that all of the 100 instances now share these four methods instead of each having their own copy.


I always like analogies when it comes to understand this type of stuff. 'Prototypical inheritance' is pretty confusing in comparison to class bass inheritance in my opinion, even though prototypes are much simpler paradigm. In fact with prototypes, there really is no inheritance, so the name in and of itself misleading, it's more a type of 'delegation'.

Imagine this ....

You're in high-school, and you're in class and have a quiz that's due today, but you don't have a pen to fill out your answers. Doh!

You're sitting next to your friend Finnius, who might have a pen. You ask, and he looks around his desk unsuccessfully, but instead of saying "I don't have a pen", he's a nice friend he checks with his other friend Derp if he has a pen. Derp does indeed have a spare pen and passes it back to Finnius, who passes it over to you to complete your quiz. Derp has entrusted the pen to Finnius, who has delegated the pen to you for use.

What is important here is that Derp does not give the pen to you, as you don't have a direct relationship with him.

This, is a simplified example of how prototypes work, where a tree of data is searched for the thing you're looking for.


It may help to categorise prototype chains into two categories.

Consider the constructor:

 function Person() {}

The value of Object.getPrototypeOf(Person) is a function. In fact, it is Function.prototype . Since Person was created as a function, it shares the same prototype function object that all functions have. It is the same as Person.__proto__ , but that property should not be used. Anyway, with Object.getPrototypeOf(Person) you effectively walk up the ladder of what is called the prototype chain.

The chain in upward direction looks like this:

PersonFunction.prototypeObject.prototype (end point)

Important is that this prototype chain has little to do with the objects that Person can construct . Those constructed objects have their own prototype chain, and this chain can potentially have no close ancestor in common with the one mentioned above.

Take for example this object:

var p = new Person();

p has no direct prototype-chain relationship with Person . Their relationship is a different one. The object p has its own prototype chain. Using Object.getPrototypeOf , you'll find the chain is as follows:

pPerson.prototypeObject.prototype (end point)

There is no function object in this chain (although that could be).

So Person seems related to two kinds of chains, which live their own lives. To "jump" from one chain to the other, you use:

  1. .prototype : jump from the constructor's chain to the created-object's chain. This property is thus only defined for function objects (as new can only be used on functions).

  2. .constructor : jump from the created-object's chain to the constructor's chain.

Here is a visual presentation of the two prototype chains involved, represented as columns:

To summarise:

The prototype property gives no information of the subject's prototype chain, but of objects created by the subject.

It is no surprise that the name of the property prototype can lead to confusion. It would maybe have been clearer if this property had been named prototypeOfConstructedInstances or something along that line.

You can jump back and forth between the two prototype chains:

Person.prototype.constructor === Person

This symmetry can be broken by explicitly assigning a different object to the prototype property (more about that later).

Create one Function, Get Two Objects

Person.prototype is an object that was created at the same time the function Person was created. It has Person as constructor, even though that constructor did not actually execute yet. So two objects are created at the same time:

  1. The function Person itself
  2. The object that will act as prototype when the function is called as a constructor

Both are objects, but they have different roles: the function object constructs , while the other object represents the prototype of any object that function will construct. The prototype object will become the parent of the constructed object in its prototype chain.

Since a function is also an object, it also has its own parent in its own prototype chain, but recall that these two chains are about different things.

Here are some equalities that could help grasp the issue -- all of these print true :

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Adding levels to the prototype chain

Although a prototype object is created when you create a constructor function, you can ignore that object, and assign another object that should be used as prototype for any subsequent instances created by that constructor.

على سبيل المثال:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Now the prototype chain of t is one step longer than that of p :

tpPerson.prototypeObject.prototype (end point)

The other prototype chain is not longer: Thief and Person are siblings sharing the same parent in their prototype chain:

Person }
Thief } → Function.prototypeObject.prototype (end point)

The earlier presented graphic can then be extended to this (the original Thief.prototype is left out):

The blue lines represent prototype chains, the other coloured lines represent other relationships:

  • between an object and its constructor
  • between a constructor and the prototype object that will be used for constructing objects

Let me tell you my understanding of prototypes. I am not going to compare the inheritance here with other languages. I wish people would stop comparing languages, and just understand the language as itself. Understanding prototypes and prototypal inheritance is so simple, as I will show you below.

Prototype is like a model, based on which you create a product. The crucial point to understand is that when you create an object using another object as it's prototype, the link between the prototype and the product is ever-lasting. على سبيل المثال:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Every object contains an internal property called the [[prototype]], which can be accessed by the Object.getPrototypeOf() function. Object.create(model) creates a new object and sets it's [[prototype]] property to the object model . Hence when you do Object.getPrototypeOf(product) , you will get the object model .

Properties in the product are handled in the following way:

  • When a property is accessed to just read it's value, its looked up in the scope chain. The search for the variable starts from the product upwards to it's prototype. If such a variable is found in the search, the search is stopped right there, and the value is returned. If such a variable cannot be found in the scope chain, undefined is returned.
  • When a property is written(altered), then the property is always written on the product object. If the product does not have such a property already, it is implicitly created and written.

Such a linking of objects using the prototype property is called prototypal inheritance. There, it is so simple, agree?


There's two distinct but related entities here that need explaining:

  • The .prototype property of functions.
  • The [[Prototype]] [1] property of all objects [2] .

These are two different things.

The [[Prototype]] property:

This is a property that exists on all [2] objects.

What's stored here is another object, which, as an object itself, has a [[Prototype]] of its own that points to another object. That other object has a [[Prototype]] of its own. This story continues until you reach the prototypical object that provides methods that are accessible on all objects (like .toString ).

The [[Prototype]] property is part of what forms the [[Prototype]] chain. This chain of [[Prototype]] objects is what is examined when, for example, [[Get]] or [[Set]] operations are performed on an object:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

The .prototype property:

This is a property that is only found on functions. Using a very simple function:

function Bar(){};

The .prototype property holds an object that will be assigned to b.[[Prototype]] when you do var b = new Bar . You can easily examine this:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

One of the most important .prototype s is that Object.prototype . This prototype holds the prototypical object that all [[Prototype]] chains contain. On it, all the available methods for new objects are defined:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Now, since .prototype is an object, it has a [[Prototype]] property. When you don't make any assignments to Function.prototype , the .prototype 's [[Prototype]] points to the prototypical object ( Object.prototype ). This is automatically performed anytime you create a new function.

This way, any time you do new Bar; the prototype chain is set up for you, you get everything defined on Bar.prototype and everything defined on Object.prototype :

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

When you do make assignments to Function.prototype all you are doing is extending the prototype chain to include another object. It's like an insertion in a singly linked list.

This basically alters the [[Prototype]] chain allowing properties that are defined on the object assigned to Function.prototype to be seen by any object created by the function.

[1: لن يخلط ذلك أحداً ؛ إتاحتها عبر و __proto__الممتلكات في العديد من التطبيقات.
[2]: كل ما عدا null.


When a constructor creates an object, that object implicitly references the constructor's “prototype” property for the purpose of resolving property references. The constructor's “prototype” property can be referenced by the program expression constructor.prototype, and properties added to an object's prototype are shared, through inheritance, by all objects sharing the prototype.





prototype-oriented