درک prototype از زبان موزیلا

چند وقتی هست که ترجیح دادم مقاله های مناسب و کاربردی در حوزه کاری خودم رو ترجمه کنم و بنویسم .

این کار باعث میشه همه با هم بیشتر یاد بگیریم .

در مورد شی گرایی داخل جاوا اسکریپت صحبت زیاد شد . اما از جایی که Brendan پایه گذار این زبان است ، دیدم کی بهتر از موزیلا میتونه این مشکل رو برای همیشه حل کنه؟


Brendan Eich is an American technologist and creator of the JavaScript programming language. He co-founded the Mozilla project, the Mozilla Foundation and the Mozilla Corporation, and served as the Mozilla Corporation's chief technical officer and briefly its chief executive officer wikipedia

اشیا داخل جاوا اسکریپت یک کیف پر و داینامیک از پروپرتی ها هستن . یعنی اینکه هر شی داخل جاوا اسکریپت پر از پروپرتی هست که این پروپرتی ها میتونن بین اشیا دیگه انتقال داده بشن .

این اشیا ، به یک شی به اسم prototype لینک میشن .

وقتی دنبال یک پروپرتی باشیم داخل یک شی ، این پرورتی فقط داخل شی جستجو نمیشه ، بلکه داخل prototype اون شی و حتی prototype اون prototype و... جستجو میشه .

جاوا اسکریپت برای کسایی که با زبان های c-style کار کردن کمی گیج کننده میشه . چون که شی گرایی که داخل مثلا جاوا هست رو نمیتونه پیدا کنه و عادتی که به نوشتن با سبک شی گرایی داره ، باعث میشه کمی گیچ بشه . البته داخل ES2015 کلمه کلیدی class آورده شد که به گفته موزیلا ، اصطلاح syntactical sugar رو داره و به زبان خودمون ، دکوریه و رفتارهای یک کلاس رو نداره .

یک مثال میخوام بزنم و درک بهتری پیدا کنیم . این مثال رو میتونین خیلی راحت پیاده سازی کنین و تست کنین .

// Let's create an object o from function f with its own properties a and b:
let f = function () {
   this.a = 1;
   this.b = 2;
   }
let o = new f(); // {a: 1, b: 2}
// add properties in f function's prototype
f.prototype.b = 3;
f.prototype.c = 4;

// do not set the prototype f.prototype = {b:3,c:4}; this will break the prototype chain
// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype.
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// {a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null

console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.

console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited. 
// This is called Property Shadowing
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and there is no 'd' property by default, check
its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.

مثالی که زده شد مستقیما از موزیلا بود بدون هیچ تغییر .

توضیحات کامل داخل کد بالا قرار داره و نیاز به توضیح نیست ، اما چیزی که جالب و مهمه ، نحوه append کردن داخل prototype .

حالا یک مثال میزنیم که ببینیم چجور میشه داخل prototype ها یک متود داشته باشیم .

نکته قابل ذکر اینه که شما میتونین هرچقدر تابع که مایلید ، از خارج از prototype وارد کنید و عملیات منطقی رو به راحتی روی اونا انجام بدید .

وقتی که یک تابع ارث بری بشه ، کلمه کلیدی `this` اشاره به شی که از اون ارث برده شده میکنه
var o = {
a: 2, 
m: function() {
 return this.a + 1;
   }
};

console.log(o.m()); // 3
// When calling o.m in this case, 'this' refers to o
var p = Object.create(o);
// p is an object that inherits from o
p.a = 4; // creates a property 'a' on p
console.log(p.m()); // 5
// when p.m is called, 'this' refers to p.
// So when p inherits the function m of o, 
// 'this.a' means p.a, the property 'a' of p

کمی عمیقتر به موضوع نگاه کنیم

میخوایم ببینیم که وقتی ما یک متود داریم ، رفتار prototype به چه شکلی هست و وقتی یک مقدار اضافه میشه چه اتفاقاتی میفته؟باید جالب باشه .

function doSomething(){}
console.log( doSomething.prototype );
// It does not matter how you declare the function, a
//  function in JavaScript will always have a default
//  prototype property.
var doSomething = function(){};
console.log( doSomething.prototype );

خوب اگر کد بالا رو داخل مرورگر نگاه کنیم ، میبینیم که prototype رو به ما به شکل کامل میده

{
    constructor: ƒ doSomething(),
    __proto__: {
    constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

خروجی بالا به ما یک سری اطلاعات در مورد این تابع میده .

حالا بیایم یک مقدار به شی doSomething اضافه کنیم :

function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );

حالا باید foo با مقدار bar به شی اضافه شده باشه . ببینیم :

{
    foo: "bar",
    constructor: ƒ doSomething(),
    __proto__: {
    constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

دقیقا چیزی که توقع داشتیم .

حالا میتونیم تمام این شی رو داخل شی دیگه ببریم و در قالب یک شی دیگه کار کنیم (چه شئ تو شئی شد) :

function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );

نتیجه رو ببینیم :

{
    prop: "some value",
    __proto__: {
    foo: "bar",
        constructor: ƒ doSomething(),
        __proto__: {
        constructor: ƒ Object(),
            hasOwnProperty: ƒ hasOwnProperty(),
            isPrototypeOf: ƒ isPrototypeOf(),
            propertyIsEnumerable: ƒ propertyIsEnumerable(),
            toLocaleString: ƒ toLocaleString(),
            toString: ƒ toString(),
            valueOf: ƒ valueOf()
        }
    }
}

خوب از این به بعد چون کمی میتونه خسته کننده بشه ، میزارم به عهده هر کس که میخواد بیشتر بخونه .

برای من خیلی به درد بخور بود . گاهی اوقات باید برگشت از اول خوند . این از اول خوندنا خیلی میتونه باعث قویتر شدن بشه . https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain