簡體   English   中英

JavaScript對象使用構造函數找到它的原型?

[英]A JavaScript object finds its prototype using constructor?

在“ 忍者的秘密”(2013年 ,第125頁)中,它說:

JavaScript中的每個對象都有一個名為constructor的隱式屬性,它引用用於創建對象的構造函數。 並且因為原型是構造函數的屬性,所以每個對象都有一種方法可以找到它的原型。

它實際上可能是我聽到的關於JavaScript的最有缺陷的事情之一,它來自一個所謂的JavaScript專家。 是不是真的

  1. 任何JavaScript對象“都有辦法使用內部[[prototype]]屬性找到它的原型”(如ECMA-262規范 ,第32頁)。 可以使用__proto__在Chrome和Firefox上訪問它,在最新版本的IE中可以使用Object.getPrototypeOf

  2. 任何對象通過在__proto__指向的原型對象中獲取它來獲取constructor屬性。 有時甚至沒有正確設置constructor屬性,因為某些JavaScript庫或框架根本不使用它。 constructor是原型對象的屬性,而不是對象本身的屬性:

(如Chrome的開發人員工具中所示):

> function Foo() {}
undefined

> var foo = new Foo()
undefined

> foo.hasOwnProperty("constructor")
false

> foo.__proto__.hasOwnProperty("constructor")
true

> foo.__proto__.constructor === Foo
true

上面的(1)和(2)是真的嗎? 什么是JavaScript中的“隱式屬性命名constructor ”,如引用文本中所示? 它試圖表示像[[prototype]]這樣的內部屬性嗎? 但更重要的是,我想知道上面的(1)和(2)是否屬實,而不是引用的文本是什么。

引用的文本非常准確,並且非常簡單地解釋了這種機制。

“JavaScript中的每個對象都有一個名為constructor的隱式屬性,它引用了用於創建對象的構造函數。”

這是絕對正確的,正如@Mathletics所指出的那樣:

foo.constructor === Foo // true

...“因為原型是構造函數的屬性,每個對象都有辦法找到它的原型。”

這也可以理解為讀作。 從構造函數中獲取原型是實例查找其原型的有效方法。

foo.constructor.prototype // Foo {}

並且

foo.constructor.prototype === foo.__proto__ // true

我認為這本書描述的方式是最合適的。 出於某種原因,“ __ proto __ ”屬性在每一側都以雙下划線命名。 正如您所指出的,它是一個內部屬性,雙下划線是一種廣泛使用的命名內部屬性的約定。 它與hasOwnProperty不是“可見的”。 不是特別因為它是一個內部屬性,而是因為它沒有直接設置在對象本身上。 這可能更好地解釋hasOwnPropery更清楚地意味着什么:

foo.a = 4;
foo.a; // 4
foo.hasOwnProperty("a"); // true
foo.constructor.prototype.b = 5;
foo.b; // 5
foo.hasOwnProperty("b"); // false

John Resig是錯的

是的,jQuery的作者也會犯錯誤。 這就是他所說的:

JavaScript中的每個對象都有一個名為constructor的隱式屬性,它引用用於創建對象的構造函數。 並且因為原型是構造函數的屬性,所以每個對象都有一種方法可以找到它的原型。

以下是他的陳述是錯誤的原因:

  1. 並非每個對象都有原型。 因此,這些對象也沒有任何隱式屬性。
  2. 並非每個具有原型的對象都是由構造函數創建的。 但是,這不包括對象,數組和正則表達式文字和函數,因為這些對象是由構造函數隱式創建的。
  3. 並非每個具有原型並且由構造函數創建的對象都具有名為constructor的隱式屬性。
  4. 並非每個具有原型的對象都是由構造函數創建的,並且具有名為constructor的隱式屬性,該屬性指向創建該對象的構造函數。
  5. 並非每個具有原型的對象都是由構造函數創建的,並且具有一個名為constructor的隱式屬性,該屬性指向創建該對象的構造函數,在其構造函數上具有一個名為prototype的屬性。
  6. 並非每個具有原型的對象都是由構造函數創建的,它具有一個名為constructor的隱式屬性,該屬性指向創建該對象的構造函數,並在其構造函數上具有一個名為prototype的屬性,該屬性指向該構造函數的原型。賓語。

讓我們通過例子來證明這些陳述,以證明John Resig的陳述是錯誤的。 由於所有這些語句都以斷言“並非每個對象”開頭,因此我們只需要找到每個語句的一個示例來證明John Resig的陳述是錯誤的。

聲明1的證明

Object.create方法可用於創建新對象並設置其內部[[prototype]]屬性。 因此,它可以用於創建一個沒有原型的對象:

var o = Object.create(null); // o has no prototype

上例中的對象沒有原型 - 它的內部[[prototype]]屬性設置為null 因此它也沒有任何隱含屬性

聲明2的證明

現在讓我們創建另一個從對象o繼承的對象p ,如下所示:

var p = Object.create(o); // the prototype of p is o

因此,對象p有一個原型,但它不是由構造函數創建的。

聲明3的證明

好吧讓我們從構造函數創建對象p (實際上這正是Object.create函數的實現方式):

function F() {}  // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F;   // p is an object which is constructed by F

這里對象p由構造函數F創建。 但是它沒有任何名為constructor隱式屬性。

聲明4的證明

