__proto__ VS. ต้นแบบใน JavaScript


785

รูปนี้แสดงให้เห็นอีกครั้งว่าทุกวัตถุมีต้นแบบ ฟังก์ชั่นตัวสร้างฟูยังมีของตัวเอง__proto__ซึ่งเป็น Function.prototype และในทางกลับกันยังอ้างอิงผ่าน__proto__คุณสมบัติของมันอีกครั้งเพื่อ Object.prototype ดังนั้นซ้ำ Foo.prototype เป็นเพียงคุณสมบัติที่ชัดเจนของ Foo ซึ่งหมายถึงต้นแบบของวัตถุ b และ c

var b = new Foo(20);
var c = new Foo(30);

อะไรคือความแตกต่างระหว่าง__proto__และprototype?

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

รูปที่ถูกนำมาจากdmitrysoshnikov.com



5
ฉันคิดว่าจากบนลงล่างหรือจากล่างขึ้นบนเป็นเรื่องของการตั้งค่า ฉันชอบวิธีนี้มากกว่าดังนั้นฉันสามารถติดตามไดอะแกรมจนกระทั่งพบว่ามีอะไรมาจากไหน
Mike Lippert

1
ฉันชอบวิธีที่ JavaScript ใช้การสืบทอดต้นแบบเพื่อแก้ไข y.constructor เป็น y .__ proto __. constructor ฉันชอบวิธีที่ Object.prototype ตั้งอยู่ที่ด้านบนสุดของเชนการสืบทอดต้นแบบด้วย Object.prototype .__ proto__ ตั้งค่าเป็น null ฉันชอบวิธีที่ไดอะแกรมทำให้การสร้างภาพสามคอลัมน์เป็นแนวคิดของการที่โปรแกรมเมอร์คิดว่าวัตถุเป็น 1 อินสแตนซ์ 2. คอนสตรัคเตอร์ 3. ต้นแบบที่คอนสตรัคเตอร์เชื่อมโยงกับอินสแตนซ์เหล่านั้นเมื่ออินสแตนซ์ผ่านคำหลักใหม่
John Sonderson

ไดอะแกรมทำให้รู้สึกทันทีหลังจากที่คุณดูบางสิ่งบางอย่างเช่นyoutube.com/watch?v=_JJgSbuj5VI , btw
mlvljr

และตอนนี้ที่ผมได้อ่านคำตอบที่รู้สึกเลยจริงๆแนะนำวิดีโอดังกล่าวข้างต้นในขณะที่มันแน่นอนมีผลึกสะอาด (และไม่ใช่ WTFy) คำอธิบายของสิ่งที่เกิดขึ้น :)
mlvljr

คำตอบ:


765

__proto__เป็นวัตถุจริงที่ใช้ในสายการค้นหาเพื่อแก้ไขวิธีการ ฯลฯ prototypeเป็นวัตถุที่ใช้สร้าง__proto__เมื่อคุณสร้างวัตถุด้วยnew:

( new Foo ).__proto__ === Foo.prototype;
( new Foo ).prototype === undefined;

239
อา! ดังนั้นจึงprototypeไม่สามารถใช้ได้กับอินสแตนซ์ของตัวเอง (หรือวัตถุอื่น ๆ ) แต่เฉพาะในฟังก์ชั่นคอนสตรัคเตอร์
rvighne

43
@rvighne: prototypeจะใช้ได้เฉพาะในฟังก์ชั่นตั้งแต่ที่พวกเขาจะได้มาจากFunction, FunctionและObjectแต่ในสิ่งอื่น ๆ มันไม่ได้เป็น อย่างไรก็ตาม__proto__สามารถใช้ได้ทุกที่
Tarik

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

9
มันยุติธรรมที่จะบอกว่า__proto__คุณสมบัติของวัตถุเป็นตัวชี้ไปยังprototypeคุณสมบัติของฟังก์ชั่นการสร้างของวัตถุ? ie foo .__ proto__ === foo.constructor.prototype
Niko Bellic

10
@Alex_Nabu ไม่มาก newCar.__proto__ เป็น ไม่เป็นตัวอย่างของCar.prototype Car.prototypeในขณะที่Car.protoype เป็นobjectตัวอย่างของ Car.prototypeไม่ได้เป็นสิ่งที่ช่วยให้newCarทรัพย์สินหรือโครงสร้างใด ๆ มันก็ISถัดไปobjectในnewCar'ห่วงโซ่ต้นแบบ ไม่ได้เป็นชั่วคราวCar.prototype objectมันเป็นobjectที่ตั้งเป็นค่าของ__proto__ทรัพย์สินของใหม่ ๆobjects ทำโดยใช้เป็นCar constructorหากคุณต้องการที่จะคิดอะไรที่เป็นพิมพ์เขียวobjectคิดว่าCarเป็นพิมพ์เขียวสำหรับ car- ใหม่objects
seangwright

335

prototypeเป็นคุณสมบัติของวัตถุฟังก์ชั่น มันเป็นต้นแบบของวัตถุที่สร้างขึ้นโดยฟังก์ชั่นนั้น

__proto__เป็นคุณสมบัติภายในของวัตถุโดยชี้ไปที่ต้นแบบ มาตรฐานในปัจจุบันให้Object.getPrototypeOf(O)วิธีการที่เทียบเท่าแม้ว่ามาตรฐานโดยพฤตินัย__proto__นั้นเร็วกว่า

คุณสามารถค้นหาinstanceofความสัมพันธ์โดยการเปรียบเทียบฟังก์ชั่นของprototypeการวัตถุห่วงโซ่และคุณสามารถทำลายความสัมพันธ์เหล่านี้โดยการเปลี่ยน__proto__prototype

function Point(x, y) {
    this.x = x;
    this.y = y;
}

var myPoint = new Point();

// the following are all true
myPoint.__proto__ == Point.prototype
myPoint.__proto__.__proto__ == Object.prototype
myPoint instanceof Point;
myPoint instanceof Object;

นี่Pointคือฟังก์ชั่นคอนสตรัคเตอร์มันสร้างวัตถุ (โครงสร้างข้อมูล) ขั้นตอน myPointเป็นวัตถุที่สร้างขึ้นโดยPoint()เพื่อให้Point.prototypeได้รับการบันทึกไว้ในmyPoint.__proto__ช่วงเวลานั้น


2
นอกจากนี้ถ้าคุณเปลี่ยน__proto__คุณสมบัติของวัตถุมันจะเปลี่ยนวัตถุที่จะทำการค้นหาต้นแบบ ตัวอย่างเช่นคุณสามารถเพิ่มวัตถุของวิธีการเป็นฟังก์ชั่นที่__proto__จะมีประเภทของวัตถุเช่น callable
kzh

myPoint .__ proto __. constructor.prototype == Point.prototype
Francisco

@ kzh lol ที่ให้ผลลัพธ์แบบตลกconsole.log(obj1.call) // [Function: call] obj1.call()// TypeError: obj1.call ไม่ใช่ฟังก์ชั่น ฉันทำobj.__proto__ = Function.__proto__
abhisekp

