__Proto__ khác với constructor.prototype như thế nào?


163
function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

Nó luôn trả về đối tượng với rating = 3.

Nhưng nếu tôi làm như sau:

newtoy.__proto__.__proto__.__proto__

Chuỗi kết thúc trở lại null.

Ngoài ra trong Internet Explorer, làm cách nào để kiểm tra null nếu không có thuộc __proto__tính?


30
Đây sơ đồ đồ thị sẽ giúp bạn hiểu sự khác biệt giữa nguyên mẫu và proto . Bạn có thể theo chuỗi proto từ đối tượng newtoy, và sau đó bạn sẽ nhận ra lý do tại sao Proto thứ 3 từ newtoy là null.
bit

Ngoài ra, rõ ràng từ sơ đồ newtoy.prototypekhông bằng newtoy.constructor.prototypevà do đó newtoy.constructor.prototypesẽ không có thuộc tính được gọi rating. Tương tự newtoy.constructor.prototype.constructor.propertycũng sẽ không có tài sản được gọi rating.
bit

Typo trong bình luận cuối cùng: do đó newtoy.constructor.prototypesẽ có tài sản được gọi là xếp hạng. Tương tự newtoy.constructor.prototype.constructor.propertycũng sẽ có tài sản được gọi là xếp hạng.
bit


1
@Royi Namir Tôi đã tải lên jsViz trên github. Đây là trang web demo . Xin đừng bận tâm làm thế nào không rõ ràng (và bẩn) mã thực tế. Dự án siêu cũ của nó mà tôi đã không chạm vào mãi mãi.
bit

Câu trả lời:


210

Gần đây tôi đã cố gắng che giấu điều này và cuối cùng đã đưa ra "bản đồ" này mà tôi nghĩ rằng đã làm sáng tỏ vấn đề

http://i.stack.imgur.com/KFzI3.png nhập mô tả hình ảnh ở đây

Tôi biết tôi không phải là người đầu tiên thực hiện điều này nhưng điều thú vị hơn là tìm ra nó khi tìm thấy nó :-). Dù sao, sau đó tôi đã tìm thấy ví dụ như một sơ đồ khác mà tôi nghĩ nói cơ bản giống nhau:

Bố cục đối tượng Javascript

Điều đáng ngạc nhiên nhất đối với tôi là khám phá ra những Object.__proto__điểm đó Function.prototype, thay vì Object.prototype, nhưng tôi chắc chắn rằng có một lý do chính đáng cho điều đó :-)

Tôi dán mã được đề cập trong hình ảnh ở đây cũng như nếu có ai muốn kiểm tra nó. Lưu ý rằng một số thuộc tính được thêm vào các đối tượng để dễ dàng biết chúng ta đang ở đâu sau một số bước nhảy:

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);

2
@utsaina Rất tuyệt. Kiểm tra một đại diện đồ họa khác của mã mà OP đã đăng. Và tôi nghĩ rằng sơ đồ của chúng tôi là thỏa thuận về các chi tiết kỹ thuật.
bit

43
Lý do tại sao Object.__proto__trỏ đến Function.prototypelà bởi vì Object()chính nó là một hàm riêng tạo ra một đối tượng trống. Vì thế,Object() là một chức năng. Bạn sẽ thấy rằng tất cả các __proto__thuộc tính chính của các loại bản địa khác đều trỏ đến Function.prototype. Object, Function, String, Number, Và Arraytất cả kế thừa nguyên mẫu Function.
Xoay

@drodsou liên kết thứ 2 của bạn thật tuyệt vời. Vui lòng kiểm tra ngay bây giờ;) mollypages.org/misc/js.mp Giải thích hay: D
abhisekp

@Sw Xoay "Do đó, Object () là một hàm" - ý của bạn là nói Object là một hàm? không có ()
giorgim

2
@GiorgiMoniava Đúng. Objectbản thân nó là một chức năng; kết quả của việc thực thi có thể gọi được Object(nghĩa là giá trị trả về của việc chạy Object()) không phải là một hàm.
Xoay

67

constructorlà một thuộc tính [[DontEnum]] được xác định trước của đối tượng được chỉ ra bởi thuộc prototypetính của một đối tượng hàm và ban đầu sẽ trỏ đến chính đối tượng hàm.

__proto__ tương đương với thuộc tính [[Prototype]] bên trong của một đối tượng, tức là nguyên mẫu thực tế của nó.

Khi bạn tạo một đối tượng với newtoán tử, thuộc tính [[Prototype]] bên trong của nó sẽ được đặt thành đối tượng được trỏ bởi hàm constructorprototype .

Điều này có nghĩa là .constructorsẽ đánh giá .__proto__.constructor, tức là hàm tạo được sử dụng để tạo đối tượng và như chúng ta đã học,protoype tính của hàm này đã được sử dụng để đặt [[Prototype]] của đối tượng.

Nó theo đó .constructor.prototype.constructorlà giống hệt .constructor(miễn là các thuộc tính này chưa được ghi đè); xem ở đây để giải thích chi tiết hơn.

Nếu __proto__có sẵn, bạn có thể đi theo chuỗi nguyên mẫu thực tế của đối tượng. Không có cách nào để làm điều này trong ECMAScript3 đơn giản vì JavaScript không được thiết kế cho hệ thống phân cấp kế thừa sâu.


3
Liên kết 'ở đây' là tiêu chuẩn vàng. Đến đó nếu bạn muốn mô tả đầy đủ.
Ricalsin

Bắt đẹp với .constructor.prototypexích. Tôi cũng không rõ ràng đối với tôi, trong khi tôi không thấy điều đó .constructorlà bằng nhau .__proto__.constructor. Điều này chỉ đơn giản có nghĩa là đi xe đạp giữa chức năng xây dựng và nguyên mẫu của nó.
Johnny_D

30

Kế thừa nguyên mẫu trong JavaScript dựa trên __proto__ thuộc tính theo nghĩa là mỗi đối tượng đang kế thừa nội dung của đối tượng được tham chiếu bởi thuộc tính của nó __proto__.

Các prototypetài sản là đặc biệt chỉ dành cho Functioncác đối tượng và chỉ khi sử dụng newnhà điều hành để gọi một Functionnhư constructor. Trong trường hợp này, đối tượng được tạo __proto__sẽ được đặt thành constructorFunction.prototype .

Điều này có nghĩa là việc thêm vào Function.prototypesẽ tự động phản ánh trên tất cả các đối tượng __proto__đang tham chiếuFunction.prototype .

Thay thế constructor Function.prototypebằng một đối tượng khác sẽ không cập nhật__proto__ tính cho bất kỳ đối tượng nào đã tồn tại.

Lưu ý rằng thuộc __proto__tính không nên được truy cập trực tiếp, Object.getPrototypeOf (object) nên được sử dụng thay thế.

Để trả lời câu hỏi đầu tiên, tôi đã tạo một sơ đồ __proto__và các prototypetham chiếu riêng biệt , không may là stackoverflow không cho phép tôi thêm hình ảnh với "ít hơn 10 danh tiếng". Có lẽ một thời gian khác.

[Chỉnh sửa] Hình sử dụng [[Prototype]]thay vì__proto__ vì bởi vì đó là cách đặc tả ECMAScript đề cập đến các đối tượng bên trong. Tôi hy vọng bạn có thể tìm ra mọi thứ.

Dưới đây là một số gợi ý để giúp bạn hiểu con số:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

Lưu ý rằng thuộc constructortính không tồn tại trong các đối tượng được tạo, nhưng được kế thừa từ nguyên mẫu.

nhập mô tả hình ảnh ở đây


@xorcus Bạn có thể giải thích điều này: new MyFunction()tạo một thể hiện đối tượng mà nó __proto__nên tham chiếu đến nguyên mẫu ctor của nó MyFunction.prototype., vậy tại sao lại đề cập MyFunction.prototype.__proto__đến Object.prototype? nó nên được giới thiệu (như mẫu đầu tiên của tôi) với nguyên mẫu của ctor của nó MyFunction.prototype(chú ý MyFunction.prototypelà bản năng của nó Myfunction)
Royi Namir

@Royi Namir: MyFunction.prototype .__ proto__ đề cập đến Object.prototype vì MyFunction.prototype là một đối tượng. Object.prototype được kế thừa bởi tất cả các đối tượng (thông thường, đó là nơi chuỗi nguyên mẫu kế thừa kết thúc). Tôi không đồng ý rằng MyFunction.prototype là một phiên bản của MyFunction. obj instanceof MyFunction <=> MyFunction.prototype.isPrototypeOf (obj) <=> MyFunction.prototype tồn tại trong chuỗi nguyên mẫu obj. Đó không phải là trường hợp của đối tượng MyFunction.prototype
xorcus

14

Objectlà Eva và Functionlà Adam, Adam ( Function) sử dụng xương của mình ( Function.prototype) để tạo ra Eve ( Object). Vậy thì ai đã tạo ra Adam ( Function)? - Nhà phát minh ngôn ngữ JavaScript :-).

Theo câu trả lời của utsaina, tôi muốn thêm thông tin hữu ích.

Điều đáng ngạc nhiên nhất đối với tôi là khám phá ra những Object.__proto__ điểm đó Function.prototype, thay vì Object.prototype, nhưng tôi chắc chắn rằng có một lý do chính đáng cho điều đó :-)

Nó không nên. Object.__proto__KHÔNG nên chỉ vào Object.prototype. Thay vào đó, ví dụ của Object o, o.__proto__nên trỏ đến Object.prototype.

(Hãy tha thứ cho tôi vì đã sử dụng các thuật ngữ classinstancetrong JavaScript, nhưng bạn biết điều đó :-)

Tôi nghĩ rằng Objectchính lớp học là một ví dụ Function, đó là lý do tại sao Object.__proto__ === Function.prototype. Do đó: Objectlà Eva và Functionlà Adam, Adam ( Function) sử dụng xương của mình ( Function.prototype) để tạo ra Eve ( Object).

Hơn nữa, ngay cả Functionbản thân lớp cũng là một thể hiện của Functionchính nó, đó Function.__proto__ === Function.prototypecũng là lý do tại saoFunction === Function.constructor

Hơn nữa, lớp thông thường Catlà một ví dụ của Function, đó là Cat.__proto__ === Function.prototype.

Lý do cho điều trên là, khi chúng ta tạo một lớp trong JavaScript, thực ra, chúng ta chỉ đang tạo một hàm, nên là một thể hiện của Function. ObjectFunctionchỉ đặc biệt, nhưng chúng vẫn là các lớp, trong khi Catlà một lớp thông thường.

Là một yếu tố, trong công cụ JavaScript của Google Chrome, 4 điều sau đây:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

Chúng đều ===(hoàn toàn bằng nhau) với 3 cái còn lại và giá trị của chúng làfunction Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

ĐỒNG Ý. Vậy thì ai tạo ra sự đặc biệt function Empty() {}( Function.prototype)? Hãy suy nghĩ về nó :-)


Đồng ý với điều đó, ngoại trừ điều cuối cùng: function Empty() {}bạn muốn nói gì về chức năng của Function.prototype, v.v?, Mã bạn đã sử dụng trong bảng điều khiển chrome là gì?
drodsou

2
Tôi đã sửa một điều cuối cùng mà bạn chỉ ra. Giá trị của chúng là function Empty() {}trong Google Chrome. Tôi cũng đã thêm đầu ra giao diện điều khiển.
Peter Lee

tất cả các hàm là thể hiện của Hàm, và do đó, tất cả các hàm kế thừa ( _ _proto_ _) từ Function.prototype. Nó đơn giản như vậy :)
xorcus 20/05/2015

Xin lỗi vì đã bình luận về chủ đề cũ. Nhưng chúng có được tạo ra bởi Inventor of Language không?
Patel Parth

6

Tôi thực sự không biết tại sao mọi người không sửa cho bạn về vấn đề thực sự trong sự hiểu biết của bạn.

Điều này sẽ giúp bạn dễ dàng phát hiện ra vấn đề hơn

Vì vậy, hãy xem những gì đang xảy ra:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

Tuyệt, vậy bây giờ chúng ta hãy nhìn vào điều này __proto__

Trước đó, hãy nhớ 2 điều liên quan đến __proto__ :

  1. Khi bạn tạo một đối tượng với newtoán tử, nội bộ [[Prototype]]/ thuộc proto__tính của nó sẽ được đặt thành thuộc prototypetính (1) của nó constructor functionhoặc "người tạo" nếu bạn muốn.

  2. Mã hóa cứng trong JS -: Object.prototype.__proto__null.

Hãy xem 2 điểm này là " bill"

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

Tốt hơn?


2

Mỗi chức năng tạo ra nguyên mẫu của nó. Và khi chúng ta tạo một đối tượng bằng cách sử dụng hàm tạo đó thì thuộc tính __proto__ của đối tượng của tôi sẽ bắt đầu trỏ đến nguyên mẫu của hàm đó.


1
Tôi nghĩ bạn có nghĩa là để nói __proto__tài sản.
demisx

Đúng. Tôi có nghĩa là tài sản proto của một đối tượng. Tôi hy vọng thông tin này hữu ích.
Apoorv Nag

2

Nếu tất cả các số liệu đó là áp đảo, hãy xem các thuộc tính có nghĩa là gì.

STH.prototype

Khi tạo một hàm mới, có một đối tượng trống được tạo song song và được liên kết với hàm bằng [[Prototype]]chuỗi. Để truy cập đối tượng này, chúng tôi sử dụng prototypethuộc tính của hàm.

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

Hãy nhớ rằng prototypetài sản chỉ có sẵn cho các chức năng.

STH.conortor

Đối tượng nguyên mẫu được đề cập ở trên không có thuộc tính nào ngoại trừ một - constructor. Thuộc tính này đại diện cho một chức năng tạo ra đối tượng nguyên mẫu.

var toy = new Gadget();

Khi tạo Gadgethàm, chúng tôi cũng tạo một đối tượng {constructor: Gadget}- không có gì giống như vậy Gadget.prototype. Như constructorđề cập đến một chức năng tạo ra một nguyên mẫu đối tượng, toy.constructorđại diện cho Gadgetchức năng. Chúng tôi viết toy.constructor.prototypevà chúng tôi đang nhận được{constructor: Gadget} một lần nữa.

Do đó, có một vòng luẩn quẩn: bạn có thể sử dụng toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototypevà nó sẽ luôn như vậy Gadget.prototype.

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

STH .__ proto__

Trong khi prototypelà một thuộc tính cụ thể cho các chức năng, __proto__có sẵn cho tất cả các đối tượng khi nó nằm trong Object.prototype. Nó đề cập đến nguyên mẫu của một chức năng có thể tạo ra một đối tượng.

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

Dưới đây, toy.__proto__Gadget.prototype. Như Gadget.prototypelà một đối tượng ( {}) và các đối tượng được tạo bằng Objecthàm (xem ví dụ ở trên), chúng tôi nhận được Object.prototype. Đây là đối tượng cao hơn trong JavaScript và nó __proto__chỉ có thể chỉ ra null.

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain

0

Câu trả lời ngắn: __proto__là một tham chiếu đến thuộc prototypetính của hàm tạo đã tạo đối tượng.

Các đối tượng trong JavaScript

Đối tượng JavaScript là một loại tích hợp cho một tập hợp các thuộc tính bằng 0 hoặc nhiều hơn. Thuộc tính là các thùng chứa các đối tượng khác, giá trị nguyên thủy hoặc hàm.

