JavaScript .prototype ของ JavaScript ทำงานอย่างไร


2041

ฉันไม่ได้เป็นภาษาการเขียนโปรแกรมแบบไดนามิก แต่ฉันได้เขียนรหัส JavaScript ของฉันอย่างยุติธรรม ฉันไม่เคยเข้าใจการเขียนโปรแกรมต้นแบบนี้จริง ๆ ไม่มีใครรู้วิธีการทำงานนี้หรือไม่?

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

ฉันจำได้ว่ามีการพูดคุยกันหลายครั้งกับผู้คน (ฉันไม่แน่ใจว่าฉันกำลังทำอะไรอยู่) แต่เมื่อฉันเข้าใจแล้วไม่มีแนวคิดของชั้นเรียน มันเป็นแค่วัตถุและอินสแตนซ์ของวัตถุเหล่านั้นเป็นโคลนของดั้งเดิมใช่ไหม

แต่วัตถุประสงค์ที่แน่นอนของคุณสมบัติ ".prototype" ใน JavaScript คืออะไร มันเกี่ยวข้องกับการสร้างอินสแตนซ์วัตถุอย่างไร

อัปเดต: วิธีที่ถูกต้อง

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

อีกทั้งสไลด์เหล่านี้ช่วยได้มากจริงๆ


78
John Resig มีสไลด์ไม่กี่ตัวบนต้นแบบฟังก์ชั่นที่เป็นประโยชน์กับฉันเมื่อดูหัวข้อ (คุณสามารถทำการเปลี่ยนแปลงรหัสและดูว่าเกิดอะไรขึ้น ... ) http://ejohn.org/apps/learn/#64
John Foster

5
วัสดุอ้างอิงที่ดีสำหรับจุดประสงค์ในการเก็บคำถามนี้อาจเป็นข้อมูลที่แสดงความคิดเห็นบางส่วนจากเว็บไซต์ของ John ในคำตอบของคุณในกรณีที่ไซต์ของเขามีการเปลี่ยนแปลงในลักษณะที่ไม่มีลิงก์ของคุณอีกต่อไป +1 ด้วยวิธีใดก็ได้ช่วยฉันด้วย
Chris

95
+1 สำหรับลิงค์ของคุณจอห์นเรซิกของนินจา JavaScript เลื่อน # เริ่มจากที่นั่นมีประโยชน์จริงๆและฉันรู้สึกว่าฉันเข้าใจต้นแบบอย่างถูกต้อง
เงินที่จ่าย nerd

4
เราต้องการวัตถุที่ใช้งานได้จริงสำหรับการใช้ต้นแบบหรือไม่? ถ้าใช่กว่าทำไม
Anshul

6
สิ่งนี้อาจช่วยคุณได้: webdeveasy.com/javascript-prototype
Naor

คำตอบ:


1007

วัตถุทุก JavaScript มี "ช่อง" ภายในเรียกว่า[[Prototype]]มีค่าเป็นอย่างใดอย่างหนึ่งหรือnull objectคุณสามารถนึกถึงสล็อตเป็นคุณสมบัติบนวัตถุภายในของเครื่องมือ JavaScript ซ่อนจากรหัสที่คุณเขียน วงเล็บเหลี่ยมรอบ[[Prototype]]มีเจตนาและเป็นข้อกำหนดข้อกำหนด ECMAScript เพื่อแสดงช่องภายใน

ค่าที่ชี้ไปตาม [[Prototype]]วัตถุนั้นเรียกว่า "ต้นแบบของวัตถุนั้น"

หากคุณเข้าถึงพร็อพเพอร์ตี้ผ่านเครื่องหมายจุด ( obj.propName) หรือobj['propName']เครื่องหมายวงเล็บเหลี่ยม ( ) และวัตถุไม่ได้มีคุณสมบัติดังกล่าวโดยตรง (เช่นคุณสมบัติของตัวเองตรวจสอบผ่านobj.hasOwnProperty('propName')) รันไทม์จะค้นหาคุณสมบัติที่มีชื่อนั้นบนวัตถุที่อ้างอิง โดย[[Prototype]]แทน หาก[[Prototype]] ยังไม่มีคุณสมบัติดังกล่าว[[Prototype]]ก็จะถูกตรวจสอบในทางกลับกันและอื่น ๆ ด้วยวิธีนี้ห่วงโซ่ต้นแบบของวัตถุดั้งเดิมจะเดินจนกว่าจะพบการแข่งขันหรือถึงจุดสิ้นสุด ที่ด้านบนสุดของห่วงโซ่ต้นแบบคือnullคุณค่า

การใช้งาน JavaScript สมัยใหม่ช่วยให้สามารถอ่านและ / หรือเขียนการเข้าถึง[[Prototype]]ด้วยวิธีต่อไปนี้:

  1. newประกอบการ (กำหนดค่าห่วงโซ่ต้นแบบบนวัตถุเริ่มต้นกลับมาจากการทำงานของผู้สร้าง)
  2. extendsคำหลัก (กำหนดค่าห่วงโซ่ต้นแบบเมื่อใช้ไวยากรณ์ชั้นเรียน)
  3. Object.createจะตั้งค่าอาร์กิวเมนต์ที่ให้เป็น[[Prototype]]วัตถุของผลลัพธ์
  4. Object.getPrototypeOfและObject.setPrototypeOf(รับ / ตั้งค่าการสร้างวัตถุ[[Prototype]] หลัง ) และ
  5. คุณสมบัติ accessor มาตรฐาน (เช่น. getter / setter) ชื่อ__proto__(คล้ายกับ 4)

Object.getPrototypeOfและObject.setPrototypeOfเป็นที่ต้องการมากกว่า__proto__ส่วนหนึ่งเป็นเพราะการทำงานของo.__proto__ เป็นเรื่องปกติnullเมื่อวัตถุมีต้นแบบของ

วัตถุที่[[Prototype]]เป็นชุดแรกในระหว่างการสร้างวัตถุ

หากคุณสร้างวัตถุใหม่ผ่านnew Func()การ[[Prototype]]ตั้งค่าเริ่มต้นของวัตถุจะเป็นวัตถุที่อ้างอิงโดยFunc.prototypeประสงค์โดยค่าเริ่มต้นจะกำหนดให้วัตถุอ้างอิงโดย

โปรดทราบว่าดังนั้นคลาสทั้งหมดและฟังก์ชั่นทั้งหมดที่สามารถใช้กับnewโอเปอเรเตอร์จะมีคุณสมบัติที่มีชื่อ.prototypeนอกเหนือจาก[[Prototype]]สล็อตภายในของตัวเอง การใช้คำสองคำนี้ "ต้นกำเนิด" เป็นที่มาของความสับสนไม่รู้จบในหมู่ผู้มาใหม่กับภาษา

การใช้ newกับฟังก์ชั่นคอนสตรัคช่วยให้เราสามารถจำลองมรดกคลาสสิกใน JavaScript; ถึงแม้ว่าระบบสืบทอดของ JavaScript จะเป็น - อย่างที่เราเห็น - ต้นแบบและไม่ใช่อิงกับคลาส

ก่อนที่จะมีการแนะนำไวยากรณ์คลาสจาวาสคริปต์ฟังก์ชันคอนสตรัคเตอร์เป็นวิธีเดียวที่จะจำลองคลาส เราสามารถคิดถึงคุณสมบัติของวัตถุที่อ้างอิงโดยคุณสมบัติของตัวสร้างฟังก์ชันใน.prototypeฐานะสมาชิกที่ใช้ร่วมกัน กล่าวคือ สมาชิกที่เหมือนกันสำหรับแต่ละอินสแตนซ์ ในระบบที่อิงกับคลาสมีการใช้วิธีการเหมือนกันสำหรับแต่ละอินสแตนซ์ดังนั้นจึงมีการเพิ่มแนวคิดใน.prototypeคุณสมบัติ อย่างไรก็ตามฟิลด์ของวัตถุนั้นเป็นแบบเฉพาะและจะถูกเพิ่มเข้าไปในวัตถุนั้นในระหว่างการก่อสร้าง

หากไม่มีไวยากรณ์คลาสนักพัฒนาต้องกำหนดค่าเชนลูกโซ่ต้นแบบด้วยตนเองเพื่อให้ได้ฟังก์ชันการทำงานที่คล้ายคลึงกับการสืบทอดแบบคลาสสิก สิ่งนี้นำไปสู่ความเหนือกว่าของวิธีการต่าง ๆ เพื่อบรรลุเป้าหมายนี้

นี่คือวิธีหนึ่ง:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... และนี่คือวิธีอื่น:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

ไวยากรณ์คลาสที่นำมาใช้ใน ES2015 ทำให้สิ่งต่าง ๆ ง่ายขึ้นโดยการจัดเตรียม extendsเป็น "หนึ่งวิธีที่แท้จริง" เพื่อกำหนดค่าเชนลูกโซ่ต้นแบบเพื่อจำลองการสืบทอดคลาสสิกใน JavaScript

ดังนั้นคล้ายกับรหัสด้านบนหากคุณใช้ไวยากรณ์คลาสเพื่อสร้างวัตถุใหม่เช่น:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... ผลของวัตถุ[[Prototype]]จะถูกตั้งค่าตัวอย่างของParentซึ่งในที่สุดก็คือ[[Prototype]]Parent.prototype

สุดท้ายถ้าคุณสร้างวัตถุใหม่ผ่านObject.create(foo)ผลของวัตถุจะถูกตั้งค่า[[Prototype]]foo


1
ดังนั้นฉันทำอะไรผิดโดยการกำหนดคุณสมบัติใหม่ในคุณสมบัติต้นแบบในตัวอย่างสั้น ๆ ของฉัน
John Leidegren

3
ฉันคิดว่านี่คือสิ่งที่การมีฟังก์ชั่นวัตถุเป็นพลเมืองชั้นหนึ่ง
John Leidegren

8
ฉันเกลียดสิ่งที่ไม่เป็นมาตรฐานโดยเฉพาะอย่างยิ่งในภาษาการเขียนโปรแกรมทำไมถึงมีแม้แต่โปรโตเมื่อมันไม่ต้องการอย่างชัดเจน?
John Leidegren

1
@John __proto__ เป็นสิ่งจำเป็นสำหรับการใช้งานภายในโดยล่าม JS เท่านั้น แต่ละวัตถุต้องการรู้ว่าคุณสมบัติและวิธีการใดเป็นส่วนหนึ่งของต้นแบบและเป็นส่วนหนึ่งของวัตถุนั้น ล่าม JS จะต้องสามารถเรียกวิธีการต้นแบบบนวัตถุ
BMiner

17
โปรดทราบว่าการใช้[[Prototype]]นั้นมีเจตนา - ECMA-262 ล้อมรอบด้วยชื่อของคุณสมบัติภายในที่มีเครื่องหมายวงเล็บคู่
Christoph

1798

ในภาษาที่ใช้การสืบทอดแบบคลาสสิกเช่น Java, C # หรือ C ++ คุณเริ่มต้นด้วยการสร้างคลาส - พิมพ์เขียวสำหรับวัตถุของคุณ - จากนั้นคุณสามารถสร้างวัตถุใหม่จากคลาสนั้นหรือคุณสามารถขยายคลาสโดยกำหนดคลาสใหม่ที่ augments ชั้นเดิม

ใน JavaScript คุณสร้างวัตถุ (ไม่มีแนวคิดของคลาส) จากนั้นคุณสามารถเพิ่มวัตถุของคุณเองหรือสร้างวัตถุใหม่จากมัน มันไม่ใช่เรื่องยาก แต่เป็นสิ่งแปลกปลอมเล็ก ๆ น้อย ๆ และยากต่อการเผาผลาญสำหรับใครบางคนที่เคยใช้วิธีแบบดั้งเดิม

ตัวอย่าง:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

จนถึงตอนนี้ฉันได้ขยายวัตถุฐานตอนนี้ฉันสร้างวัตถุอื่นแล้วสืบทอดจากบุคคล

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