myFn.__proto__ = {foo: 'bar'}
kzh

ฉันคิดว่าฉันได้รับคะแนนของคุณแล้ว
ComicScrip

120

คุณสมบัติต้นแบบถูกสร้างขึ้นเมื่อมีการประกาศฟังก์ชัน

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

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

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

// adds a new method age to the Person.prototype Object.
Person.prototype.age = function(){return date-dob}; 

เป็นที่น่าสังเกตว่าPerson.prototypeเป็นObjectตัวอักษรตามค่าเริ่มต้น (สามารถเปลี่ยนแปลงได้ตามต้องการ)

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

var person1 = new Person(somedate);
var person2 = new Person(somedate);

สร้าง 2 กรณีของPersonเหล่านี้ 2 วัตถุที่สามารถเรียกageวิธีการของการPerson.prototypeเป็น,person1.ageperson2.age

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

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

__proto__ไม่ได้เป็นวิธีมาตรฐานในการเข้าถึงห่วงโซ่ต้นแบบวิธีมาตรฐาน Object.getPrototypeOf(obj)แต่ที่คล้ายกันคือการใช้งาน

โค้ดด้านล่างสำหรับinstanceofโอเปอร์เรเตอร์ช่วยให้เข้าใจได้ดีขึ้น:

instanceofผู้ประกอบการระดับวัตถุกลับมาtrueเมื่อวัตถุเป็นตัวอย่างของคลาสที่เฉพาะเจาะจงมากขึ้นหากClass.prototypeพบในห่วงโซ่โปรโตของวัตถุนั้นวัตถุนั้นเป็นตัวอย่างของระดับที่

function instanceOf(Func){
  var obj = this;
  while(obj !== null){
    if(Object.getPrototypeOf(obj) === Func.prototype)
      return true;
    obj = Object.getPrototypeOf(obj);
  }
  return false;
}      

วิธีการดังกล่าวสามารถเรียกว่าเป็น: instanceOf.call(object, Class)ซึ่งผลตอบแทนจริงถ้าวัตถุเป็นตัวอย่างของการเรียน


2
ฉันสงสัยว่าทำไมprototypeวัตถุถึงถูกสร้างขึ้นภายในตั้งแต่แรก? หนึ่งสามารถกำหนดวิธีการคงที่เพียงแค่วัตถุฟังก์ชั่น เช่นfunction f(a){this.a = a}; f.increment = function(){return ++this.a}? เหตุใดจึงไม่เลือกวิธีนี้ในการเพิ่มวิธีการprototypeคัดค้าน สิ่งนี้จะใช้ได้ถ้าf.__proto__ = gตำแหน่ง g เป็นคลาสพื้นฐาน
abhisekp

อาจprototypeเลือกที่จะใช้ออบเจ็กต์ร่วมกันได้เนื่องจากคุณสมบัติตัวสร้างฟังก์ชั่นพิเศษเท่านั้นที่สามารถเก็บไว้ในวัตถุตัวสร้างฟังก์ชัน
abhisekp

1
ที่จริงแล้วมันจะเลอะเพราะinstanceofจะส่งผล({}) instanceof Function === trueให้ไม่มีความแตกต่างระหว่างต้นแบบถ้าprototypeคุณสมบัติถูกลบออก
abhisekp

@abhisekp คุณหมายถึงอะไร: "สิ่งนี้จะใช้ได้ถ้า f .__ proto__ = g โดยที่ g คือคลาสฐาน" ฉันไม่ทราบว่าสิ่งนี้มีความหมายบางอย่างที่ฉันไม่เข้าใจ แต่ถ้าคุณเพิ่มคุณสมบัติและวิธีการในลักษณะนั้นเมื่อคุณใช้newคำหลักเพื่อสร้างอินสแตนซ์คุณสมบัติและวิธีการจะไม่ถูกคัดลอก เกิน.
doubleOrt

66

วิธีคิดที่ดีคือ ...

prototypeถูกใช้โดยconstructor()ฟังก์ชั่น มันควรจะถูกเรียกว่าอะไรบางอย่างเหมือน"prototypeToInstall"จริงเพราะนั่นคือสิ่งที่มันเป็น

และ__proto__เป็นที่ "ต้นแบบที่ติดตั้ง" บนวัตถุ (ที่ถูกสร้าง / ติดตั้งบนวัตถุจากconstructor()ฟังก์ชั่นดังกล่าว)


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

2
โปรดเปลี่ยน " constructor()ฟังก์ชั่น" เป็น "ฟังก์ชั่นคอนสตรัคเตอร์" เนื่องจากอาจมีความสับสนกับ " __proto__.constructor()ฟังก์ชั่น" ฉันคิดว่าสิ่งนี้สำคัญเนื่องจาก __proto __. คอนสตรัคเตอร์ไม่ได้ถูกเรียกจริงเมื่อใช้newคำหลัก
Alexander Gonchiy

1
คำสั่งที่ว่า "ตัวต้นแบบถูกใช้โดย constructor () ฟังก์ชั่น " บอกเพียงส่วนหนึ่งของความจริงที่สำคัญ แต่บอกว่าในทางที่มีแนวโน้มที่จะทำให้ผู้อ่านคิดว่ามันเป็นความจริงทั้งหมด ต้นแบบถูกสร้างขึ้นภายในสำหรับทุกการประกาศฟังก์ชั่นใน Javascript โดยไม่คำนึงถึงวิธีการที่จะเรียกใช้ฟังก์ชั่นในอนาคต - มีหรือไม่มีคำหลักใหม่ ; ต้นแบบของฟังก์ชั่นที่ประกาศไปยังวัตถุตามตัวอักษร
Yiling

62

เพื่ออธิบายให้เราสร้างฟังก์ชั่น

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

เมื่อ JavaScript รันรหัสนี้จะเพิ่มprototypeคุณสมบัติการa, prototypeคุณสมบัติเป็นวัตถุที่มีสองคุณสมบัติมัน:

  1. constructor
  2. __proto__

ดังนั้นเมื่อเราทำ

a.prototype มันกลับมา

     constructor: a  // function definition
    __proto__: Object

อย่างที่คุณเห็นconstructorไม่มีอะไรนอกจากฟังก์ชั่นaและ__proto__ชี้ไปที่ระดับรูทObjectของ JavaScript

ให้เราดูว่าเกิดอะไรขึ้นเมื่อเราใช้aฟังก์ชั่นด้วยnewคำสำคัญ

var b = new a ('JavaScript');

เมื่อจาวาสคริปต์ดำเนินการรหัสนี้มันจะทำ 4 สิ่ง:

  1. มันสร้างวัตถุใหม่วัตถุที่ว่างเปล่า // {}
  2. มันจะสร้าง__proto__บนbและทำให้มันชี้ไปa.prototypeดังนั้นb.__proto__ === a.prototype
  3. มันประมวลผลa.prototype.constructor(ซึ่งเป็นคำจำกัดความของฟังก์ชั่นa) กับวัตถุที่สร้างขึ้นใหม่ (สร้างขึ้นในขั้นตอนที่ 1) เป็นบริบทของมัน (นี้) ดังนั้นnameทรัพย์สินที่ส่งผ่านเป็น 'JavaScript' (ซึ่งถูกเพิ่มเข้าไปthis) จะถูกเพิ่มเข้าไปในวัตถุที่สร้างขึ้นใหม่
  4. มันส่งคืนวัตถุที่สร้างขึ้นใหม่ใน (สร้างในขั้นตอน # 1) ดังนั้น var bได้รับมอบหมายให้กับวัตถุที่สร้างขึ้นใหม่

ตอนนี้ถ้าเราเพิ่มa.prototype.car = "BMW"และทำสิ่ง b.carที่ส่งออก "BMW" จะปรากฏขึ้น

นี่เป็นเพราะเมื่อ JavaScript เรียกใช้รหัสนี้มันค้นหาcarคุณสมบัติในbมันไม่พบ JavaScript ที่ใช้แล้วb.__proto__(ซึ่งทำเพื่อชี้ไปที่ 'a.prototype' ในขั้นตอนที่ 2) และค้นหาcarคุณสมบัติจึงส่งคืน "BMW"


2
1. constructorไม่กลับมาa()! มันกลับaมา 2. __proto__ส่งคืนObject.prototypeไม่ใช่วัตถุรูทใน Javascript
doubleOrt

1
นี่เป็นคำตอบที่ยอดเยี่ยม!
john-raymon

+1 นี่คือคำตอบที่ดีที่สุดสำหรับการอธิบายว่าต้นแบบตัวจริงคืออะไร (วัตถุที่มีคุณสมบัติสองอย่าง) และวิธีที่ Javascript เรียกใช้งานโค้ดแต่ละชิ้น ข้อมูลนี้ยากที่จะเกิดขึ้นอย่างน่าประหลาดใจ
java-addict301

53

ต้นแบบ VS __proto__ VS. [[Prototype]]

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

ตัวอย่าง:

function Foo () {
    this.name = 'John Doe';
}

// Foo has an object property called prototype.
// prototype was created automatically when we declared the function Foo.
Foo.hasOwnProperty('prototype'); // true

// Now, we can assign properties and methods to it:
Foo.prototype.myName = function () {
    return 'My name is ' + this.name;
}

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

var b = new Foo();

b.[[Prototype]] === Foo.prototype  // true


ส่วนตัว[[Prototype]]เชื่อมโยงไปยังวัตถุที่ฟังก์ชั่นที่เรียกว่าวงเล็บคู่ต้นแบบหรือเพียงแค่ เบราว์เซอร์จำนวนมากให้การเชื่อมโยงสาธารณะแก่เราที่เรียกว่า__proto__!

จะเจาะจงมากขึ้น__proto__จริง ๆ แล้วเป็นฟังก์ชัน getterที่เป็นของวัตถุ JavaScript ดั้งเดิม มันจะคืนค่าการเชื่อมโยงต้นแบบภายใน - ส่วนตัวของสิ่งที่thisมีผลผูกพัน (คืนค่า[[Prototype]]ของb):

b.__proto__ === Foo.prototype // true

เป็นที่น่าสังเกตว่าการเริ่มต้นECMAScript5คุณยังสามารถใช้วิธีgetPrototypeOfเพื่อรับการเชื่อมโยงส่วนตัวภายใน:

Object.getPrototypeOf(b) === b.__proto__ // true


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


2
@Taurus คลิกที่ส่วนหัวมันจะนำไปสู่เอกสารข้อกำหนด ECMAScript ตรวจสอบส่วนที่ 9 (พฤติกรรมปกติและวัตถุแปลก) ซึ่งอธิบายในรายละเอียดเพิ่มเติม
Lior Elrom

ฉันคิดว่ามีข้อผิดพลาดบางอย่างที่นี่: _ วัตถุใหม่ที่มีการเชื่อมโยงภายในหรือส่วนตัวไปยังต้นแบบของฟังก์ชัน Foo_ คุณหมายถึง: วัตถุใหม่ที่มีการเชื่อมโยงภายในหรือส่วนตัวไปยังต้นแบบของฟังก์ชัน Fooหรือไม่?
Koray Tugay

1
ขอบคุณ @KorayTugay! ใช่ฉันสะกดผิด :) +1
Lior Elrom

30

เพื่อให้ชัดเจนขึ้นนอกเหนือจากคำตอบที่ดีข้างต้น:

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

var eve = new Person("Eve");

eve.__proto__ == Person.prototype //true

eve.prototype  //undefined

กรณีมี__proto__ , เรียนมีต้นแบบ


12

ใน JavaScript ฟังก์ชันสามารถใช้เป็นตัวสร้างได้ นั่นหมายความว่าเราสามารถสร้างวัตถุออกมาโดยใช้คำหลักใหม่ ฟังก์ชั่นทุกตัวสร้างมาพร้อมกับวัตถุในตัวถูกผูกมัดกับพวกเขา วัตถุในตัวนี้เรียกว่าต้นแบบInstances of a constructor function use __proto__ to access the prototype property of its constructor function.

ไดอะแกรมต้นแบบ

  1. ก่อนอื่นเราสร้างตัวสร้าง: function Foo(){}. เพื่อความชัดเจน Foo เป็นเพียงฟังก์ชั่นอื่น แต่เราสามารถสร้างวัตถุจากมันด้วยคำหลักใหม่ นั่นเป็นเหตุผลที่เราเรียกมันว่าฟังก์ชั่นคอนสตรัคเตอร์

  2. ทุกฟังก์ชั่นมีคุณสมบัติที่เป็นเอกลักษณ์ซึ่งเรียกว่าคุณสมบัติต้นแบบ ดังนั้นฟังก์ชั่นFooการสร้างมีคุณสมบัติต้นแบบซึ่งชี้ไปที่ต้นแบบซึ่งก็คือFoo.prototype(ดูภาพ)

  3. ฟังก์ชั่นคอนสตรัคเตอร์เป็นฟังก์ชั่นซึ่งเป็นตัวอย่างของคอนสตรัคระบบที่เรียกว่า ดังนั้นเราจึงสามารถพูดได้ว่าfunction Fooมันถูกสร้างขึ้นโดยคอนสตรัค [[Function]] ดังนั้น__proto__ของเราจุดประสงค์ที่จะเป็นต้นแบบของคอนสตรัคของตนซึ่งเป็นFoo functionFunction.prototype

  4. Function.prototypeเป็นตัวเองคืออะไร [[Object]]แต่วัตถุที่สร้างขึ้นมาจากตัวสร้างระบบที่เรียกว่า ดังนั้นเป็นตัวสร้างของ[[Object]] Function.prototypeดังนั้นเราจึงสามารถพูดได้ว่าเป็นตัวอย่างของFunction.prototype [[Object]]ดังนั้น__proto__ของจุดที่จะต้องFunction.prototypeObject.prototype

  5. Object.prototypeเป็นคนสุดท้ายที่ยืนอยู่ในห่วงโซ่ต้นแบบ ฉันหมายความว่ามันไม่ได้ถูกสร้างขึ้น มันมีอยู่แล้วในระบบ ดังนั้นจุดที่จะต้อง__proto__null

  6. Fooตอนนี้เรามากรณีของ เมื่อเราสร้างอินสแตนซ์ที่ใช้มันจะสร้างวัตถุใหม่ซึ่งเป็นตัวอย่างของnew Foo() Fooนั่นหมายถึงFooตัวสร้างของอินสแตนซ์เหล่านี้ ที่นี่เราสร้างสองอินสแตนซ์ (x และ y) __proto__ของ x และ y Foo.prototypeจุดทำให้การ


เพื่อให้ชัดเจน: อินสแตนซ์ไม่มีคุณสมบัติต้นแบบ เฉพาะฟังก์ชันคอนสตรัคเตอร์ใช่ไหม ... ดังนั้นความแตกต่างระหว่างอินสแตนซ์และฟังก์ชั่นคอนสตรัคเตอร์ของมันคือ: ฟังก์ชั่นคอนสตรัคเตอร์มีทั้ง 1. โปรโต 2. วัตถุต้นแบบในขณะที่อินสแตนซ์เท่านั้นมีคุณสมบัติ.
Shaz

@Shaz คุณพูดถูก อินสแตนซ์ใช้โปรโตเพื่อเข้าถึงคุณสมบัติต้นแบบของฟังก์ชันตัวสร้าง
AL-zami

แต่ทำไมเมื่อคุณเขียน: var car = Object.create (Vehicle); คุณจะได้รับรถยนต์. __ proto__ = ยานพาหนะ แต่คุณยังได้รับคุณสมบัติ car.prototype ที่ชี้ไปที่ Vehicle.prototype?
Shaz

@ shaz คุณสามารถให้ jsfiddle ได้ไหมเพื่อที่ฉันจะได้เห็นภาพสถานการณ์?
AL-zami

1
ที่นี่ car.prototype เป็นทรัพย์สินที่สืบทอดมา รถสืบทอดคุณสมบัติ 'ต้นแบบ' จากฟังก์ชั่นยานพาหนะ ดังนั้น car.prototype === ยานพาหนะต้นแบบ คุณสมบัติ "ต้นแบบ" เป็นคุณสมบัติบนยานพาหนะ รถสามารถเข้าถึงได้ผ่านทางโซ่ต้นแบบ ฉันหวังว่านี่จะช่วยลดความสับสนของคุณ
AL-zami

8

สรุป:

__proto__คุณสมบัติของวัตถุเป็นทรัพย์สินที่แมปไปที่prototypeฟังก์ชั่นคอนสตรัคของวัตถุ ในคำอื่น ๆ :

instance.__proto__ === constructor.prototype // true

สิ่งนี้ใช้ในการสร้างprototypeห่วงโซ่ของวัตถุ prototypeห่วงโซ่เป็นกลไกการค้นหาสำหรับคุณสมบัติบนวัตถุ หากมีการเข้าถึงคุณสมบัติของวัตถุ JavaScript จะตรวจสอบวัตถุเองก่อน หากไม่พบสถานที่ให้บริการนั้นก็จะปีนขึ้นไปprotochainจนถึงจนกว่าจะพบ (หรือไม่)

ตัวอย่าง:

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

Person.prototype.age = 25;

const willem = new Person('Willem');

console.log(willem.__proto__ === Person.prototype); // the __proto__ property on the instance refers to the prototype of the constructor

console.log(willem.age); // 25 doesn't find it at willem object but is present at prototype
console.log(willem.__proto__.age); // now we are directly accessing the prototype of the Person function 

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

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

ทำไมถึงมีประโยชน์

JavaScript มีกลไกในการค้นหาคุณสมบัติObjectsที่เรียกว่า'การสืบทอดต้นแบบ'นี่คือสิ่งที่มันทำโดยทั่วไป:

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

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

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

let mySelf = new Person('Willem');

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

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


7

ฉันเป็นต้นแบบการเรียนรู้จากคุณไม่รู้ JS: นี่คือ & วัตถุต้นแบบซึ่งเป็นหนังสือที่ยอดเยี่ยมที่จะเข้าใจการออกแบบภายใต้และชี้แจงความเข้าใจที่คลาดเคลื่อนมากมาย (นั่นคือเหตุผลที่ฉันพยายามหลีกเลี่ยงการใช้มรดกและสิ่งต่าง ๆ เช่นinstanceof)

แต่ฉันมีคำถามเดียวกันกับที่คนถามกันที่นี่ คำตอบหลายคำตอบมีประโยชน์และให้ความรู้อย่างแท้จริง ฉันชอบที่จะแบ่งปันความเข้าใจของฉัน


ต้นแบบคืออะไร

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

วิธีรับต้นแบบของวัตถุ?

ผ่าน__proto__หรือObject.getPrototypeOf

var a = { name: "wendi" };
a.__proto__ === Object.prototype // true
Object.getPrototypeOf(a) === Object.prototype // true

function Foo() {};
var b = new Foo();
b.__proto__ === Foo.prototype
b.__proto__.__proto__ === Object.prototype

คือprototypeอะไร

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

เมื่อเราสร้างฟังก์ชั่นa, prototypeถูกสร้างโดยอัตโนมัติเป็นคุณสมบัติพิเศษในaและบันทึกรหัสฟังก์ชั่นในขณะที่บนconstructorprototype

function Foo() {};
Foo.prototype // Object {constructor: function}
Foo.prototype.constructor === Foo // true

ฉันชอบที่จะพิจารณาคุณสมบัตินี้เป็นสถานที่ในการจัดเก็บคุณสมบัติ (รวมถึงวิธีการ) ของวัตถุฟังก์ชั่น นั่นเป็นเหตุผลที่ว่าทำไมฟังก์ชันอรรถประโยชน์ใน JS มีการกำหนดไว้เช่นArray.prototype.forEach(), Function.prototype.bind(),Object.prototype.toString().

ทำไมต้องเน้นคุณสมบัติของฟังก์ชั่น ?

{}.prototype // undefined;
(function(){}).prototype // Object {constructor: function}

// The example above shows object does not have the prototype property.
// But we have Object.prototype, which implies an interesting fact that
typeof Object === "function"
var obj = new Object();

ดังนั้นArary, Function, Objectฟังก์ชั่นทั้งหมด ฉันควรยอมรับว่านี่เป็นการรีเฟรชการแสดงผลของฉันใน JS ฉันรู้ว่าฟังก์ชั่นเป็นพลเมืองอันดับหนึ่งใน JS แต่ดูเหมือนว่ามันสร้างขึ้นจากฟังก์ชั่น

ความแตกต่างระหว่าง__proto__และprototypeคืออะไร

__proto__การอ้างอิงทำงานบนทุกวัตถุเพื่ออ้างถึง[[Prototype]]คุณสมบัติของมัน

prototypeเป็นวัตถุที่สร้างขึ้นโดยอัตโนมัติเป็นคุณสมบัติพิเศษของฟังก์ชั่นซึ่งใช้ในการจัดเก็บคุณสมบัติ (รวมถึงวิธีการ) ของฟังก์ชั่นวัตถุ

ด้วยสองสิ่งนี้เราสามารถแมปเชนต้นแบบได้ เหมือนภาพนี้แสดงให้เห็น:

function Foo() {}
var b = new Foo();

b.__proto__ === Foo.prototype // true
Foo.__proto__ === Function.prototype // true
Function.prototype.__proto__ === Object.prototype // true

7

 จาวาสคริปต์ต้นแบบเทียบกับ __prototype__

'use strict'
function A() {}
var a = new A();
class B extends A {}
var b = new B();
console.log('====='); // =====
console.log(B.__proto__ === A); // true
console.log(B.prototype.__proto__ === A.prototype); // true
console.log(b.__proto__ === B.prototype); // true
console.log(a.__proto__ === A.prototype); // true
console.log(A.__proto__ === Function.__proto__); // true
console.log(Object.__proto__ === Function.__proto__); // true
console.log(Object.prototype === Function.__proto__.__proto__); // true
console.log(Object.prototype.__proto__ === null); // true

ในจาวาสคริปต์ทุก ๆ อ็อบเจกต์ (ฟังก์ชั่นก็เป็นออบเจ็กต์ด้วยเช่นกัน) มี__proto__คุณสมบัติคุณสมบัติอ้างอิงถึงต้นแบบ

เมื่อเราใช้ตัวnewดำเนินการกับตัวสร้างเพื่อสร้างวัตถุใหม่__proto__คุณสมบัติของวัตถุใหม่จะถูกตั้งค่าด้วยprototypeคุณสมบัติของตัวสร้างจากนั้นตัวสร้างจะถูกเรียกโดยวัตถุใหม่ในกระบวนการนั้น "นี่" จะเป็นการอ้างอิงถึงวัตถุใหม่ ในขอบเขตตัวสร้างในที่สุดก็ส่งคืนวัตถุใหม่

ต้นแบบของตัวสร้างเป็น__proto__คุณสมบัติสมบัติของตัวสร้างprototypeทำงานร่วมกับnewผู้ควบคุม

ตัวสร้างต้องเป็นฟังก์ชัน แต่ฟังก์ชันไม่ได้เป็นตัวสร้างเสมอแม้ว่าจะมีprototypeคุณสมบัติ

จริง ๆ แล้ว Prototype chain เป็น__proto__คุณสมบัติของวัตถุเพื่ออ้างอิงต้นแบบของมันและ__proto__คุณสมบัติของต้นแบบเพื่ออ้างอิงต้นแบบของต้นแบบและอื่น ๆ จนกระทั่งอ้างอิง__proto__คุณสมบัติต้นแบบของ Object ซึ่งอ้างอิงถึง null

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

console.log(a.constructor === A); // true
// "a" don't have constructor,
// so it reference to A.prototype by its ``__proto__`` property,
// and found constructor is reference to A

[[Prototype]]และ__proto__คุณสมบัติก็เหมือนกัน

เราสามารถใช้วิธี getPrototypeOf ของ Object เพื่อรับต้นแบบของบางสิ่ง

console.log(Object.getPrototypeOf(a) === a.__proto__); // true

ฟังก์ชั่นใด ๆ ที่เราเขียนสามารถใช้ในการสร้างวัตถุที่มีnewผู้ประกอบการดังนั้นทุกคนของฟังก์ชั่นเหล่านั้นสามารถเป็นตัวสร้าง


6

อีกวิธีที่ดีที่จะเข้าใจ:

var foo = {}

/* 
foo.constructor is Object, so foo.constructor.prototype is actually 
Object.prototype; Object.prototype in return is what foo.__proto__ links to. 
*/
console.log(foo.constructor.prototype === foo.__proto__);
// this proves what the above comment proclaims: Both statements evaluate to true.
console.log(foo.__proto__ === Object.prototype);
console.log(foo.constructor.prototype === Object.prototype);

หลังจากรองรับ IE11 __proto__แล้วเท่านั้น ก่อนที่รุ่นนั้นเช่น IE9 ที่คุณสามารถใช้เพื่อให้ได้constructor__proto__


เฉพาะที่ฉันจะเขียนมันในทางอื่น ๆ : foo .__ proto__ === foo.constructor.prototype
epeleg

6

แบบเดิม

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

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

function Robot(name) {
    this.name = name;
}
var robot = new Robot();

// the following are true   
robot.__proto__ == Robot.prototype
robot.__proto__.__proto__ == Object.prototype

นี่คือคำอธิบายของฉัน (ในจินตนาการ) เพื่อล้างความสับสน:

ลองจินตนาการว่ามีคลาสจินตภาพ (พิมพ์เขียว / ตัวตัด coockie) ที่เกี่ยวข้องกับฟังก์ชัน คลาสจินตภาพนั้นใช้เพื่อยกตัวอย่างวัตถุ prototypeเป็นกลไกการขยาย (วิธีการขยายใน C # หรือ Swift Extension) เพื่อเพิ่มสิ่งต่าง ๆ ในชั้นเรียนจินตนาการ

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

ด้านบนสามารถจินตนาการได้เป็น:

// imaginary class
class Robot extends Object{

    static prototype = Robot.class  
    // Robot.prototype is the way to add things to Robot class
    // since Robot extends Object, therefore Robot.prototype.__proto__ == Object.prototype

    var __proto__;

    var name = "";

    // constructor
    function Robot(name) {

        this.__proto__ = prototype;
        prototype = undefined;

        this.name = name;
    }

} 

ดังนั้น,

var robot = new Robot();

robot.__proto__ == Robot.prototype
robot.prototype == undefined
robot.__proto__.__proto__ == Object.prototype

ตอนนี้เพิ่มวิธีการprototypeของหุ่นยนต์:

Robot.prototype.move(x, y) = function(x, y){ Robot.position.x = x; Robot.position.y = y};
// Robot.prototype.move(x, y) ===(imagining)===> Robot.class.move(x, y)

สามารถจินตนาการได้ว่าเป็นส่วนขยายของคลาสหุ่นยนต์:

// Swift way of extention
extension Robot{
    function move(x, y){    
        Robot.position.x = x; Robot.position.y = y
    }
}

ซึ่งในทางกลับกัน

// imaginary class
class Robot{

    static prototype = Robot.class // Robot.prototype way to extend Robot class
    var __proto__;

    var name = "";

    // constructor
    function Robot(name) {

        this.__proto__ = prototype;
        prototype = undefined;

        this.name = name;
    }

    // added by prototype (as like C# extension method)
    function move(x, y){ 
        Robot.position.x = x; Robot.position.y = y
    };
}

ยังคงคิดถึงชื่อ__proto__และต้นแบบที่ต่อเนื่องกันมากขึ้น ต้นแบบและมรดกอาจ?
Dmitry

ฉันจะบอกว่าprototype& __proto__ควรหลีกเลี่ยงทั้งสองอย่าง เรามีชั้นเรียนแล้วและฉันชอบ OOP
Hassan Tareq

ปัญหาคือคลาสนั้นค่อนข้างใหม่และไม่ได้รับการสนับสนุนจากเอ็นจิ้นที่สะดวกสบายอย่างเช่น Microsoft JScript (ดีที่มีเมื่อทำงานบน C และต้องการเอ็นจิ้นสคริปต์ที่รวดเร็วและสกปรกที่มีอยู่เสมอ) และ nashorn javascript (ซึ่งมาพร้อมกับทั้งหมด การติดตั้ง Java ใหม่ภายใต้ jjs และเป็นวิธีที่ดีในการทำให้ Java เข้าสู่สภาพแวดล้อมแบบไดนามิกที่บริสุทธิ์ซึ่งคุณไม่จำเป็นต้องคอมไพล์ซ้ำ ๆ อยู่ตลอดเวลา) สิ่งคือถ้าคลาสเป็นน้ำตาลมันจะไม่เป็นปัญหา แต่มันไม่ใช่มันมีสิ่งที่เป็นไปไม่ได้หากไม่มีพวกมันในเวอร์ชั่น js ที่เก่ากว่า เช่นเดียวกับการขยาย "ฟังก์ชั่น"
Dmitry

ในที่สุดเราจะได้รับการสนับสนุน ฉันเป็นผู้พัฒนาแบ็กเอนด์ดังนั้นฉันไม่มีปัญหาฉันเขียนโค้ดใน js น้อยมาก
Hassan Tareq

และการสืบทอดสมาชิกสแตติกในลักษณะที่เพิ่มใหม่ / ลบสมาชิกสแตติกจากพาเรนต์จะสังเกตเห็นโดยเด็ก (ซึ่งฉันไม่สามารถนึกถึงวิธีที่จะทำกับ JScript ซึ่งไม่ได้เสนอ Object.assign / __ proto __ / getPrototypeOf ดังนั้นคุณ มีคนจรจัดกับ Object.prototype รากมันแกล้ง)
มิทรี

4

หากต้องการใส่เพียง:

> var a = 1
undefined
> a.__proto__
[Number: 0]
> Number.prototype
[Number: 0]
> Number.prototype === a.__proto__
true

สิ่งนี้ช่วยให้คุณสามารถแนบคุณสมบัติกับวัตถุ X.prototype หลังจากที่ประเภท X ได้รับอินสแตนซ์และพวกเขาจะยังคงเข้าถึงคุณสมบัติใหม่เหล่านั้นผ่านการอ้างอิง __proto__ ซึ่ง Javascript-engine ใช้ในการเดินโซ่ต้นแบบ


4

Prototype หรือ Object.prototypeเป็นคุณสมบัติของวัตถุตามตัวอักษร มันแสดงให้เห็นถึงวัตถุต้นแบบวัตถุซึ่งคุณสามารถแทนที่เพื่อเพิ่มคุณสมบัติหรือวิธีการเพิ่มเติมตามห่วงโซ่ต้นแบบ

__proto__เป็นคุณสมบัติ accessor (ฟังก์ชั่น get และ set) ซึ่งจะแสดงต้นแบบต้นแบบภายในของวัตถุที่ผ่านการเข้าถึง

อ้างอิง:

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
  2. http://www.w3schools.com/js/js_object_prototypes.asp

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


Object.prototypeไม่ใช่คุณสมบัติของวัตถุตามตัวอักษรพยายามพิมพ์{}.prototypeส่งคืนไม่ได้กำหนด; แต่ก็สามารถเข้าถึงได้ผ่านซึ่งผลตอบแทน{}.__proto__ Object.prototype
doubleOrt

3

ฉันรู้ว่าฉันมาสาย แต่ให้ฉันพยายามทำให้มันง่ายขึ้น

ให้เราบอกว่ามีฟังก์ชั่น

    function Foo(message){

         this.message = message ; 
     };

     console.log(Foo.prototype);

ฟังก์ชั่น Foo จะมีวัตถุต้นแบบเชื่อมโยงอยู่ ดังนั้นเมื่อใดก็ตามที่เราสร้างฟังก์ชันใน JavaScript มันจะมีวัตถุต้นแบบเชื่อมโยงอยู่เสมอ

ตอนนี้ให้เราไปข้างหน้าและสร้างวัตถุสองรายการโดยใช้ฟังก์ชั่นฟู

    var a = new Foo("a");
    var b = new Foo("b");
    console.log(a.message);
    console.log(b.message);
  1. ตอนนี้เรามีวัตถุสองอย่างคือวัตถุ a และวัตถุ b ทั้งสองถูกสร้างขึ้นโดยใช้ตัวสร้าง Foo โปรดทราบว่า Constructor เป็นเพียงคำศัพท์ที่นี่
  2. วัตถุ a และ b ทั้งคู่มีสำเนาข้อความคุณสมบัติ
  3. วัตถุทั้งสองนี้ a และ b เชื่อมโยงกับวัตถุต้นแบบของตัวสร้าง Foo
  4. บนวัตถุ a และ b เราสามารถเข้าถึง Foo prototype โดยใช้คุณสมบัติprotoในเบราว์เซอร์ทั้งหมดและใน IE เราสามารถใช้ Object.getPrototypeOf (a) หรือ Object.getPrototypeOf (b)

ตอนนี้ Foo.prototype, a. โปรโตและบี โปรโต ทั้งหมดหมายถึงวัตถุเดียวกัน

    b.__proto__ === Object.getPrototypeOf(a);
    a.__proto__ ===  Foo.prototype;
    a.constructor.prototype  === a.__proto__;

ทั้งหมดข้างต้นจะกลับมาจริง

ดังที่เราทราบคุณสมบัติ JavaScript สามารถเพิ่มแบบไดนามิก เราสามารถเพิ่มคุณสมบัติให้กับวัตถุ

    Foo.prototype.Greet = function(){

         console.log(this.message);
    }
    a.Greet();//a
    b.Greet();//b
    a.constructor.prototype.Greet();//undefined 

ตามที่คุณเห็นเราได้เพิ่มวิธี Greet () ใน Foo.prototype แต่สามารถเข้าถึงได้ใน a และ b หรือวัตถุอื่น ๆ ที่สร้างขึ้นโดยใช้ Foo

ขณะดำเนินการ a.Greet () จาวาสคริปต์จะค้นหา Greet ในวัตถุ a จากรายการคุณสมบัติ เมื่อไม่พบมันจะเพิ่มขึ้นในโปรโตเชนของ ตั้งแต่ protoและ Foo.prototype เป็นวัตถุเดียวกัน JavaScript จะค้นหาวิธี Greet () และดำเนินการ

ฉันหวังว่าตอนนี้ต้นแบบและโปรโต จะง่ายขึ้นเล็กน้อย


3

ตัวอย่างที่อธิบาย:

function Dog(){}
Dog.prototype.bark = "woof"

let myPuppie = new Dog()

ตอนนี้ myPupppie มี__proto__คุณสมบัติที่ชี้ไปที่ Dog.prototype

> myPuppie.__proto__
>> {bark: "woof", constructor: ƒ}

แต่ myPuppie ไม่มีคุณสมบัติต้นแบบ

> myPuppie.prototype
>> undefined

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

คำอธิบายที่ดีโดย MPJ ที่นี่: proto vs prototype - การสร้าง Object ใน JavaScript


3

ฉันได้วาดรูปขนาดเล็กที่แสดงตัวอย่างข้อมูลต่อไปนี้ให้กับตัวเองแล้ว:

var Cat = function() {}
var tom = new Cat()

ทำความเข้าใจกับ __proto__ และอุปกรณ์ต้นแบบ

ฉันมีพื้นฐาน OO แบบคลาสสิกดังนั้นจึงเป็นประโยชน์ในการแสดงลำดับชั้นในลักษณะนี้ เพื่อช่วยให้คุณอ่านไดอะแกรมนี้ให้ปฏิบัติสี่เหลี่ยมในรูปภาพเป็นวัตถุ JavaScript และใช่ฟังก์ชั่นก็เป็นวัตถุด้วย ;)

วัตถุใน JavaScript มีคุณสมบัติและ__proto__เป็นเพียงหนึ่งในนั้น

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

วัตถุรากใน JavaScript คือObject.prototypeและวัตถุอื่น ๆ ทั้งหมดเป็นลูกหลานของอันนี้ __proto__คุณสมบัติของวัตถุรากnullซึ่งหมายถึงการสิ้นสุดของห่วงโซ่มรดก

คุณจะสังเกตเห็นว่าprototypeเป็นคุณสมบัติของฟังก์ชั่น Catเป็นฟังก์ชั่น แต่ยังFunctionและObject(ฟังก์ชั่น) ฟังก์ชั่น tomไม่ใช่ฟังก์ชันดังนั้นจึงไม่มีคุณสมบัตินี้

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

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

แน่นอนเมื่อเราสร้างtomวัตถุด้วยวัตถุnew Cat()ที่สร้างขึ้นจะมีการ__proto__ตั้งค่าคุณสมบัติให้prototypeวัตถุของฟังก์ชั่นการสร้าง

ในท้ายที่สุดขอให้เราเล่นกับแผนภาพนี้สักหน่อย ข้อความต่อไปนี้เป็นจริง:

  • tom.__proto__Cat.prototypeจุดสถานที่ให้บริการไปยังวัตถุเช่นเดียวกับ

  • Cat.__proto__ชี้ไปที่Function.prototypeวัตถุเช่นเดียวกับFunction.__proto__และObject.__proto__ทำ

  • Cat.prototype.__proto__และชี้ไปที่วัตถุเดียวกันและที่เป็นtom.__proto__.__proto__Object.prototype

ไชโย!


อธิบายได้ดีมาก!
StackOverflow UI

@theshinylight tom.__proto__และCat.prototypeมีความเข้มงวดเท่ากันดังนั้นtom.__proto__ === Cat.prototype และ Cat.prototype === tom.__proto__เป็นจริง ดังนั้นลูกศรในภาพหมายถึงอะไร?
aXuser264

ลูกศรสีดำ (ถ้าคุณอ้างถึง) ไม่มีความหมายเฉพาะนอกเหนือจากคุณสมบัติของวัตถุ ดังนั้นprototypeคุณสมบัติของCatวัตถุ (จากคำถามของคุณ)
theshinylight

2

บทนิยาม

(ตัวเลขที่อยู่ในวงเล็บ () คือ 'ลิงก์' ไปยังโค้ดที่เขียนด้านล่าง)

prototype- วัตถุที่ประกอบด้วย:
=> ฟังก์ชั่น (3) ของเฉพาะนี้ConstructorFunction.prototype(5) ที่สามารถเข้าถึงได้โดยแต่ละวัตถุ (4) สร้างขึ้นหรือที่จะสร้างผ่านฟังก์ชั่นคอนสตรัคนี้ (1)
=> ฟังก์ชั่นคอนสตรัคตัวเอง (1) )
=> __proto__ของวัตถุเฉพาะนี้ (วัตถุต้นแบบ)

__proto__ (dandor proto?) - ลิงก์ระหว่างวัตถุใด ๆ (2) ที่สร้างขึ้นผ่านฟังก์ชั่นคอนสตรัคเตอร์เฉพาะ (1) และคุณสมบัติของวัตถุต้นแบบ (5) ของคอนสตรัคเตอร์นั้นที่อนุญาตให้แต่ละวัตถุที่สร้างขึ้น (2) เข้าถึงฟังก์ชั่นของต้นแบบ และวิธีการ (4) (__proto__โดยค่าเริ่มต้นรวมอยู่ในทุกวัตถุเดียวใน JS)

การจำแนกรหัส

1

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

2

    var John = new Person(‘John’, 37);
    // John is an object

3

    Person.prototype.getOlder = function() {
        this.age++;
    }
    // getOlder is a key that has a value of the function

4

    John.getOlder();

5

    Person.prototype;

1

ฉันจะลองคำอธิบายชั้นประถมศึกษาปีที่ 4:

สิ่งที่ง่ายมาก A prototypeเป็นตัวอย่างของวิธีการสร้างบางสิ่งบางอย่าง ดังนั้น:

  • ฉันfunctionและฉันสร้างวัตถุใหม่ที่คล้ายกับของฉันprototype

  • ฉันobjectและฉันถูกสร้างขึ้นโดยใช้__proto__ตัวอย่างของฉัน

หลักฐาน :

function Foo() { }

var bar = new Foo()

// `bar` is constructed from how Foo knows to construct objects
bar.__proto__ === Foo.prototype // => true

// bar is an instance - it does not know how to create objects
bar.prototype // => undefined

1
Nope ไม่prototypeหรือมิได้__proto__มีการใช้ในเวลาใด ๆ ที่เป็นพิมพ์เขียวหรือเพื่อการสร้างวัตถุใด ๆ นี่คือตำนานที่นำเสนอโดยclassไวยากรณ์ที่พร่ามัวและเป็นรุ่นก่อน ดังที่คำตอบของโพสต์บอกว่ามันใช้สำหรับการค้นหาและในกรณีของprototypeการระบุการconstructorใช้งานด้วยnew(ซึ่งเป็นส่วนหนึ่งของกลไกการแสร้งทำเป็นแบบคลาสซี่ที่ทำให้ผู้ใช้สับสนจำนวนมากรวมถึงฉันด้วย)
Christof Kälin

จุดแรกควรเป็น "ฉันเป็นฟังก์ชันและฉันสร้างวัตถุใหม่ที่จะมอบให้ต้นแบบของฉัน"
Nitin Jadhav

1

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

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

ในตัวอย่างด้านบน:

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

var eve = new Person("Eve");

console.log(eve.__proto__ == Person.prototype) // true
// this is exactly what prototype does, made Person.prototype equal to eve.__proto__

ฉันหวังว่ามันสมเหตุสมผล


1
prototypeไม่ได้ใช้เพื่อสร้าง__proto__วัตถุ __proto__เมื่อเข้าถึงจะให้การอ้างอิงไปยังprototypeวัตถุเท่านั้น
doubleOrt

1

สิ่งที่เกี่ยวกับการใช้__proto__สำหรับวิธีการคงที่?

function Foo(name){
  this.name = name
  Foo.__proto__.collection.push(this)
  Foo.__proto__.count++

}

Foo.__proto__.count=0
Foo.__proto__.collection=[]

var bar = new Foo('bar')
var baz = new Foo('baz')

Foo.count;//2
Foo.collection // [{...}, {...}]
bar.count // undefined

นั่นเป็นเหตุผลว่าทำไมคำตอบของ" __proto__VS. prototypeใน JavaScript" ?
Andreas

มันดีหรือเปล่าเกี่ยวกับ Foo.collection.push (นี่) Foo.count ++
Selva Ganapathi


1

มีเพียงวัตถุเดียวเท่านั้นที่ใช้สำหรับการผูกสายโพรเพล เห็นได้ชัดว่าวัตถุนี้มีชื่อและค่า: __proto__เป็นชื่อของมันและprototypeเป็นค่าของมัน นั่นคือทั้งหมดที่

เพื่อให้เข้าใจง่ายยิ่งขึ้นดูที่แผนภาพด้านบนของโพสต์นี้ (Diagram โดย dmitry soshnikov) คุณจะไม่พบ__proto__คะแนนจากสิ่งอื่นนอกจากprototypeจากค่าของมัน

ส่วนสำคัญคือ: __proto__เป็นชื่อที่อ้างอิงถึงวัตถุต้นแบบและprototypeเป็นวัตถุต้นแบบจริง

มันเหมือนว่า:

let x = {name: 'john'};

xคือชื่อวัตถุ (ตัวชี้) และ{name: 'john'}เป็นวัตถุจริง (ค่าข้อมูล)

หมายเหตุ:นี่เป็นเพียงคำใบ้ที่ทำให้เข้าใจง่ายขึ้นอย่างมากเกี่ยวกับความสัมพันธ์ในระดับสูง

อัปเดต:นี่คือตัวอย่าง Javascript ที่เป็นรูปธรรมอย่างง่ายสำหรับภาพประกอบที่ดีกว่า:

let x = new String("testing") // Or any other javascript object you want to create

Object.getPrototypeOf(x) === x.__proto__; // true

ซึ่งหมายความว่าเมื่อObject.getPrototypeOf(x)เราได้รับค่าที่แท้จริงของx(ซึ่งก็คือต้นแบบของมัน) เป็นสิ่งที่__proto__ของxจะชี้ไปที่ ดังนั้นแน่นอนชี้ไปที่เป็นต้นแบบของ__proto__ xดังนั้น__proto__การอ้างอิงx(ตัวชี้ของx) และprototypeเป็นค่าของx (ต้นแบบของมัน)

ฉันหวังว่ามันชัดเจนในตอนนี้


1

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

function protofoo(){
}
var protofoo1 = new protofoo();
console.log(protofoo.prototype.toString()); //[object Object]

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

var foo={
  check: 10
};
console.log(foo.__proto__); // empty
console.log(bar.prototype); //  TypeError
foo.__proto__ = protofoo1; // assigned
console.log(foo.__proto__); //protofoo

เราสามารถใช้ Object.create เพื่อเชื่อมโยงวัตถุอย่างชัดเจน

// we can create `bar` and link it to `foo`
var bar = Object.create( foo );
bar.fooprops= "We checking prototypes";
console.log(bar.__proto__); // "foo"
console.log(bar.fooprops); // "We checking prototypes"
console.log(bar.check); // 10 is delegated to `foo`

0

__proto__เป็นฐานในการสร้างprototypeและฟังก์ชั่นคอนสตรัคเช่น: function human(){}มีการprototypeแบ่งปันผ่าน__proto__ในตัวอย่างใหม่ของฟังก์ชั่นคอนสตรัค อ่านรายละเอียดเพิ่มเติมได้ที่นี่


@ Derick แดเนียล: ไม่แน่ใจว่าทำไมคุณลงคะแนนนี้ แต่การแก้ไขที่คุณทำไม่ใช่ว่าฉันพยายามสื่อ แก้ไขได้อีกเพื่อการกวาดล้างเพิ่มเติม :)
Jyoti Duhan

Jyoti ฉันไม่ได้ลงคะแนนคำตอบของคุณคนอื่นทำฉันเพิ่งแก้ไข :)
Freelancer

0

ในฐานะที่เป็นนี้ระบุไว้อย่างถูกต้อง

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

( new Foo ).__proto__ === Foo.prototype;
( new Foo ).prototype === undefined;

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

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

ลองพิจารณาตัวอย่างต่อไปนี้:

function Human(){
    this.speed = 25;
}

var himansh = new Human();

Human.prototype.showSpeed = function(){
    return this.speed;
}

himansh.__proto__ === Human.prototype;  //true
himansh.showSpeed();    //25

//now re-initialzing the Human.prototype aka changing its memory location
Human.prototype = {lhs: 2, rhs:3}

//himansh.__proto__ will still continue to point towards the same original memory location. 

himansh.__proto__ === Human.prototype;  //false
himansh.showSpeed();    //25

-1

ความเข้าใจของฉันคือ: __proto__ และอุปกรณ์ต้นแบบทั้งหมดใช้สำหรับเทคนิคลูกโซ่ต้นแบบ ความแตกต่างคือฟังก์ชั่นที่มีเครื่องหมายขีดล่าง (เช่น __proto__) ไม่ได้มีจุดมุ่งหมายสำหรับนักพัฒนาที่เรียกใช้อย่างชัดเจนเลย กล่าวอีกนัยหนึ่งมันเป็นเพียงกลไกบางอย่างเช่นการสืบทอด ฯลฯ มันเป็น 'แบ็คเอนด์' แต่ฟังก์ชั่นที่ไม่มีเครื่องหมายขีดล่างได้รับการออกแบบมาสำหรับการเรียกใช้อย่างชัดเจนพวกมันคือ 'front-end'


3
มีมากกว่า__proto__และprototypeมากกว่าเพียงแค่การตั้งชื่อ พวกเขาอาจหรือไม่อาจชี้ไปที่วัตถุเดียวกัน ดูคำตอบ @zyklus
demisx

1
@demisx แน่นอนว่าคุณพูดถูก แต่ความคิดเห็นของฉันคือความแตกต่างของชื่อเปิดเผยความแตกต่างของฟังก์ชัน
Beicai

มันไม่เพียงพอที่จะกล่าวว่า "ตามความเข้าใจของคุณ" โดยเฉพาะอย่างยิ่งเมื่อมีการให้คำตอบที่ดีอื่น ๆ มาก่อน ...
ProfNandaa

-3

!!! นี่คือคำอธิบายที่ดีที่สุดในโลก !!!!!

var q = {}
var prototype = {prop: 11}

q.prop // undefined
q.__proto__ = prototype
q.prop // 11

ในฟังก์ชั่นเครื่องมือสร้าง javascript เรียกสิ่งนี้q.__proto__ = prototypeโดยอัตโนมัติเมื่อเราเขียนnew Classและใน__proto__ชุดเสาClass.prototype

function Class(){}
Class.prototype = {prop: 999} // set prototype as we need, before call new

var q = new Class() // q.__proto__ = Class.prototype
q.prop // 999

สนุก %)

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