في جافاسكريبت، يمكن أن تحتوي الدوال على خصائص. جميع الدوال لديها خاصية خاصة تسمى prototype
.
function doSomething() {}
console.log(doSomething.prototype);
// الدوال في جافاسكريبت دائمًا تحتوي على خاصية prototype افتراضية
// الاستثناء: دوال الأسهم لا تحتوي على خاصية prototype افتراضية
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);
doSomething()
لديها خاصية prototype
افتراضية. بعد تشغيل الكود أعلاه، ستعرض وحدة التحكم كائنًا مشابهًا لما يلي:
{ constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
ملاحظة: تستخدم وحدة تحكم Chrome [[Prototype]]
للإشارة إلى النموذج الأولي للكائن، وفقًا لمصطلحات المواصفات؛ يستخدم Firefox .prototype
. للتوحيد، سنستخدم [[Prototype]]
.
يمكننا إضافة خصائص إلى النموذج الأولي لـ doSomething()
:
function doSomething() {}
doSomething.prototype.foo = "bar";
console.log(doSomething.prototype);
النتيجة:
{ foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
استخدم عامل التشغيل new
لإنشاء نسخة من doSomething()
بناءً على هذا النموذج الأولي. استدعاء الدالة باستخدام عامل التشغيل new
سيعيد كائنًا هو نسخة من الدالة. يمكن بعد ذلك إضافة خصائص إلى هذا الكائن.
function doSomething() {}
doSomething.prototype.foo = "bar"; // إضافة خاصية إلى النموذج الأولي
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // إضافة خاصية إلى الكائن
console.log(doSomeInstancing);
النتيجة:
{ prop: "some value", [[Prototype]]: { foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
[[Prototype]]
لـ doSomeInstancing
هو doSomething.prototype
. عند الوصول إلى خاصية doSomeInstancing
، سيُجري وقت التشغيل أولاً فحصًا لمعرفة ما إذا كان doSomeInstancing
يحتوي على تلك الخاصية أم لا.
إذا لم يكن كذلك، سيُجري وقت التشغيل بحثًا عن الخاصية في doSomeInstancing.[[Prototype]]
(أي doSomething.prototype
). إذا كان doSomeInstancing.[[Prototype]]
يحتوي على الخاصية التي يتم البحث عنها، فسيتم استخدام تلك الخاصية.
إذا لم يكن كذلك، فسيتم فحص doSomeInstancing.[[Prototype]].[[Prototype]]
. افتراضيًا، يكون [[Prototype]]
لخاصية prototype
لأي دالة هو Object.prototype
. لذلك، سيتم البحث في doSomeInstancing.[[Prototype]].[[Prototype]]
(أي doSomething.prototype.[[Prototype]]
أو Object.prototype
). تُسمى هذه العملية سلسلة النماذج الأولية (Prototype Chain).
إذا لم يتم العثور على الخاصية في doSomeInstancing.[[Prototype]].[[Prototype]]
، فسيتم البحث في doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
. مع ذلك، لا يوجد doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
، لأن Object.prototype.[[Prototype]]
هو null
. بعد البحث في سلسلة النماذج الأولية بأكملها، سيؤكد وقت التشغيل أن الخاصية غير موجودة وقيمة الخاصية هي undefined
.
function doSomething() {}
doSomething.prototype.foo = "bar";
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: ", doSomeInstancing.prop);
console.log("doSomeInstancing.foo: ", doSomeInstancing.foo);
console.log("doSomething.prop: ", doSomething.prop);
console.log("doSomething.foo: ", doSomething.foo);
console.log("doSomething.prototype.prop:", doSomething.prototype.prop);
console.log("doSomething.prototype.foo: ", doSomething.prototype.foo);
النتيجة:
doSomeInstancing.prop: some value
doSomeInstancing.foo: bar
doSomething.prop: undefined
doSomething.foo: undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo: bar