Trình xây dựng trong JavaScript

Các hàm là các đối tượng thông thường (thực hiện [[Call]]theo thuật ngữ ECMA-262) với khả năng bổ sung có thể gọi được nhưng đóng vai trò khác trong JavaScript: chúng trở thành các hàm tạo ( nhà máy cho các đối tượng) nếu được gọi thông quanew toán tử. Do đó, các constructor là một dạng tương tự thô đối với các lớp trong các ngôn ngữ khác.

Mỗi hàm JavaScript thực sự là một thể hiện của Functionđối tượng hàm dựng sẵn có một thuộc tính đặc biệt có tên prototypeđược sử dụng để thực hiện các thuộc tính kế thừa và chia sẻ dựa trên nguyên mẫu. Mỗi đối tượng được tạo bởi một hàm xây dựng có một tham chiếu ngầm (được gọi là nguyên mẫu hoặc __proto__) đến giá trị của hàm tạo của nó prototype.

Hàm tạo prototypelà một loại bản thiết kế để xây dựng các đối tượng vì mọi đối tượng được tạo bởi hàm tạo đều thừa hưởng một tham chiếu đến nóprototype .

Chuỗi nguyên mẫu

Một đối tượng chỉ định nguyên mẫu của nó thông qua thuộc tính bên trong [[Prototype]]hoặc __proto__. Mối quan hệ nguyên mẫu giữa hai đối tượng là về sự kế thừa: mọi đối tượng có thể có một đối tượng khác làm nguyên mẫu. Nguyên mẫu có thể là nullgiá trị.

Chuỗi các đối tượng được kết nối bởi thuộc __proto__tính được gọi là chuỗi nguyên mẫu . Khi một tham chiếu được tạo cho một thuộc tính trong một đối tượng, thì tham chiếu đó là thuộc tính gặp phải trong đối tượng đầu tiên trong chuỗi nguyên mẫu có chứa một thuộc tính của tên đó. Chuỗi nguyên mẫu hoạt động như thể nó là một vật thể duy nhất.

Xem hình ảnh này (trích từ blog này ):

proto.jpg

Bất cứ khi nào bạn cố gắng truy cập một thuộc tính trong một đối tượng, JavaScript sẽ bắt đầu tìm kiếm nó trong đối tượng đó và tiếp tục với nguyên mẫu của nó, nguyên mẫu của nguyên mẫu và cứ thế cho đến khi gặp thuộc tính hoặc nếu __proto__giữ giá trị null.

Kiểu thừa kế này sử dụng chuỗi nguyên mẫu thường được gọi là ủy quyền để tránh nhầm lẫn với các ngôn ngữ khác sử dụng chuỗi lớp.

Hầu như tất cả các đối tượng là trường hợp của Object, bởi vì Object.prototypelà cuối cùng trong chuỗi nguyên mẫu của họ. Nhưng Object.prototypekhông phải là một ví dụ ObjectObject.prototype.__proto__giữ giá trị null.

Bạn cũng có thể tạo một đối tượng với nullnguyên mẫu như thế này:

var dict = Object.create(null);

Một đối tượng như là một bản đồ tốt hơn (từ điển) so với một đối tượng theo nghĩa đen, đó là lý do mô hình này đôi khi được gọi là dict mẫu ( dict cho từ điển).

Lưu ý: literal đối tượng được tạo sử dụng {}là các trường hợp Objectkể từ khi ({}).__proto__là một tham chiếu đến Object.prototype.


Vui lòng trích dẫn nguồn trích dẫn và hiện vật bạn đang sử dụng. Hình ảnh dường như đến từ giamir.com/pseudoclass-and-prototypal-inherribution-in-JS , bạn có bản quyền về nó không?
Bergi

@Bergi Tôi đã trích dẫn nguồn của hình ảnh. Hầu hết các trích dẫn tôi đã sử dụng đều được trích xuất từ ​​tiêu chuẩn JS hoặc MDN
eigenslacker
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.