ในขณะที่กล่าวว่าฉันไม่สามารถเรียก setAmountDue (), getAmountDue () กับบุคคล

//The following statement generates an error.
john.setAmountDue(1000);

352
ฉันคิดว่าคำตอบเกี่ยวกับสแต็คโอเวอร์โฟลว์ไม่เพียง แต่น่าสนใจสำหรับโปสเตอร์ดั้งเดิมเท่านั้น และฉันเคยเป็นหนึ่งในพวกเขาและฉันได้รับประโยชน์จากโพสต์เก่า ฉันคิดว่าฉันสามารถมีส่วนร่วมกับคำตอบอื่น ๆ ที่เพิ่มตัวอย่างรหัสบางอย่าง เกี่ยวกับคำถามของคุณ: หากคุณไม่สนใจคำถามใหม่มันไม่ทำงาน เมื่อฉันเรียก myCustomer.sayMyName () จะส่งคืน "myCustomer.sayMyName ไม่ใช่ฟังก์ชัน" วิธีที่ง่ายที่สุดคือการทดสอบกับ firebug และดูว่าเกิดอะไรขึ้น
stivlo

7
เท่าที่ฉันเข้าใจ var Person = function (name) {... }; กำลังกำหนดฟังก์ชั่นการสร้างความสามารถในการสร้างวัตถุบุคคล ดังนั้นจึงยังไม่มีวัตถุเฉพาะฟังก์ชันตัวสร้างที่ไม่ระบุชื่อเท่านั้นที่ถูกกำหนดให้กับบุคคล นี่เป็นคำอธิบายที่ดีมาก: helephant.com/2008/08/how-javascript-objects-work
stivlo

17
คำเตือน: คำตอบนี้ไม่สนใจความจริงที่ว่าคอนสตรัคเตอร์ระดับผู้ปกครองไม่ได้ถูกเรียกใช้ในแต่ละครั้ง เหตุผลเดียวที่ใช้ได้ก็เพราะเขาทำสิ่งเดียวกัน (ตั้งชื่อ) ทั้งในตัวสร้างลูกและผู้ปกครอง สำหรับคำอธิบายเชิงลึกเพิ่มเติมเกี่ยวกับข้อผิดพลาดทั่วไปที่เกิดขึ้นเมื่อพยายามรับมรดกใน JavaScript (และวิธีแก้ไขปัญหาขั้นสุดท้าย) โปรดดู: โพสต์สแต็กล้นนี้
Aaren Cordova

3
ฉันสังเกตเห็นว่าคำตอบนี้ไม่ได้พูดถึงว่าโดยการใช้ "new Person ()" เป็นต้นแบบคุณกำลังตั้งค่าคุณสมบัติอินสแตนซ์ "ชื่อ" ของ "บุคคล" เป็นคุณสมบัติคงที่ของ "ลูกค้า" (เพื่อลูกค้าทั้งหมด อินสแตนซ์จะมีคุณสมบัติเหมือนกัน) ในขณะที่มันเป็นตัวอย่างพื้นฐานที่ดีอย่าทำอย่างนั้น :) สร้างฟังก์ชั่นใหม่ที่ไม่ระบุชื่อเพื่อทำหน้าที่เป็น "สะพาน" โดยการตั้งค่าต้นแบบเป็น "Person.prototype" จากนั้นสร้างอินสแตนซ์จากมันและตั้งค่า "Customer.prototype" เป็นอินสแตนซ์ที่ไม่ระบุตัวตนแทน
James Wilkins

10
เกี่ยวกับCustomer.prototype = new Person();สายการแสดง MDN ตัวอย่างการใช้Customer.prototype = Object.create(Person.prototype)และระบุว่า'มีข้อผิดพลาดที่พบบ่อยนี่คือการใช้ 'คนใหม่ ()'' แหล่งที่มา
Rafael Eyng

186

นี่เป็นโมเดลวัตถุต้นแบบที่ง่ายมากที่จะพิจารณาเป็นตัวอย่างระหว่างการอธิบายโดยยังไม่มีความคิดเห็น:

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

มีบางจุดที่สำคัญที่เราต้องพิจารณาก่อนที่จะผ่านแนวคิดต้นแบบ

1- วิธีการทำงานของ JavaScript ทำงานได้จริง:

ในขั้นตอนแรกเราต้องคิดออกว่าฟังก์ชั่น JavaScript ใช้งานได้จริงอย่างไรในฐานะคลาสเช่นฟังก์ชั่นที่ใช้thisคำหลักในนั้นหรือเพียงแค่ฟังก์ชั่นปกติที่มีข้อโต้แย้งสิ่งที่มันทำและสิ่งที่มันส่งกลับ

สมมติว่าเราต้องการสร้างPersonแบบจำลองวัตถุ แต่ในขั้นตอนนี้ผมจะพยายามที่จะทำในสิ่งที่แน่นอนเดียวกันโดยไม่ต้องใช้prototypeและnewคำหลัก

ดังนั้นในขั้นตอนนี้functions, objectsและthisคำหลักทั้งหมดที่เรามี

คำถามแรกที่จะเป็นวิธีการที่thisคำหลักจะเป็นประโยชน์โดยไม่ต้องใช้newคำหลัก

ดังนั้นเพื่อตอบว่าสมมุติว่าเรามีวัตถุว่างเปล่าและฟังก์ชันสองอย่างเช่น:

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

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

และตอนนี้โดยไม่ใช้newคำสำคัญว่าเราจะใช้ฟังก์ชันเหล่านี้ได้อย่างไร ดังนั้น JavaScript มี 3 วิธีที่แตกต่างกัน:

วิธีแรกคือเพียงเรียกใช้ฟังก์ชันเป็นฟังก์ชันปกติ:

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

ในกรณีนี้นี้จะเป็นวัตถุบริบทปัจจุบันซึ่งมักจะเป็นระดับโลก windowวัตถุที่อยู่ในเบราว์เซอร์หรือในGLOBAL Node.jsมันหมายความว่าเราจะมี window.name ในเบราว์เซอร์หรือ GLOBAL.name ใน Node.js โดยมี "George" เป็นค่า

ข เราสามารถแนบมันเข้ากับวัตถุเป็นคุณสมบัติ

- วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการดัดแปลงpersonวัตถุเปล่าเช่น:

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

วิธีนี้เราสามารถเรียกพวกเขาเช่น:

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

และตอนนี้personวัตถุก็เหมือน:

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

- วิธีอื่นในการแนบคุณสมบัติกับวัตถุคือการใช้prototypeวัตถุนั้นที่สามารถค้นหาได้ในวัตถุ JavaScript ใด ๆ ที่มีชื่อ__proto__และฉันพยายามอธิบายเล็กน้อยในส่วนสรุป ดังนั้นเราสามารถได้ผลลัพธ์ที่คล้ายกันโดยทำ:

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

แต่วิธีที่สิ่งที่เราทำจริง ๆ คือการแก้ไขObject.prototypeเพราะเมื่อใดก็ตามที่เราสร้างวัตถุ JavaScript โดยใช้ตัวอักษร ( { ... }) มันจะถูกสร้างขึ้นตามObject.prototypeซึ่งหมายความว่ามันจะได้รับการแนบกับวัตถุที่สร้างขึ้นใหม่เป็นแอตทริบิวต์ชื่อ__proto__ดังนั้นถ้าเราเปลี่ยน ตามที่เราได้ทำกับข้อมูลโค้ดก่อนหน้านี้วัตถุ JavaScript ทั้งหมดจะเปลี่ยนไปไม่ใช่วิธีปฏิบัติที่ดี ดังนั้นสิ่งที่อาจเป็นแนวปฏิบัติที่ดีกว่าในขณะนี้:

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

และตอนนี้วัตถุอื่น ๆ อยู่ในความสงบ แต่มันก็ดูเหมือนจะไม่เป็นวิธีที่ดี ดังนั้นเรายังมีอีกหนึ่งโซลูชั่น แต่เพื่อใช้โซลูชันนี้เราควรกลับไปที่บรรทัดของโค้ดที่personมีการสร้างวัตถุ ( var person = {};) จากนั้นเปลี่ยนเป็น:

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

สิ่งที่มันคือการสร้าง JavaScript ใหม่ObjectและแนบpropertiesObjectไปกับ__proto__แอตทริบิวต์ ดังนั้นเพื่อให้แน่ใจว่าคุณสามารถทำได้:

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

แต่จุดที่ยุ่งยากก็คือคุณสามารถเข้าถึงคุณสมบัติทั้งหมดที่กำหนดไว้ใน__proto__ระดับแรกของpersonวัตถุ (อ่านส่วนสรุปเพื่อดูรายละเอียดเพิ่มเติม)


ตามที่คุณเห็นโดยใช้สองวิธีthisนี้จะชี้ไปที่personวัตถุ

ค. JavaScript มีวิธีอื่นในการจัดเตรียมฟังก์ชันthisซึ่งใช้การโทรหรือใช้เพื่อเรียกใช้ฟังก์ชัน

ใช้ () วิธีการเรียกใช้ฟังก์ชั่นที่มีค่านี้และข้อโต้แย้งที่ได้รับให้เป็นอาร์เรย์ (หรือวัตถุเหมือนอาร์เรย์)

และ

การเรียก () วิธีการเรียกใช้ฟังก์ชั่นที่ได้รับค่านี้และข้อโต้แย้งให้เป็นรายบุคคล

ด้วยวิธีนี้ซึ่งเป็นที่ชื่นชอบเราสามารถเรียกฟังก์ชั่นของเราเช่น:

Person.call(person, "George");

หรือ

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

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

3 วิธีการเหล่านี้เป็นขั้นตอนเริ่มต้นที่สำคัญในการหาฟังก์ชันการทำงานของต้นแบบ


2- newคำหลักทำงานอย่างไร

นี่เป็นขั้นตอนที่สองเพื่อทำความเข้าใจเกี่ยวกับการ.prototypeทำงานนี่คือสิ่งที่ฉันใช้เพื่อจำลองกระบวนการ:

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

ในส่วนนี้ฉันจะพยายามทำตามขั้นตอนทั้งหมดที่ JavaScript ใช้โดยไม่ต้องใช้newคำหลักและprototypeเมื่อคุณใช้newคำหลัก ดังนั้นเมื่อเราทำnew Person("George"), Personฟังก์ชั่นทำหน้าที่เป็นผู้สร้างเหล่านี้เป็นสิ่งที่ไม่ JavaScript หนึ่งโดยหนึ่ง:

ก่อนอื่นมันทำให้วัตถุที่ว่างเปล่าโดยทั่วไปจะมีแฮชว่างเปล่าเช่น:

var newObject = {};

ข ขั้นตอนต่อไปที่ JavaScript ใช้คือการแนบวัตถุต้นแบบทั้งหมดกับวัตถุที่สร้างขึ้นใหม่

เรามีmy_person_prototypeที่นี่คล้ายกับวัตถุต้นแบบ

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

ไม่ใช่วิธีที่ JavaScript แนบคุณสมบัติที่กำหนดไว้ในต้นแบบ วิธีที่แท้จริงเกี่ยวข้องกับแนวคิดลูกโซ่ต้นแบบ


& b. แทนที่จะทำตามสองขั้นตอนเหล่านี้คุณสามารถได้ผลลัพธ์เดียวกันโดยทำดังนี้

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()

JavaScript ภายในทำให้วัตถุแฮที่ว่างเปล่าและจากนั้นมันจะช่วยให้วัตถุที่สร้างแล้วสร้างสามารถทำสิ่งที่มันต้องการเพราะนี้ภายในของตัวสร้างที่เป็นวัตถุที่ถูกสร้างขึ้นเพียงและจากนั้นก็จะช่วยให้คุณวัตถุแน่นอนว่า หากคุณยังไม่ได้ใช้คำสั่ง return ในฟังก์ชั่นของคุณหรือถ้าคุณใส่return undefined;ท้ายฟังก์ชันเนื้อหาของคุณ

ดังนั้นเมื่อจาวาสคริปต์ไปค้นหาคุณสมบัติบนวัตถุสิ่งแรกที่มันทำคือมันค้นหาบนวัตถุนั้น แล้วก็มีคุณสมบัติลับ[[prototype]]ที่เรามักจะชอบมัน__proto__และคุณสมบัตินั้นคือสิ่งที่จาวาสคริปต์มีต่อไป และเมื่อมันมองผ่าน__proto__ตราบใดที่มันเป็นวัตถุ JavaScript อีกตัวมันมีคุณสมบัติของมันเอง__proto__มันจะขึ้นไปเรื่อย ๆ จนกว่ามันจะไปถึงจุดที่สิ่งต่อไป__proto__นี้เป็นโมฆะ จุดเป็นวัตถุเดียวในจาวาสคริปต์ที่มี__proto__คุณสมบัติเป็นโมฆะคือObject.prototypeวัตถุ:

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

และนั่นคือวิธีที่มรดกทำงานใน JavaScript

โซ่ต้นแบบ

ในคำอื่น ๆ เมื่อคุณมีคุณสมบัติต้นแบบในฟังก์ชั่นและคุณเรียกใหม่ในที่หลังจาก JavaScript เสร็จสิ้นการมองหาวัตถุที่สร้างขึ้นใหม่สำหรับคุณสมบัติมันจะไปดูฟังก์ชั่น.prototypeและยังเป็นไปได้ว่าวัตถุนี้มี ต้นแบบภายในของตัวเอง และอื่น ๆ


6
a) โปรดอย่าอธิบายต้นแบบโดยการคัดลอกคุณสมบัติ b) การตั้งค่าภายใน[[ต้นแบบ]]เกิดขึ้นก่อนที่จะใช้ฟังก์ชันตัวสร้างบนอินสแตนซ์โปรดเปลี่ยนลำดับที่ c) jQuery ไม่น่าสนใจในคำถามนี้เลย
Bergi

1
@ Bergi: ขอบคุณสำหรับการชี้ให้เห็นฉันจะได้รับการชื่นชมถ้าคุณแจ้งให้เราทราบว่ามันไม่เป็นไรตอนนี้
Mehran Hatami

7
คุณช่วยทำให้มันเรียบง่ายได้ไหม? คุณพูดถูกทุกประเด็น แต่นักเรียนที่อ่านคำอธิบายนี้อาจสับสนเป็นครั้งแรก เลือกตัวอย่างที่ง่ายขึ้นและให้โค้ดอธิบายตัวเองหรือเพิ่มความคิดเห็นเพื่อชี้แจงสิ่งที่คุณหมายถึง
PM

2
@PM: ขอบคุณสำหรับความคิดเห็นของคุณ ฉันพยายามทำให้มันง่ายที่สุด แต่ฉันคิดว่าคุณพูดถูกมันยังมีบางจุดที่คลุมเครือ ดังนั้นฉันจะพยายามแก้ไขและอธิบายเพิ่มเติม :)
Mehran Hatami

1
@AndreaMattioli เพราะวิธีนี้คุณกำลังสร้างวัตถุใหม่โดยสิ้นเชิงและแทนที่สิ่งเก่าที่อาจถูกแชร์ในวัตถุอื่นด้วย โดยการแทนที่__proto__คุณจะล้างคุณสมบัติต้นแบบระดับสูงสุดทั้งหมดก่อนแล้วจึงมีฐานโปรโตใหม่ซึ่งไม่ได้ใช้ร่วมกันอีกต่อไปเว้นแต่คุณจะแบ่งปัน
Mehran Hatami

77

ต้นแบบของ Koans ทั้งเจ็ด

ในขณะที่ Ciro San สืบเชื้อสาย Mount Fire Fox หลังจากการทำสมาธิลึกจิตใจของเขาชัดเจนและสงบสุข

อย่างไรก็ตามมือของเขากระสับกระส่ายและคว้าแปรงและจดบันทึกต่อไปนี้


0)สองสิ่งที่แตกต่างสามารถเรียกว่า "ต้นแบบ":

  • คุณสมบัติต้นแบบเช่นเดียวกับใน obj.prototype

  • ต้นแบบทรัพย์สินภายในแสดงเป็น[[Prototype]] ใน ES5

    มันสามารถเรียกดูผ่านทาง Object.getPrototypeOf()ES5

    Firefox ทำให้สามารถเข้าถึงได้ผ่าน__proto__คุณสมบัติเป็นส่วนขยาย ES6 ตอนนี้กล่าวถึง__proto__ความต้องการบางอย่างสำหรับตัวเลือก


1)แนวคิดเหล่านั้นมีอยู่เพื่อตอบคำถาม:

เมื่อฉันทำobj.propertyJS จะค้นหา.propertyที่ไหน

การสืบทอดแบบคลาสสิกควรส่งผลกระทบต่อการค้นหาคุณสมบัติโดยสังหรณ์ใจ


2)

  • __proto__จะใช้สำหรับจุดการค้นหาสถานที่ให้บริการในขณะที่.obj.property
  • .prototypeจะไม่นำมาใช้สำหรับการค้นหาโดยตรงทางอ้อมเท่านั้นในขณะที่มันเป็นตัวกำหนดในการสร้างวัตถุที่มี__proto__new

ลำดับการค้นหาคือ:

  • objเพิ่มคุณสมบัติด้วยobj.p = ...หรือObject.defineProperty(obj, ...)
  • คุณสมบัติของ obj.__proto__
  • คุณสมบัติของobj.__proto__.__proto__และอื่น ๆ
  • ถ้าบาง__proto__เป็นผลตอบแทนnullundefined

นี่คือสิ่งที่เรียกว่าห่วงโซ่ต้นแบบ

คุณสามารถหลีกเลี่ยงการ.ค้นหาด้วย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(i) { this.i = i }
var f = new F(1)

สอดคล้องกับไดอะแกรมต่อไปนี้ (บางNumberสิ่งถูกละเว้น):

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

แผนภาพนี้แสดงโหนดวัตถุที่กำหนดไว้ล่วงหน้าหลายภาษา:

  • null
  • Object
  • Object.prototype
  • Function
  • Function.prototype
  • 1
  • Number.prototype(สามารถพบได้ด้วย(1).__proto__วงเล็บบังคับเพื่อสนองไวยากรณ์)

โค้ด 2 บรรทัดของเราสร้างวัตถุใหม่ต่อไปนี้เท่านั้น:

  • f
  • F
  • F.prototype

iตอนนี้เป็นคุณสมบัติของfเพราะเมื่อคุณ:

var f = new F(1)

มันประเมิน Fด้วยthisการเป็นค่าที่newจะกลับมาซึ่งได้รับมอบหมายfแล้ว


5) .constructorโดยปกติมาจากF.prototypeการ.ค้นหา:

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

เมื่อเราเขียนf.constructorJavaScript จะทำการ.ค้นหาดังนี้:

  • f ไม่ได้มี .constructor
  • f.__proto__ === F.prototype มี .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.
c = new C(1)
c.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://stackoverflow.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

แผนภาพที่เรียบง่ายโดยไม่มีวัตถุที่กำหนดไว้ล่วงหน้าทั้งหมด:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

ลองใช้เวลาสักครู่เพื่อศึกษาวิธีการทำงานต่อไปนี้

c = new C(1)
c.inc() === 2

บรรทัดแรกตั้งค่าc.iเป็น1ได้อธิบายไว้ใน "4)"

ในบรรทัดที่สองเมื่อเรา:

c.inc()
  • .incพบผ่าน[[Prototype]]ห่วงโซ่: c-> C->C.prototype ->inc
  • เมื่อเราเรียกใช้ฟังก์ชันใน Javascript ในขณะที่X.Y()JavaScript ตั้งค่าthisให้เท่ากับXภายในการY()เรียกใช้ฟังก์ชันโดยอัตโนมัติ!

ตรรกะเดียวกันที่แน่นอนยังอธิบายd.incและd.inc2และ

บทความนี้https://javascript.info/class#not-just-a-syntax-sugarกล่าวถึงผลกระทบเพิ่มเติมที่classควรรู้ บางคนอาจไม่สามารถทำได้โดยไม่มีclassคำหลัก (ตรวจสอบสิ่งที่ต้องทำ):


1
@tomasb ขอบคุณ! "ฉันไม่รู้ว่าคุณได้รับสิ่งนี้จากที่ใด": หลังจากฉันเห็นภาษาแบบไดนามิกสองสามครั้งฉันสังเกตเห็นว่าสิ่งที่สำคัญที่สุดเกี่ยวกับระบบการเรียนคือการ.ค้นหา (และจำนวนสำเนาของข้อมูล) . ดังนั้นฉันจึงเริ่มเข้าใจประเด็นนั้น ส่วนที่เหลือคือ Google + บล็อกโพสต์ + ล่าม Js :)
Ciro Santilli 冠状病毒审查六四事件法轮功

1
ฉันยังไม่เข้าใจว่าทำไม g.constructor === วัตถุเพราะคุณพูดว่า "4) เมื่อคุณทำ f = new F ใหม่ก็กำหนด f.constructor = F" คุณช่วยอธิบายเพิ่มเติมให้ฉันได้ไหม อย่างไรก็ตามนี่คือคำตอบที่ดีที่สุดที่ฉันกำลังมองหา ขอบคุณมาก!
nguyenngoc101

@ nguyenngoc101 ขอบคุณ! sets f.constructor = Fส่วนหนึ่งเป็นโจ๋งครึ่มผิดและขัดแย้งกับส่วนเพิ่มเติม: .constructorพบผ่าน.การค้นหาในห่วงโซ่ต้นแบบ แก้ไขทันที
Ciro Santilli 法轮功病毒审查六四事件法轮功

จากการอภิปรายทั้งหมดที่ฉันได้รับ (มาจากการสืบทอดแบบคลาสสิก) ถ้าฉันสร้างฟังก์ชันคอนสตรัคเตอร์และพยายามสร้างอินสแตนซ์ของมันโดยใช้โอเปอเรเตอร์ใหม่ฉันจะได้รับวิธีการและคุณสมบัติที่แนบมากับวัตถุ proto เท่านั้น และคุณสมบัติในการคัดค้านวัตถุถ้าเราต้องการรับมรดกไมล์ใช่มั้ย
blackHawk

1
@CiroSantilli 刘晓波死六四事件法轮功ฉันไม่คิดว่ามันเป็นข้อบกพร่องใน Chromium ฉันคิดว่ามันเป็นเพียงอาการที่fต้นแบบของถูกตั้งค่าจากFเวลาก่อสร้างเท่านั้น fจะไม่ทราบหรือสนใจF.prototypeได้ตลอดเวลาหลังจากสร้างครั้งแรก
John Glassmyer

76

prototypeอนุญาตให้คุณทำคลาส หากคุณไม่ได้ใช้งานprototypeมันจะกลายเป็นแบบคงที่

นี่เป็นตัวอย่างสั้น ๆ

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

ในกรณีข้างต้นคุณมีการทดสอบการโทรฟังก์ชั่นคงที่ ฟังก์ชั่นนี้สามารถเข้าถึงได้โดย obj.test เท่านั้นซึ่งคุณสามารถจินตนาการ obj ให้เป็นคลาสได้

ที่อยู่ในรหัสด้านล่าง

function obj()
{
}

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

obj ได้กลายเป็นคลาสที่สามารถยกตัวอย่างได้ obj หลายอินสแตนซ์สามารถมีอยู่และพวกเขาทั้งหมดมีtestฟังก์ชัน

ข้างต้นคือความเข้าใจของฉัน ฉันทำให้เป็นชุมชนวิกิดังนั้นผู้คนสามารถแก้ไขฉันได้หากฉันทำผิด


13
-1: prototypeเป็นคุณสมบัติของฟังก์ชั่นคอนสตรัคเตอร์ไม่ใช่กรณีเช่นรหัสของคุณผิด! บางทีคุณอาจจะหมายถึงไม่ได้มาตรฐานคุณสมบัติ__proto__ของวัตถุ แต่ที่เป็นสัตว์ที่แตกต่างกันทั้ง ...
คริสโต

@Christoph - ขอบคุณสำหรับการชี้ให้เห็น ฉันได้อัปเดตโค้ดตัวอย่างแล้ว
Ramesh

3
มันมีอะไรมากกว่านั้น ... พลัส JavaScript ไม่ใช่ภาษาที่ใช้เป็นคลาส - มันเกี่ยวข้องกับการสืบทอดผ่านทางต้นแบบคุณต้องครอบคลุมความแตกต่างในรายละเอียดเพิ่มเติม!
James

5
ฉันคิดว่าคำตอบนี้เป็นความเข้าใจผิดเล็กน้อย
Armin Cifuentes

อาจเป็นคำตอบคือ 'นำไปสู่ความเข้าใจผิด' แต่อธิบายว่าใช้ต้นแบบใดและตอนนี้ทุกอย่างชัดเจนสำหรับฉันหลังจากที่คำตอบเหล่านั้นด้วยคะแนนนับร้อยขึ้นไป ขอบคุณ.
Aleks

66

หลังจากอ่านกระทู้นี้ฉันรู้สึกสับสนกับ JavaScript Prototype Chain แล้วฉันก็พบแผนภูมิเหล่านี้

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance * [[protytype]] * และ <code> prototype </code> คุณสมบัติของฟังก์ชันวัตถุ

เป็นแผนภูมิที่ชัดเจนเพื่อแสดงการสืบทอด JavaScript โดย Prototype Chain

และ

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

อันนี้มีตัวอย่างพร้อมโค้ดและไดอะแกรมที่ดีหลายอัน

ในที่สุดต้นแบบโซ่ตกกลับไปที่ Object.prototype

โซ่ต้นแบบสามารถขยายทางเทคนิคได้นานเท่าที่คุณต้องการในแต่ละครั้งโดยการตั้งค่าต้นแบบของคลาสย่อยให้เท่ากับวัตถุของคลาสแม่

หวังว่ามันจะมีประโยชน์สำหรับคุณที่จะเข้าใจ JavaScript Prototype Chain


เป็นไปได้หรือไม่ที่จะมีการสืบทอดหลายรายการบน Javascript?

Foo เป็นวัตถุตามตัวอักษรที่นี่หรือฟังก์ชั่นวัตถุ? หากวัตถุเป็นตัวอักษรฉันเชื่อว่า Foo.prototype จะไม่ชี้กลับไปที่ Foo ผ่านตัวสร้าง
Madhur Ahuja

@ user3376708 JavaScript รองรับการสืบทอดเดี่ยว ( แหล่งที่มา )
Rafael Eyng

@ Nuno_147 มันไม่ชัดเจนในตอนแรก แต่ถ้าคุณดูนานพอคุณอาจได้อะไรจากมัน
marcelocra

3
คุณช่วยอธิบายความ[[Prototype]]หมายได้อย่างไร
CodyBugstein

40

วัตถุทุกชิ้นมีคุณสมบัติภายใน[[Prototype]]เชื่อมโยงไปยังวัตถุอื่น:

object [[Prototype]]  anotherObject

ในจาวาสคริปต์ดั้งเดิมวัตถุที่เชื่อมโยงเป็น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;

คุณไม่เห็นเป้าหมายลิงก์ ( Object.prototype) ในคำสั่งใหม่ แทนเป้าหมายโดยนัยคอนสตรัค (Object )

โปรดจำไว้ว่า:

  • วัตถุทุกคนมีการเชื่อมโยง[[Prototype]]บางครั้งเปิดเผยว่า__proto__
  • ทุกฟังก์ชั่นมีprototypeคุณสมบัติโดยเริ่มแรกถือวัตถุว่างเปล่า
  • วัตถุที่สร้างขึ้นด้วยใหม่จะเชื่อมโยงกับprototypeคุณสมบัติของตัวสร้างของพวกเขา
  • หากไม่เคยใช้ฟังก์ชั่นเป็นตัวสร้างprototypeคุณสมบัติของมันจะไม่ถูกใช้งาน
  • ถ้าคุณไม่จำเป็นต้องตัวสร้างให้ใช้Object.createnewแทน

1
การแก้ไข 5 ลบข้อมูลที่มีประโยชน์บางส่วนออกรวมถึงข้อมูลเกี่ยวกับ Object.create () ดูการแก้ไข 4
Palec

@Palec ฉันควรจะเพิ่มอะไรกลับ?
sam

2
IMO อย่างน้อยลิงก์ไปยังObject.create()เอกสาร @sam ลิงก์ไปยัง__proto__และObject.prototypeจะเป็นการปรับปรุงที่ดี และฉันชอบตัวอย่างของคุณว่าต้นแบบทำงานกับตัวสร้างได้อย่างไรและObject.create()พวกเขาอาจเป็นส่วนที่ยาวและเกี่ยวข้องน้อยกว่าที่คุณต้องการกำจัด
Palec

จากการอภิปรายทั้งหมดที่ฉันได้รับ (มาจากการสืบทอดแบบคลาสสิก) ถ้าฉันสร้างฟังก์ชันคอนสตรัคเตอร์และพยายามสร้างอินสแตนซ์ของมันโดยใช้โอเปอเรเตอร์ใหม่ฉันจะได้รับวิธีการและคุณสมบัติที่แนบมากับวัตถุ proto เท่านั้น และคุณสมบัติในการคัดค้านวัตถุถ้าเราต้องการรับมรดกไมล์ใช่มั้ย
blackHawk

29

Javascript ไม่มีการสืบทอดในความหมายปกติ แต่มันมีสายโซ่ต้นแบบ

โซ่ต้นแบบ

หากไม่พบสมาชิกของวัตถุในวัตถุจะค้นหาในโซ่ต้นแบบ โซ่ประกอบด้วยวัตถุอื่น ๆ ต้นแบบของอินสแตนซ์ที่กำหนดสามารถเข้าถึงได้ด้วย__proto__ตัวแปร วัตถุทุกชิ้นมีสิ่งหนึ่งเนื่องจากไม่มีความแตกต่างระหว่างคลาสและอินสแตนซ์ใน javascript

ข้อดีของการเพิ่มฟังก์ชั่น / ตัวแปรให้กับต้นแบบคือมันต้องอยู่ในหน่วยความจำเพียงครั้งเดียวไม่ใช่สำหรับทุกอินสแตนซ์

นอกจากนี้ยังมีประโยชน์สำหรับการสืบทอดเนื่องจากเชนลูกโซ่ต้นแบบอาจประกอบด้วยวัตถุอื่น ๆ อีกมากมาย


1
FF และ Chrome รองรับprotoแต่ไม่ใช่ IE หรือ Opera
มี

Georg โปรดชี้แจงสำหรับ noob - "ไม่มีความแตกต่างระหว่างคลาสและอินสแตนซ์ใน javascript" - คุณสามารถทำอย่างละเอียด? มันทำงานอย่างไร
Hamish Grubijan

จากการอภิปรายทั้งหมดที่ฉันได้รับ (มาจากการสืบทอดแบบคลาสสิก) ถ้าฉันสร้างฟังก์ชันคอนสตรัคเตอร์และพยายามสร้างอินสแตนซ์ของมันโดยใช้โอเปอเรเตอร์ใหม่ฉันจะได้รับวิธีการและคุณสมบัติที่แนบมากับวัตถุ proto เท่านั้น และคุณสมบัติในการคัดค้านวัตถุถ้าเราต้องการรับมรดกไมล์ใช่มั้ย
blackHawk

28

บทความนี้มีความยาว แต่ฉันแน่ใจว่ามันจะล้างคำถามส่วนใหญ่ของคุณเกี่ยวกับลักษณะ "ต้นแบบ" ของการสืบทอด JavaScript และยิ่งกว่านั้น โปรดอ่านบทความฉบับสมบูรณ์

JavaScript นั้นมีชนิดข้อมูลสองชนิด

  • ไม่ใช่วัตถุ
  • วัตถุ

ไม่ใช่วัตถุ

ต่อไปนี้เป็นชนิดข้อมูลที่ไม่ใช่วัตถุ

  • เชือก
  • หมายเลข (รวมถึง NaN และ Infinity)
  • ค่าบูลีน (จริง, เท็จ)
  • ไม่ได้กำหนด

ชนิดข้อมูลเหล่านี้จะคืนค่าต่อไปนี้เมื่อคุณใช้ตัวดำเนินการtypeof

typeof "string literal" (หรือตัวแปรที่มีสตริงตัวอักษร) === 'string'

typeof 5 (หรือตัวอักษรตัวเลขหรือตัวแปรใด ๆ ที่มีตัวอักษรตัวเลขหรือNaN หรือ Infynity ) === 'number'

typeof จริง (หรือเท็จหรือตัวแปรที่มีจริงหรือเท็จ ) === 'บูลีน'

typeof undefined (หรือตัวแปรที่ไม่ได้กำหนดหรือตัวแปรที่มีundefined ) === 'undefined'

สตริง , จำนวนและบูลีนชนิดข้อมูลที่สามารถแสดงทั้งวัตถุและองค์กรไม่แสวงหาวัตถุ .When พวกเขาจะแสดงเป็นวัตถุ typeof ของพวกเขาอยู่เสมอ === 'วัตถุ' เราจะกลับมาเมื่อเราเข้าใจชนิดข้อมูลวัตถุ

วัตถุ

ประเภทข้อมูลวัตถุสามารถแบ่งออกเป็นสองประเภทเพิ่มเติม

  1. วัตถุประเภทฟังก์ชั่น
  2. วัตถุประเภทฟังก์ชั่นที่ไม่ใช่

วัตถุประเภทฟังก์ชั่นเป็นคนที่กลับสตริง'ฟังก์ชั่น'กับtypeofผู้ประกอบการ ฟังก์ชันที่ผู้ใช้กำหนดทั้งหมดและ JavaScript ทั้งหมดที่มีอยู่ในวัตถุที่สามารถสร้างวัตถุใหม่โดยใช้ตัวดำเนินการใหม่อยู่ในหมวดหมู่นี้ สำหรับเช่น

  • วัตถุ
  • เชือก
  • จำนวน
  • บูลีน
  • แถว
  • อาร์เรย์ที่พิมพ์
  • นิพจน์ทั่วไป
  • ฟังก์ชัน
  • วัตถุในตัวอื่น ๆ ทั้งหมดที่สามารถสร้างวัตถุใหม่โดยใช้ตัวดำเนินการใหม่
  • ฟังก์ชัน UserDefinedFunction () {/ * รหัสที่ผู้ใช้กำหนด * /}

ดังนั้น typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (ฟังก์ชัน) == = typeof (UserDefinedFunction) === 'function'

วัตถุประเภทฟังก์ชั่นทั้งหมดเป็นจริงของอินสแตนซ์ของวัตถุ JavaScript ที่สร้างขึ้นในฟังก์ชั่น (รวมถึงวัตถุฟังก์ชั่นนั่นคือมันถูกกำหนดซ้ำ) มันเหมือนกับว่าวัตถุเหล่านี้ได้รับการกำหนดในวิธีดังต่อไปนี้

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

ตามที่ระบุไว้ในวัตถุประเภทฟังก์ชั่นยังสามารถสร้างวัตถุใหม่โดยใช้ประกอบการใหม่ สำหรับเช่นวัตถุประเภทObject , String , Number , Boolean , Array , RegExp หรือUserDefinedFunctionสามารถสร้างขึ้นได้โดยใช้

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

วัตถุที่สร้างขึ้นจึงมีทั้งหมดไม่ใช่วัตถุประเภทฟังก์ชั่นและกลับของพวกเขาtypeof === 'วัตถุ' ในทุกกรณีวัตถุ "a" ไม่สามารถสร้างวัตถุเพิ่มเติมโดยใช้ตัวดำเนินการใหม่ได้ ดังนั้นต่อไปนี้เป็นสิ่งที่ผิด

var b=new a() //error. a is not typeof==='function'

สร้างขึ้นในวัตถุคณิตศาสตร์เป็นtypeof === 'วัตถุ' ดังนั้นไม่สามารถสร้างออบเจ็กต์ประเภทคณิตศาสตร์ใหม่โดยโอเปอเรเตอร์ใหม่

var b=new Math() //error. Math is not typeof==='function'

นอกจากนี้ยังสังเกตเห็นว่าวัตถุ , อาร์เรย์และนิพจน์ทั่วไปที่ฟังก์ชั่นสามารถสร้างวัตถุใหม่โดยไม่ต้องใช้ประกอบการใหม่ อย่างไรก็ตามคนที่โง่เขลาไม่ได้

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

ฟังก์ชั่นที่ผู้ใช้กำหนดเป็นกรณีพิเศษ

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

เนื่องจากวัตถุประเภทฟังก์ชั่นสามารถสร้างวัตถุใหม่พวกเขาจะเรียกว่าคอนสตรัคเตอร์

ทุกคนสร้าง / ฟังก์ชั่น (ไม่ว่าจะสร้างขึ้นในหรือผู้ใช้กำหนด) เมื่อกำหนดโดยอัตโนมัติมีคุณสมบัติที่เรียกว่า"ต้นแบบ"ที่มีค่าตามค่าเริ่มต้นถูกกำหนดให้เป็นวัตถุ วัตถุนี้ตัวเองมีคุณสมบัติที่เรียกว่า"คอนสตรัค"ซึ่งเริ่มต้นโดยการอ้างอิงกลับสร้าง / ฟังก์ชั่น

ตัวอย่างเช่นเมื่อเรากำหนดฟังก์ชั่น

function UserDefinedFunction()
{
}

ต่อไปนี้เกิดขึ้นโดยอัตโนมัติ

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

นี้ทรัพย์สิน "ต้นแบบ"เป็นเพียงอยู่ในวัตถุประเภทฟังก์ชั่น (และไม่เคยอยู่ในองค์กรไม่แสวงหาวัตถุประเภทฟังก์ชั่น )

นี่เป็นเพราะเมื่อมีการสร้างวัตถุใหม่ (โดยใช้ตัวดำเนินการใหม่) มันจะสืบทอดคุณสมบัติและวิธีการทั้งหมดจากวัตถุต้นแบบของฟังก์ชันตัวสร้างปัจจุบันเช่นการ อ้างอิงภายใน จะถูกสร้างขึ้นในวัตถุที่สร้างขึ้นใหม่ที่อ้างอิงวัตถุที่อ้างอิงโดยวัตถุต้นแบบ

นี้"อ้างอิงภายใน"ที่สร้างขึ้นในวัตถุสำหรับการอ้างอิงคุณสมบัติที่สืบทอดมาเป็นที่รู้จักในฐานะต้นแบบของวัตถุ (ซึ่งอ้างอิงวัตถุอ้างอิงโดยสร้างเป็น"ต้นแบบ"คุณสมบัติ แต่จะแตกต่างจากมัน) สำหรับวัตถุใด ๆ (ฟังก์ชั่นหรือฟังก์ชั่นไม่แสวงหา) นี้สามารถเรียกใช้Object.getPrototypeOf ()วิธีการ การใช้วิธีนี้เราสามารถติดตามต้นแบบลูกโซ่ของวัตถุได้

นอกจากนี้ทุกวัตถุที่สร้างขึ้น ( ประเภทฟังก์ชั่นหรือประเภทที่ไม่ใช่ฟังก์ชั่น ) มีคุณสมบัติ"ตัวสร้าง"ซึ่งสืบทอดมาจากวัตถุที่อ้างอิงโดยคุณสมบัติต้นแบบของฟังก์ชั่นการสร้าง โดยค่าเริ่มต้นคุณสมบัติ"ตัวสร้าง"นี้อ้างอิงถึงฟังก์ชันตัวสร้างที่สร้างขึ้น (หากไม่มีการเปลี่ยนแปลง "ต้นแบบ" ของฟังก์ชันตัวสร้างเริ่มต้น)

สำหรับวัตถุทุกประเภทฟังก์ชั่นฟังก์ชั่นคอนสตรัคเสมอ ฟังก์ชั่น () {}

สำหรับวัตถุประเภทที่ไม่ใช่ฟังก์ชั่น (เช่น Javascript สร้างขึ้นในวัตถุทางคณิตศาสตร์) ฟังก์ชั่นคอนสตรัคเป็นฟังก์ชั่นที่สร้างขึ้น สำหรับคณิตศาสตร์วัตถุมันเป็นวัตถุฟังก์ชั่น () {}

แนวคิดทั้งหมดที่อธิบายข้างต้นอาจเป็นเรื่องเล็กน้อยที่จะเข้าใจโดยไม่ต้องมีรหัสสนับสนุน โปรดอ่านบรรทัดโค้ดต่อไปนี้เพื่อทำความเข้าใจแนวคิด พยายามที่จะรันมันเพื่อความเข้าใจที่ดีขึ้น

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

ต้นแบบลูกโซ่ของทุกวัตถุในที่สุดจะติดตามกลับไปที่ Object.prototype (ซึ่งตัวมันเองไม่มีวัตถุต้นแบบ) รหัสต่อไปนี้สามารถใช้สำหรับการติดตามโซ่ต้นแบบของวัตถุ

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

โซ่ต้นแบบสำหรับวัตถุต่าง ๆ ทำงานดังนี้

  • ทุกฟังก์ชั่นวัตถุ (รวมถึงสร้างขึ้นในฟังก์ชั่นวัตถุ) -> Function.prototype -> Object.prototype -> null
  • Simple Objects (สร้างโดย Object ใหม่ () หรือ {} รวมถึงบิวด์ในวัตถุ Math) -> Object.prototype -> null
  • วัตถุที่สร้างขึ้นด้วยใหม่หรือ Object.create -> โซ่ต้นแบบอย่างน้อยหนึ่งโซ่ -> Object.prototype -> null

สำหรับการสร้างวัตถุที่ไม่มีต้นแบบให้ใช้ดังต่อไปนี้:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

บางคนอาจคิดว่าการตั้งค่าคุณสมบัติต้นแบบของตัวสร้างเป็นโมฆะจะต้องสร้างวัตถุที่มีต้นแบบต้นแบบเป็นโมฆะ อย่างไรก็ตามในกรณีดังกล่าวต้นแบบของวัตถุที่สร้างขึ้นใหม่ถูกตั้งค่าเป็น Object.prototype และตัวสร้างของมันถูกตั้งค่าเป็นฟังก์ชั่นวัตถุ นี่แสดงให้เห็นโดยรหัสต่อไปนี้

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

ต่อไปนี้ในบทสรุปของบทความนี้

  • มีประเภทวัตถุสองประเภทประเภทฟังก์ชั่นและประเภทไม่ใช่ฟังก์ชั่น
  • เฉพาะประเภทฟังก์ชั่นวัตถุที่สามารถสร้างวัตถุใหม่โดยใช้ใหม่ผู้ประกอบการ วัตถุที่สร้างขึ้นจึงเป็นวัตถุประเภท Non Function ไม่ใช่วัตถุประเภทฟังก์ชั่นไม่สามารถต่อสร้างวัตถุที่ใช้ประกอบการใหม่

  • วัตถุประเภทฟังก์ชั่นทั้งหมดโดยค่าเริ่มต้นมีคุณสมบัติ"ต้นแบบ" นี้"ต้นแบบ"ทรัพย์สินอ้างอิงวัตถุที่มี"คอนสตรัค"ทรัพย์สินที่โดยอ้างอิงเริ่มต้นวัตถุประเภทฟังก์ชั่นของตัวเอง

  • วัตถุทุกชนิด ( ประเภทฟังก์ชั่นและประเภทฟังก์ชั่นแบบไม่ ) มีคุณสมบัติ "คอนสตรัค" ว่าด้วยการอ้างอิงเริ่มต้นวัตถุประเภทฟังก์ชั่น / สร้างที่สร้างมัน

  • ทุกวัตถุที่ถูกสร้างขึ้นภายในอ้างอิงถึงวัตถุที่อ้างอิงโดย คุณสมบัติ"ต้นกำเนิด"ของ Constructor ที่สร้างขึ้น วัตถุนี้เป็นที่รู้จักกันในชื่อต้นแบบของวัตถุที่สร้างขึ้น(ซึ่งแตกต่างจากฟังก์ชั่น "วัตถุต้นแบบ" คุณสมบัติประเภทวัตถุซึ่งมันอ้างอิง) วิธีนี้วัตถุที่สร้างขึ้นสามารถเข้าถึงวิธีการและคุณสมบัติที่กำหนดในวัตถุที่อ้างอิงโดยตรงโดยคุณสมบัติ "ต้นแบบ" ของตัวสร้าง (ในเวลาที่สร้างวัตถุ)

  • ต้นแบบของวัตถุ (และด้วยเหตุนี้ชื่อคุณสมบัติได้รับมรดกของมัน) สามารถเรียกใช้Object.getPrototypeOf () วิธีการ ในความเป็นจริงวิธีนี้สามารถใช้สำหรับการนำทางต้นแบบลูกโซ่ทั้งหมดของวัตถุ

  • ต้นแบบลูกโซ่ของทุกวัตถุในที่สุดจะติดตามกลับไปที่ Object.prototype (ยกเว้นว่าวัตถุนั้นถูกสร้างขึ้นโดยใช้ Object.create (null) ในกรณีที่วัตถุนั้นไม่มีต้นแบบ)

  • typeof (new Array ()) === 'object'คือการออกแบบของภาษาและไม่ใช่ความผิดพลาดตามที่ดักลาส Crockford

  • การตั้งค่าคุณสมบัติต้นแบบของตัวสร้างเป็นโมฆะ (หรือไม่ได้กำหนด, จำนวน, จริง, เท็จ, สตริง) จะต้องไม่สร้างวัตถุที่มีต้นแบบต้นแบบที่ว่างเปล่า ในกรณีเช่นนี้ต้นแบบของวัตถุที่สร้างขึ้นใหม่จะถูกตั้งค่าเป็น Object.prototype และตัวสร้างของวัตถุนั้นถูกตั้งค่าเป็นฟังก์ชันวัตถุ

หวังว่านี่จะช่วยได้


24

แนวคิดของการprototypalสืบทอดเป็นหนึ่งในสิ่งที่ซับซ้อนที่สุดสำหรับนักพัฒนาหลายคน ลองทำความเข้าใจถึงรากเหง้าของปัญหาเพื่อทำความเข้าใจให้prototypal inheritanceดีขึ้น เริ่มจากplainฟังก์ชั่นกันก่อน

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าเราใช้newโอเปอเรเตอร์กับTree functionเราเรียกมันว่าเป็นconstructorฟังก์ชั่น

ป้อนคำอธิบายรูปภาพที่นี่

ทุกฟังก์ชั่นที่มีJavaScript prototypeเมื่อคุณเข้าสู่ระบบTree.prototypeคุณจะได้รับ ...

ป้อนคำอธิบายรูปภาพที่นี่

หากคุณดูconsole.log()ผลลัพธ์ข้างต้นคุณสามารถเห็นคุณสมบัติคอนสตรัคเตอร์ในTree.prototypeและ__proto__คุณสมบัติด้วย เครื่องหมาย__proto__แสดงprototypeว่าสิ่งนี้functionมีพื้นฐานมาจากและนี่เป็นเพียงธรรมดาที่JavaScript functionยังไม่มีการinheritanceตั้งค่าจึงหมายถึงสิ่งObject prototypeที่เป็นสิ่งที่สร้างขึ้นใน JavaScript ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

มีสิ่งต่าง ๆ เช่น.toString, .toValue, .hasOwnPropertyฯลฯ ...

__proto__ซึ่งถูกนำมา Mozilla ของฉันจะเลิกใช้แล้วและจะถูกแทนที่ด้วยวิธีการที่จะได้รับObject.getPrototypeOfobject's prototype

ป้อนคำอธิบายรูปภาพที่นี่

Object.getPrototypeOf(Tree.prototype); // Object {} 

Tree prototypeให้เพิ่มวิธีการของเรา

ป้อนคำอธิบายรูปภาพที่นี่

เราได้ปรับเปลี่ยนRootและเพิ่มfunctionสาขาเข้าไป

ป้อนคำอธิบายรูปภาพที่นี่

ซึ่งหมายความว่าเมื่อคุณสร้างสิ่งใดสิ่งinstanceหนึ่งTreeคุณสามารถเรียกได้ว่าเป็นbranchวิธีการ

ป้อนคำอธิบายรูปภาพที่นี่

นอกจากนี้เรายังสามารถเพิ่มprimitivesหรือของเราobjectsPrototype

ป้อนคำอธิบายรูปภาพที่นี่

ลองเพิ่มของเราchild-treeTree

ป้อนคำอธิบายรูปภาพที่นี่

นี่Childสืบทอดของมันprototypeจากต้นไม้, สิ่งที่เรากำลังทำอะไรที่นี่คือการใช้วิธีการในการสร้างวัตถุใหม่ตามออกสิ่งที่คุณผ่านที่นี่มันเป็นObject.create() Tree.prototypeในกรณีนี้สิ่งที่เรากำลังทำคือการตั้งค่าต้นแบบของเด็กเป็นวัตถุใหม่ที่มีลักษณะเหมือนTreeต้นแบบ ต่อไปเราจะมีการตั้งค่าChild's constructor to Child, Tree()ถ้าเราไม่ได้มันจะชี้ไปที่

ป้อนคำอธิบายรูปภาพที่นี่

Childตอนนี้มีของตัวเองprototypeมัน__proto__ชี้ไปTreeและชี้ไปที่ฐานTree's prototypeObject

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

ตอนนี้คุณสร้างinstanceของChildและโทรซึ่งเป็นครั้งแรกที่มีอยู่ในbranch Treeเรายังไม่ได้กำหนดจริงของเราในbranch Child prototypeแต่Root prototypeเด็กที่สืบทอดมาจาก

ป้อนคำอธิบายรูปภาพที่นี่

ใน JS ทุกอย่างไม่ใช่วัตถุทุกอย่างสามารถทำหน้าที่เหมือนวัตถุ

Javascriptมีพื้นฐานเหมือนstrings, number, booleans, undefined, null.พวกเขาจะไม่แต่ก็สามารถทำหน้าที่เหมือนobject(i.e reference types) objectลองดูตัวอย่างที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ในบรรทัดแรกของรายการนี้primitiveค่าสตริงจะถูกกำหนดให้เป็นชื่อ บรรทัดที่สองถือว่าชื่อเหมือนobjectและเรียกcharAt(0)โดยใช้เครื่องหมายจุด

นี่คือสิ่งที่เกิดขึ้นเบื้องหลัง: // สิ่งที่JavaScriptเครื่องยนต์ทำ

ป้อนคำอธิบายรูปภาพที่นี่

String objectมีอยู่เฉพาะสำหรับคำสั่งอย่างใดอย่างหนึ่งก่อนที่จะถูกทำลาย (กระบวนการที่เรียกว่าautoboxing) prototypal inheritanceลองอีกครั้งได้รับกลับไปของเรา

  • Javascriptสนับสนุนมรดกทางขึ้นอยู่กับdelegation prototypes
  • แต่ละคนFunctionมีprototypeคุณสมบัติซึ่งหมายถึงวัตถุอื่น
  • properties/functionsถูกมองจากobjectตัวมันเองหรือผ่าน prototypeเครือข่ายถ้ามันไม่มีอยู่จริง

prototypeใน JS เป็นวัตถุที่คุณไปยังผู้ปกครองของผู้อื่นyields [เช่น .. การมอบสิทธิ์]หมายความว่าหากคุณไม่สามารถทำอะไรคุณจะบอกให้คนอื่นทำเพื่อคุณobject Delegation

ป้อนคำอธิบายรูปภาพที่นี่

https://jsfiddle.net/say0tzpL/1/

หากคุณค้นหาซอด้านบนสุนัขสามารถเข้าถึงtoStringวิธีการได้ แต่ไม่สามารถใช้ได้ แต่สามารถใช้ได้ผ่านทางโซ่ต้นแบบที่ได้รับมอบหมายObject.prototype

ป้อนคำอธิบายรูปภาพที่นี่

หากคุณดูด้านล่างเรากำลังพยายามเข้าถึงcallวิธีการที่มีให้ในทุกfunction

ป้อนคำอธิบายรูปภาพที่นี่

https://jsfiddle.net/rknffckc/

หากคุณค้นหาซอดังกล่าวข้างต้นProfileฟังก์ชั่นมีการเข้าถึงcallวิธีการ แต่ไม่สามารถใช้ได้ในนั้น แต่สามารถใช้ได้ผ่านห่วงโซ่ต้นแบบที่ได้รับมอบหมายFunction.prototype

ป้อนคำอธิบายรูปภาพที่นี่

หมายเหตุ: prototypeเป็นคุณสมบัติของตัวสร้างฟังก์ชั่นในขณะที่__proto__เป็นคุณสมบัติของวัตถุที่สร้างขึ้นจากฟังก์ชั่นการสร้าง ฟังก์ชั่นทุกคนมาพร้อมกับคุณสมบัติที่มีค่าเป็นที่ว่างเปล่าprototype objectเมื่อเราสร้างตัวอย่างของฟังก์ชั่นที่เราได้รับทรัพย์สินภายใน[[Prototype]]หรือมีการอ้างอิงเป็นต้นแบบของฟังก์ชั่นที่__proto__constructor

ป้อนคำอธิบายรูปภาพที่นี่

แผนภาพด้านบนดูซับซ้อนเล็กน้อย แต่นำเสนอภาพรวมทั้งหมดเกี่ยวกับวิธีการprototype chainingทำงาน เดินผ่านสิ่งนี้ช้าๆ:

มีสองตัวอย่างb1และb2ผู้สร้างBarและผู้ปกครองคือ Foo และมีสองวิธีจากห่วงโซ่ต้นแบบidentifyและspeakผ่านBarและFoo

ป้อนคำอธิบายรูปภาพที่นี่

https://jsfiddle.net/kbp7jr7n/

หากคุณค้นหาโค้ดด้านบนเรามีFooConstructor ที่มีเมธอดidentify()และBarConstructor ที่มีspeakเมธอด เราสร้างสองBarอินสแตนซ์b1และประเภทของผู้ปกครองที่มีคือb2 Fooตอนนี้ในขณะที่speakวิธีการโทรของBarเราสามารถที่จะระบุผู้ที่โทรพูดผ่านprototypeห่วงโซ่

ป้อนคำอธิบายรูปภาพที่นี่

Barตอนนี้มีวิธีการทั้งหมดFooที่กำหนดไว้ในprototypeนั้น ลองมาขุดต่อไปในการทำความเข้าใจObject.prototypeและFunction.prototypeและวิธีการที่พวกเขาจะเกี่ยวข้อง หากคุณมองขึ้นคอนสตรัคของFoo, BarและมีObjectFunction constructor

ป้อนคำอธิบายรูปภาพที่นี่

prototypeของBarมีFoo, prototypeของFooเป็นObjectและถ้าคุณดูอย่างใกล้ชิดprototypeของที่เกี่ยวข้องกับFooObject.prototype

ป้อนคำอธิบายรูปภาพที่นี่

ก่อนที่เราจะปิดตัวลงนี้ขอเพียงแค่ห่อด้วยชิ้นเล็ก ๆ ของรหัสที่นี่เพื่อสรุปทุกอย่างข้างต้น เราจะใช้instanceofประกอบการนี่เพื่อตรวจสอบว่าobjectมีอยู่ในของprototypeห่วงโซ่prototypeทรัพย์สินของconstructorซึ่งล่างสรุปแผนภาพใหญ่ทั้งหมด

ป้อนคำอธิบายรูปภาพที่นี่

ฉันหวังว่าข้อมูลนี้จะเพิ่มบางอย่างฉันรู้ว่านี่อาจจะเป็นเรื่องใหญ่ที่จะเข้าใจ ... ในคำง่ายๆมันเป็นเพียงวัตถุที่เชื่อมโยงกับวัตถุ !!!!


22

จุดประสงค์ที่แท้จริงของคุณสมบัติ ".prototype" นี้คืออะไร

ส่วนต่อประสานกับคลาสมาตรฐานสามารถขยายได้ ตัวอย่างเช่นคุณกำลังใช้Arrayคลาสและคุณต้องเพิ่ม serializer ที่กำหนดเองสำหรับวัตถุอาร์เรย์ทั้งหมดของคุณ คุณจะใช้เวลาในการเขียนรหัสคลาสย่อยหรือใช้การแต่งหรือ ... คุณสมบัติต้นแบบแก้ปัญหานี้โดยให้ผู้ใช้ควบคุมชุดสมาชิก / วิธีการที่แน่นอนที่มีให้กับคลาส

คิดว่าต้นแบบเป็นตัวชี้ vtable พิเศษ เมื่อสมาชิกบางคนหายไปจากคลาสเดิมต้นแบบจะค้นหาที่รันไทม์


21

มันอาจช่วยในการจัดหมวดหมู่โซ่ต้นแบบเป็นสองประเภท

พิจารณาตัวสร้าง:

 function Person() {}

ค่าของObject.getPrototypeOf(Person)คือฟังก์ชั่น Function.prototypeในความเป็นจริงมันเป็น นับตั้งแต่Personถูกสร้างขึ้นเป็นฟังก์ชั่นมันแบ่งปันวัตถุฟังก์ชั่นต้นแบบเดียวกันกับที่ฟังก์ชั่นทั้งหมดมี มันเหมือนกับPerson.__proto__แต่ไม่ควรใช้คุณสมบัตินั้น อย่างไรก็ตามด้วยObject.getPrototypeOf(Person)คุณเดินขึ้นบันไดอย่างมีประสิทธิภาพในสิ่งที่เรียกว่าโซ่ต้นแบบ

ห่วงโซ่ในทิศทางขึ้นเป็นดังนี้:

    Person→การFunction.prototype→การObject.prototype(จุดสิ้นสุด)

ที่สำคัญคือห่วงโซ่ต้นแบบนี้มีน้อยจะทำอย่างไรกับวัตถุที่Personสามารถสร้าง วัตถุที่สร้างขึ้นเหล่านั้นมีโซ่ต้นแบบของตนเองและโซ่นี้อาจไม่มีบรรพบุรุษที่ใกล้เคียงกับสิ่งที่กล่าวมาข้างต้น

ยกตัวอย่างวัตถุนี้:

var p = new Person();

Pไม่มีความสัมพันธ์โดยตรงต้นแบบโซ่กับบุคคล ความสัมพันธ์ของพวกเขาแตกต่างกัน Object pมีเชนต้นแบบของตัวเอง ใช้Object.getPrototypeOfคุณจะพบโซ่เป็นดังนี้:

    p→การPerson.prototype→การObject.prototype(จุดสิ้นสุด)

ไม่มีฟังก์ชั่นวัตถุในห่วงโซ่นี้ (แม้ว่าอาจจะเป็น)

ดังนั้นPersonดูเหมือนว่าเกี่ยวข้องกับสองชนิดของโซ่ซึ่งมีชีวิตของตัวเอง ในการ "ข้าม" จากห่วงโซ่หนึ่งไปอีกโซ่คุณใช้:

  1. .prototype: กระโดดจากห่วงโซ่ของตัวสร้างไปยังห่วงโซ่ของวัตถุที่สร้าง คุณสมบัตินี้ถูกกำหนดไว้สำหรับวัตถุฟังก์ชันnewเท่านั้น(ตามที่สามารถใช้กับฟังก์ชันได้เท่านั้น)

  2. .constructor: กระโดดจากห่วงโซ่ของวัตถุที่สร้างไปยังห่วงโซ่ของตัวสร้าง

นี่คือการนำเสนอภาพของโซ่ต้นแบบทั้งสองที่เกี่ยวข้องซึ่งแสดงเป็นคอลัมน์:

ป้อนคำอธิบายรูปภาพที่นี่

เพื่อสรุป:

prototypeคุณสมบัติให้ข้อมูลของไม่มีเรื่องของห่วงโซ่ต้นแบบ แต่ของวัตถุที่สร้างขึ้นโดยเรื่อง

ไม่น่าแปลกใจที่ชื่อของสถานที่ให้บริการprototypeอาจนำไปสู่ความสับสน มันอาจจะชัดเจนกว่านี้ถ้าคุณสมบัตินี้ได้รับการตั้งชื่อprototypeOfConstructedInstancesหรือบางสิ่งบางอย่างตามสายนั้น

คุณสามารถข้ามไปมาระหว่างเชนต้นแบบสองอัน:

Person.prototype.constructor === Person

สมมาตรนี้สามารถถูกทำลายได้โดยการกำหนดวัตถุอื่นให้กับ prototypeทรัพย์สิน (เพิ่มเติมเกี่ยวกับเรื่องนั้นในภายหลัง)

สร้างฟังก์ชั่นหนึ่งรับสองวัตถุ

Person.prototypeเป็นวัตถุที่ถูกสร้างขึ้นในเวลาเดียวกันกับฟังก์ชั่นที่Personถูกสร้างขึ้น มันPersonเป็นตัวสร้างแม้ว่าตัวสร้างนั้นยังไม่ได้ดำเนินการจริง ดังนั้นสองวัตถุถูกสร้างขึ้นในเวลาเดียวกัน:

  1. ฟังก์ชั่นPersonนั้นเอง
  2. วัตถุที่จะทำหน้าที่เป็นต้นแบบเมื่อฟังก์ชั่นถูกเรียกว่าเป็นตัวสร้าง

ทั้งสองเป็นวัตถุ แต่มีบทบาทแตกต่างกัน: วัตถุฟังก์ชั่นสร้างในขณะที่วัตถุอื่นแสดงต้นแบบของวัตถุใด ๆ ที่ฟังก์ชั่นจะสร้าง วัตถุต้นแบบจะกลายเป็นแม่ของวัตถุที่สร้างขึ้นในห่วงโซ่ต้นแบบของมัน

เนื่องจากฟังก์ชั่นยังเป็นวัตถุมันยังมีพาเรนต์ของตัวเองในห่วงโซ่ต้นแบบของตัวเอง แต่จำได้ว่าทั้งสองโซ่มีความแตกต่างกัน

นี่คือความเท่าเทียมกันบางประการที่จะช่วยให้เข้าใจปัญหา - สิ่งเหล่านี้ทั้งหมด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);

การเพิ่มระดับให้กับห่วงโซ่ต้นแบบ

แม้ว่าวัตถุต้นแบบจะถูกสร้างขึ้นเมื่อคุณสร้างฟังก์ชั่นคอนสตรัคเตอร์คุณสามารถละเว้นวัตถุนั้นและกำหนดวัตถุอื่นที่ควรใช้เป็นต้นแบบสำหรับอินสแตนซ์ที่ตามมาใด ๆ ที่สร้างโดยนวกรรมิกนั้น

ตัวอย่างเช่น

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

ตอนนี้ลูกโซ่ต้นแบบของtยาวกว่าขั้นตอนหนึ่งของp :

    t→การp→การPerson.prototype→การObject.prototype(จุดสิ้นสุด)

โซ่ต้นแบบอื่นไม่ได้อีกต่อไปThiefและPersonเป็นพี่น้องร่วมกันผู้ปกครองเดียวกันในห่วงโซ่ต้นแบบของพวกเขา:

    Person}
    Thief  } →การFunction.prototype→การObject.prototype(จุดสิ้นสุด)

กราฟิกที่นำเสนอก่อนหน้านี้สามารถขยายไปถึงสิ่งนี้ได้ (ต้นฉบับThief.prototypeถูกทิ้งไว้):

ป้อนคำอธิบายรูปภาพที่นี่

เส้นสีฟ้าแสดงถึงโซ่ต้นแบบเส้นสีอื่น ๆ แสดงถึงความสัมพันธ์อื่น ๆ :

  • ระหว่างวัตถุและตัวสร้าง
  • ระหว่างตัวสร้างและวัตถุต้นแบบที่จะใช้สำหรับการสร้างวัตถุ

18

คู่มือที่ชัดเจนสำหรับ JavaScript เชิงวัตถุ - คำอธิบายวิดีโอที่กระชับและชัดเจน ~ 30 นาทีสำหรับคำถามที่ถาม (หัวข้อ Prototypal Inheritance เริ่มตั้งแต่5:45ถึงแม้ว่าฉันอยากจะฟังทั้งวิดีโอ) ผู้เขียนของวิดีโอนี้ยังทำ JavaScript เว็บไซต์ Visualizer วัตถุhttp://www.objectplayground.com/ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


1
การอ้างอิงวิดีโอที่ยอดเยี่ยม
lukeaus

16

ฉันพบว่าเป็นประโยชน์ในการอธิบาย "ต้นแบบลูกโซ่" เป็นการประชุมแบบเรียกซ้ำเมื่อobj_n.prop_Xถูกอ้างอิง:

หากobj_n.prop_Xไม่มีอยู่ให้ตรวจสอบobj_n+1.prop_Xที่obj_n+1 = obj_n.[[prototype]]

หากprop_Xพบในที่สุดในวัตถุต้นแบบ k-th แล้ว

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

คุณสามารถค้นหากราฟของความสัมพันธ์ของวัตถุ Javascript ตามคุณสมบัติได้ที่นี่:

กราฟวัตถุ js

http://jsobjects.org


14

เมื่อตัวสร้างสร้างวัตถุวัตถุนั้นจะอ้างอิงคุณสมบัติ "ต้นแบบ" ของตัวสร้างโดยปริยายเพื่อวัตถุประสงค์ในการแก้ไขการอ้างอิงคุณสมบัติ คุณสมบัติ“ ต้นแบบ” ของคอนสตรัคเตอร์สามารถอ้างอิงได้โดยนิพจน์โปรแกรม constructor.prototype และคุณสมบัติที่เพิ่มให้กับต้นแบบของออบเจ็กต์ถูกแชร์ผ่านการสืบทอดโดยออบเจ็กต์ทั้งหมดที่แชร์ต้นแบบ


11

ที่นี่มีสองสิ่งที่แตกต่าง แต่เกี่ยวข้องกันที่ต้องอธิบาย:

  • .prototypeทรัพย์สินของฟังก์ชั่น
  • [[Prototype]][1]คุณสมบัติของวัตถุทั้งหมด[2]

นี่เป็นสองสิ่งที่แตกต่างกัน

[[Prototype]]ทรัพย์สิน:

นี่คือคุณสมบัติที่มีอยู่ในวัตถุ[2]ทั้งหมด

สิ่งที่เก็บไว้ที่นี่คือวัตถุอื่นซึ่งในฐานะวัตถุมีวัตถุ[[Prototype]]ของตนเองที่ชี้ไปยังวัตถุอื่น วัตถุอื่นนั้นมี[[Prototype]]ของตัวเอง เรื่องนี้ดำเนินต่อไปจนกว่าคุณจะไปถึงวัตถุต้นแบบที่มีวิธีการที่สามารถเข้าถึงได้บนวัตถุทั้งหมด (เช่น.toString)

[[Prototype]]คุณสมบัติเป็นส่วนหนึ่งของสิ่งที่รูปแบบ[[Prototype]]ห่วงโซ่ สายโซ่ของ[[Prototype]]วัตถุนี้เป็นสิ่งที่ตรวจสอบเมื่อตัวอย่างเช่น[[Get]]หรือ[[Set]]การดำเนินการจะดำเนินการกับวัตถุ:

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

.prototypeทรัพย์สิน:

นี่คือคุณสมบัติที่พบได้เฉพาะในฟังก์ชั่น ใช้ฟังก์ชั่นที่ง่ายมาก:

function Bar(){};

.prototypeคุณสมบัติถือวัตถุที่จะได้รับมอบหมายให้เมื่อคุณทำb.[[Prototype]] var b = new Barคุณสามารถตรวจสอบสิ่งนี้ได้อย่างง่ายดาย:

// 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

หนึ่งในสิ่งที่สำคัญที่สุด.prototypeคือว่าของObjectฟังก์ชั่น ต้นแบบนี้มีวัตถุต้นแบบที่[[Prototype]]โซ่ทั้งหมดมี ในนั้นมันมีการกำหนดวิธีการที่ใช้ได้ทั้งหมดสำหรับวัตถุใหม่:

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

ตอนนี้เนื่องจาก.prototypeเป็นวัตถุจึงมี[[Prototype]]คุณสมบัติ เมื่อคุณไม่ได้ทำให้ได้รับมอบหมายใด ๆ เพื่อFunction.prototypeที่.prototype's [[Prototype]]จุดไปยังวัตถุแม่บท (Object.prototype ) สิ่งนี้จะดำเนินการโดยอัตโนมัติทุกครั้งที่คุณสร้างฟังก์ชั่นใหม่

ด้วยวิธีนี้ทุกครั้งที่คุณทำnew Bar;เชนต้นแบบนั้นคุณจะได้รับทุกสิ่งที่กำหนดไว้Bar.prototypeและทุกสิ่งที่กำหนดไว้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)

เมื่อคุณทำที่ได้รับมอบหมายให้ไปFunction.prototypeสิ่งที่คุณกำลังทำอยู่คือการขยายห่วงโซ่ต้นแบบที่จะรวมถึงวัตถุอื่น มันเหมือนกับการแทรกในรายการที่เชื่อมโยงโดยลำพัง

สิ่งนี้จะเปลี่ยนแปลง[[Prototype]]ห่วงโซ่การอนุญาตให้คุณสมบัติที่กำหนดไว้ในวัตถุที่กำหนดให้Function.prototypeมองเห็นได้โดยวัตถุใด ๆ ที่สร้างขึ้นโดยฟังก์ชั่น


[1: นั่นจะไม่ทำให้ใครสับสน ทำให้สามารถใช้งานผ่านทางคุณสมบัติในการใช้งานจำนวนมาก __proto__
[2]: nullทั้งหมดยกเว้น


10

ให้ฉันบอกความเข้าใจของฉันเกี่ยวกับต้นแบบ ฉันจะไม่เปรียบเทียบมรดกที่นี่กับภาษาอื่น ฉันหวังว่าผู้คนจะหยุดการเปรียบเทียบภาษาและเพียงแค่เข้าใจภาษาเป็นของตัวเอง การทำความเข้าใจกับต้นแบบและการสืบทอดมรดกนั้นง่ายมากอย่างที่ฉันจะแสดงให้คุณเห็นด้านล่าง

ต้นแบบเป็นเหมือนรูปแบบที่คุณสร้างผลิตภัณฑ์ขึ้นอยู่กับ จุดสำคัญในการทำความเข้าใจคือเมื่อคุณสร้างวัตถุโดยใช้วัตถุอื่นเป็นต้นแบบการเชื่อมโยงระหว่างต้นแบบและผลิตภัณฑ์จะยั่งยืน ตัวอย่างเช่น

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

วัตถุทุกชิ้นมีคุณสมบัติภายในที่เรียกว่า [[ต้น]] ซึ่งสามารถเข้าถึงได้โดยObject.getPrototypeOf()ฟังก์ชั่น Object.create(model)สร้างวัตถุใหม่และการตั้งค่าของมัน [[ต้นแบบ]] คุณสมบัติเพื่อวัตถุรูปแบบ ดังนั้นเมื่อคุณทำObject.getPrototypeOf(product)คุณจะได้รับวัตถุรูปแบบ

คุณสมบัติในผลิตภัณฑ์ได้รับการจัดการด้วยวิธีดังต่อไปนี้:

  • เมื่อมีการเข้าถึงพร็อพเพอร์ตี้เพื่ออ่านค่าของคุณสมบัตินั้นจะค้นหาในห่วงโซ่ขอบเขต การค้นหาตัวแปรเริ่มต้นจากผลิตภัณฑ์ขึ้นไปจนถึงต้นแบบของมัน หากพบตัวแปรดังกล่าวในการค้นหาการค้นหาจะหยุดอยู่ตรงนั้นและส่งคืนค่า หากไม่พบตัวแปรดังกล่าวในห่วงโซ่ขอบเขตระบบจะไม่ส่งคืนค่าที่กำหนดไว้
  • เมื่อคุณสมบัติถูกเขียน (แก้ไข) ดังนั้นคุณสมบัติจะถูกเขียนบนวัตถุผลิตภัณฑ์เสมอ หากผลิตภัณฑ์ไม่ได้มีคุณสมบัติดังกล่าวแล้วมันจะถูกสร้างและเขียนโดยปริยาย

เช่นการเชื่อมโยงของวัตถุโดยใช้คุณสมบัติต้นแบบเรียกว่าการสืบทอดต้นแบบ ที่นั่นมันง่ายมากเห็นด้วย?


ไม่ได้เขียนเกี่ยวกับผลิตภัณฑ์ตามที่ได้รับมอบหมายเสมอไป คุณไม่ได้อธิบายอย่างชัดเจนว่าสมาชิกบางคนจะต้องเริ่มต้นและสมาชิกที่ใช้ร่วมกันสามารถทำงานต้นแบบได้ โดยเฉพาะอย่างยิ่งเมื่อคุณมีสมาชิกที่ไม่แน่นอนที่เฉพาะเจาะจงอินสแตนซ์: stackoverflow.com/questions/16063394/…
HMR

HMR: ในตัวอย่างของคุณในคำตอบของคุณ ben.food.push ("แฮมเบอร์เกอร์"); line เปลี่ยนแปลงคุณสมบัติของวัตถุต้นแบบเนื่องจากสิ่งต่อไปนี้: 1. ) ben.food แรกถูกค้นหาและการดำเนินการค้นหาใด ๆ ก็จะค้นหาห่วงโซ่ขอบเขต 2. ) ฟังก์ชัน push ของวัตถุ ben.food นั้นจะถูกดำเนินการ โดยการเขียนโหมดในคำตอบของฉันฉันหมายถึงเมื่อคุณกำหนดค่าให้กับมันอย่างชัดเจนเช่นใน: ben.food = ['Idly']; สิ่งนี้จะสร้างคุณสมบัติใหม่ (หากยังไม่มี) บนวัตถุผลิตภัณฑ์แล้วกำหนดค่าให้กับมัน
Aravind

HMR: ขอบคุณสำหรับความคิดเห็นของคุณมันทำให้ฉันคิดและทดสอบความเข้าใจของฉัน
Aravind

เมื่อทำการกำหนด ben.food ใหม่จะทำให้เงาสมาชิกอาหารยกเว้นอาหารที่สร้างขึ้นโดยใช้ Object.defineProperty, Object.defineProperties หรือ Object.create ด้วยอาร์กิวเมนต์ที่สอง (ไม่ใช่ทุกครั้ง) คุณสามารถเปลี่ยนต้นแบบด้วยการกำหนดค่าใหม่ (สิ่งที่ดูเหมือน) เมื่อคุณสร้างตัวตั้งค่า getter เมื่อพูดถึงรูปแบบของการสืบทอดฉันเข้าใจว่าตัวสร้างฟังก์ชั่นนั้นยากที่จะเข้าใจและมีปัญหาที่สำคัญบางอย่าง แต่ก็ดีถ้าคุณเข้าใจ การสืบทอดใน JavaScript ไม่ได้เริ่มต้นและจบลงด้วยการตั้งค่าต้นแบบเริ่มต้น (ตัวสร้าง) จะต้องใช้อีกครั้ง
HMR

คำตอบของคุณดีในการอธิบายต้นแบบ แต่อาจตีความผิดโดยเข้าใจง่ายกว่าการสืบทอดใน JavaScript และสมาชิกเฉพาะอินสแตนซ์ มีคำถามมากมายที่ถามว่าทำไมการกลายสมาชิกต้นแบบในอินสแตนซ์จึงส่งผลกระทบต่ออินสแตนซ์อื่น
HMR


10

พิจารณาkeyValueStoreวัตถุต่อไปนี้:

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; }
    };
})();

ฉันสามารถสร้างตัวอย่างใหม่ของวัตถุนี้โดยทำสิ่งนี้:

kvs = keyValueStore.create();

แต่ละอินสแตนซ์ของวัตถุนี้จะมีคุณสมบัติสาธารณะดังต่อไปนี้:

  • data
  • get
  • set
  • delete
  • getLength

ทีนี้สมมติว่าเราสร้างkeyValueStoreวัตถุนี้ 100 อินสแตนซ์ แม้ว่าget, set, delete, getLengthจะทำสิ่งเดียวที่แน่นอนสำหรับแต่ละเหล่านี้ 100 กรณีทุกกรณีมีสำเนาของตัวเองของฟังก์ชั่นนี้

ตอนนี้คิดว่าคุณอาจจะมีเพียงหนึ่งเดียวget, set, deleteและgetLengthคัดลอกและแต่ละกรณีจะอ้างอิงว่าฟังก์ชั่นเดียวกัน สิ่งนี้จะดีกว่าสำหรับประสิทธิภาพและต้องการหน่วยความจำน้อย

นั่นคือสิ่งที่ต้นแบบเข้ามาต้นแบบคือ "พิมพ์เขียว" ของคุณสมบัติที่สืบทอด แต่ไม่ได้คัดลอกโดยอินสแตนซ์ ดังนั้นนี่หมายความว่ามันมีอยู่เพียงครั้งเดียวในหน่วยความจำสำหรับทุกอินสแตนซ์ของวัตถุและใช้ร่วมกันโดยอินสแตนซ์เหล่านั้นทั้งหมด

ตอนนี้ให้พิจารณาkeyValueStoreวัตถุอีกครั้ง ฉันเขียนมันใหม่ได้เช่นนี้

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; }
    };
})();

สิ่งนี้จะเหมือนกับkeyValueStoreวัตถุรุ่นก่อนหน้ายกเว้นว่าวิธีการทั้งหมดจะถูกวางในต้นแบบทันที สิ่งนี้หมายความว่าอินสแตนซ์ทั้งหมด 100 ตอนนี้ใช้วิธีการเหล่านี้ร่วมกันสี่วิธีแทนที่จะแต่ละวิธีมีสำเนาของตนเอง


9

สรุป:

  • ฟังก์ชั่นเป็นวัตถุใน javascript จึงสามารถมีคุณสมบัติ
  • ฟังก์ชัน (Constructor) มีคุณสมบัติต้นแบบเสมอ
  • เมื่อฟังก์ชั่นถูกใช้เป็นตัวสร้างที่มีnewคำหลักวัตถุที่ได้รับต้นแบบ การอ้างอิงถึงต้นแบบนี้สามารถพบได้ใน__proto__คุณสมบัติของวัตถุที่สร้างขึ้นใหม่
  • __proto__คุณสมบัตินี้อ้างถึงprototypeคุณสมบัติของฟังก์ชันตัวสร้าง

ตัวอย่าง:

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

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

เหตุใดจึงมีประโยชน์นี้:

Javascript มีกลไกเมื่อค้นหาคุณสมบัติของ Object ซึ่งเรียกว่า'prototypal inheritance'นี่คือสิ่งที่ทำกันโดยทั่วไป:

  • ขั้นแรกจะมีการตรวจสอบว่าสถานที่ให้บริการตั้งอยู่บนวัตถุเอง ถ้าเป็นเช่นนั้นคุณสมบัตินี้จะถูกส่งกลับ
  • ถ้าคุณสมบัติไม่ได้อยู่บนวัตถุนั้นมันจะ 'ไต่ขึ้น Protochain' โดยทั่วไปแล้วจะดูที่วัตถุที่ถูกอ้างถึงโดยคุณสมบัติโปรโต ที่นั่นมันตรวจสอบว่าทรัพย์สินที่มีอยู่บนวัตถุที่อ้างถึงโดยโปรโต
  • ถ้าคุณสมบัติไม่ได้อยู่บนวัตถุโปรโตมันก็จะปีนขึ้นโซ่โปรโตไปจนถึงวัตถุ
  • หากไม่สามารถหาคุณสมบัติได้ที่นี่ในวัตถุและเชนต้นแบบมันจะส่งคืนไม่ได้กำหนด

ตัวอย่างเช่น:

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

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);

ปรับปรุง:

__proto__คุณสมบัติได้รับการคัดค้านแม้ว่ามันจะดำเนินการในเบราว์เซอร์ที่ทันสมัยที่สุดวิธีที่ดีกว่าที่จะได้รับการอ้างอิงต้นแบบวัตถุจะเป็น:

Object.getPrototypeOf()


7

ฉันมักจะชอบอุปมาอุปมัยเมื่อมันมาทำความเข้าใจกับสิ่งประเภทนี้ 'การถ่ายทอดทางพันธุกรรมของต้นแบบ' ค่อนข้างสับสนเมื่อเปรียบเทียบกับการสืบทอดคลาสเบสในความคิดของฉันแม้ว่าต้นแบบจะเป็นกระบวนทัศน์ที่เรียบง่ายกว่ามาก ในความเป็นจริงกับต้นแบบไม่มีการสืบทอดดังนั้นชื่อในและของตัวเองทำให้เข้าใจผิดมันเป็นประเภทของ 'การมอบหมาย' มากขึ้น

ลองนึกภาพสิ่งนี้ ....

คุณอยู่ในโรงเรียนมัธยมและอยู่ในชั้นเรียนและมีแบบทดสอบที่ถึงกำหนดในวันนี้ แต่คุณไม่มีปากกาที่จะตอบคำถามของคุณ Doh!

คุณกำลังนั่งถัดจาก Finnius เพื่อนของคุณซึ่งอาจมีปากกา คุณถามและเขามองไปรอบ ๆ โต๊ะของเขาไม่สำเร็จ แต่แทนที่จะพูดว่า "ฉันไม่มีปากกา" เขาเป็นเพื่อนที่ดีที่เขาตรวจสอบกับ Derp เพื่อนคนอื่นของเขาถ้าเขามีปากกา Derp มีปากกาสำรองและส่งคืนให้ Finnius ผู้ส่งผ่านมันไปให้คุณเพื่อทำแบบทดสอบ Derp มอบหมายให้ปากกาแก่ Finnius ซึ่งมอบหมายให้คุณใช้ปากกา

สิ่งสำคัญคือ Derp ไม่ได้มอบปากกาให้คุณเนื่องจากคุณไม่มีความสัมพันธ์โดยตรงกับเขา

นี่เป็นตัวอย่างที่ง่ายของวิธีการทำงานของต้นแบบซึ่งมีการค้นหาแผนผังข้อมูลสำหรับสิ่งที่คุณกำลังมองหา


3

รูปแบบอื่นที่แสดง__proto__ , ต้นแบบและตัวสร้างความสัมพันธ์: ป้อนคำอธิบายรูปภาพที่นี่



1

สิ่งสำคัญคือต้องเข้าใจว่ามีความแตกต่างระหว่างต้นแบบของวัตถุ (ซึ่งมีให้ผ่านObject.getPrototypeOf(obj)หรือผ่าน__proto__คุณสมบัติที่เลิกใช้แล้ว) และprototypeคุณสมบัติของฟังก์ชันตัวสร้าง อดีตคือคุณสมบัติในแต่ละอินสแตนซ์และหลังเป็นคุณสมบัติในตัวสร้าง นั่นคือหมายถึงวัตถุเช่นเดียวกับObject.getPrototypeOf(new Foobar())Foobar.prototype

การอ้างอิง: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes


0

ต้นแบบสร้างวัตถุใหม่โดยโคลนที่มีอยู่วัตถุ ดังนั้นเมื่อเราคิดถึงต้นแบบเราสามารถคิดถึงการโคลนนิ่งหรือทำสำเนาบางสิ่งแทนการสร้างมันขึ้นมา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.