如果為變量o分配了一個對象文字,然后將其用作構造函數Fprototype屬性,該怎么辦?

var o = {};      // object literals inherit from Object.prototype
function F() {}  // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F;   // p is an object which is constructed by F

現在,對象p有一個名為constructor的隱式屬性,但它指向的是Object而不是F 因此p.constructor.prototype指向Object.prototype而不是o

聲明證明5

也許您認為問題是繼承? 好吧,讓我們完全廢除繼承。 從頭開始:

var p = new F;      // p is constructed by F, it inherits from F.prototype
delete F.prototype; // devious isn't it? I love being naughty
function F() {}     // declarations are hoisted

好吧,現在對象p繼承自F.prototype ,它有一個名為constructor的隱式屬性,指向F本身。 但是,由於我們從F刪除了prototype屬性,因此無法通過p.constructor.prototype訪問p的原型(現在它將返回undefined )。

聲明證明6

讓我們稍微修改最后一個例子。 我們不會刪除F.prototype而是將其設置為其他內容。 例如:

var o = {};      // object literals inherit from Object.prototype
var p = new F;   // p is constructed by F, it inherits from F.prototype
F.prototype = o; // oops, what will happen now?
function F() {}  // declarations are hoisted

現在,對象p繼承自F.prototype ,它有一個名為constructor的隱式屬性,指向F本身。 然而,由於我們設置F.prototypeo當我們訪問p.constructor.prototype我們會得到o代替了原來的F.prototype

結論

正如你所看到的,John Resig的陳述是完全錯誤的。 我們不需要6個例子來證明這一點。 任何一個例子就足夠了。 但是我想表明他的陳述是多么錯誤。 因此,我寫下了我能想到的每一個可能的例子,反駁了他的陳述。

JavaScript是一種原型面向對象的編程語言,這意味着對象繼承自其他對象。 構造函數不是嚴格要求創建對象的。 然而,它們被賦予了不正當的重要性,因為不幸的是,這是原型繼承在JavaScript中的工作方式。

原型繼承的構造函數模式經常令人困惑和誤導。 此外,它隱藏了原型繼承的真正方式,它不使用構造函數(原型繼承的原型模式)。 要了解更多信息,請閱讀以下答案: https//stackoverflow.com/a/17008403/783743

我是如何學習它以及可以觀察到的

每個對象都有一個__proto__屬性,每個函數都有一個額外的prototype屬性。 prototype本身就是一個對象,並且有一個名為constructor的屬性,它指向原始函數:

function C(){}
console.log(C.hasOwnProperty("prototype")); //true
console.log(C.prototype.hasOwnProperty("constructor")); //true
console.log(C.prototype.constructor === C); //true

創建對象時,它的__proto__屬性設置為指向其構造函數的prototype屬性:

var c = new C();
console.log(c.__proto__ === C.prototype) //true

關於c是什么的所有信息,它是從__proto__屬性知道的。 只需手動更改__proto__屬性即可觀察到:

function D(){}
c.__proto__ = D.prototype;
console.log(c.constructor === D) //true
console.log(c instanceof D) //true

因此,考慮到所有這些,很難相信存在一個構造函數屬性,從中可以得到所有東西。 但是在內部構造函數屬性(即__constr__ )中仍然可能存在我們無法訪問但在請求原型時訪問的屬性。 至少我猜是可能的。

有一件事也讓我感到困惑,是:

console.log(c.hasOwnProperty("__proto__")) //false

我的猜測是__proto__只是沒有被hasOwnProperty方法抓住,但也許其他人可以對此有所了解。

小提琴

ECMA說什么

以下是ECMA規范的一些摘錄:

所有對象都有一個名為[[Prototype]]的內部屬性。 此屬性的值為null或對象,用於實現繼承。 本機對象是否可以將主機對象作為其[[Prototype]]取決於實現。 每個[[Prototype]]鏈必須具有有限長度(即,從任何對象開始,遞歸訪問[[Prototype]]內部屬性必須最終導致null值)。 為了獲取訪問權限,[[Prototype]]對象的命名數據屬性是繼承的(作為子對象的屬性可見),但不是put訪問權限。 為get訪問和put訪問繼承了命名的訪問者屬性

和:

8.12.2 [[GetProperty]](P)

當使用屬性名稱P調用O的[[GetProperty]]內部方法時,將執行以下步驟:

  1. 設prop是調用屬性名為P的O的[[GetOwnProperty]]內部方法的結果。

  2. 如果prop未定義,則返回prop。

  3. 讓proto成為O的[[Prototype]]內部屬性的值。

  4. 如果proto為null,則返回undefined。

  5. 返回用參數P調用proto的[[GetProperty]]內部方法的結果。

我的結論

ECMA明確指出,有一個內部原型屬性,如果JavaScript仍然首先訪問某些內部構造函數屬性,訪問已經存在的內容,那么它們就沒有意義。 此外,可以測試的所有內容都指向構造函數是原型屬性的想法,而不是相反。

所以我想可以肯定地說,這是它的工作原理。

我還重讀了Jon Resig的引文。 我認為他the prototype is a property of the constructor的屬性是我們可以直接訪問的原型屬性,並且每個函數都有。 他在這里也不是特別具體。 我想他只是想要一個簡單的解釋,並且不想讓人們立刻迷惑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM