[英]A JavaScript object finds its prototype using constructor?
在“ 忍者的秘密”(2013年 ,第125頁)中,它說:
JavaScript中的每個對象都有一個名為
constructor
的隱式屬性,它引用用於創建對象的構造函數。 並且因為原型是構造函數的屬性,所以每個對象都有一種方法可以找到它的原型。
它實際上可能是我聽到的關於JavaScript的最有缺陷的事情之一,它來自一個所謂的JavaScript專家。 是不是真的
任何JavaScript對象“都有辦法使用內部[[prototype]]
屬性找到它的原型”(如ECMA-262規范 ,第32頁)。 可以使用__proto__
在Chrome和Firefox上訪問它,在最新版本的IE中可以使用Object.getPrototypeOf
任何對象通過在__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
是的,jQuery的作者也會犯錯誤。 這就是他所說的:
JavaScript中的每個對象都有一個名為
constructor
的隱式屬性,它引用用於創建對象的構造函數。 並且因為原型是構造函數的屬性,所以每個對象都有一種方法可以找到它的原型。
以下是他的陳述是錯誤的原因:
constructor
的隱式屬性。 constructor
的隱式屬性,該屬性指向創建該對象的構造函數。 constructor
的隱式屬性,該屬性指向創建該對象的構造函數,在其構造函數上具有一個名為prototype
的屬性。 constructor
的隱式屬性,該屬性指向創建該對象的構造函數,並在其構造函數上具有一個名為prototype
的屬性,該屬性指向該構造函數的原型。賓語。 讓我們通過例子來證明這些陳述,以證明John Resig的陳述是錯誤的。 由於所有這些語句都以斷言“並非每個對象”開頭,因此我們只需要找到每個語句的一個示例來證明John Resig的陳述是錯誤的。
Object.create
方法可用於創建新對象並設置其內部[[prototype]]
屬性。 因此,它可以用於創建一個沒有原型的對象:
var o = Object.create(null); // o has no prototype
上例中的對象沒有原型 - 它的內部[[prototype]]
屬性設置為null
。 因此它也沒有任何隱含屬性 。
現在讓我們創建另一個從對象o
繼承的對象p
,如下所示:
var p = Object.create(o); // the prototype of p is o
因此,對象p
有一個原型,但它不是由構造函數創建的。
好吧讓我們從構造函數創建對象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
隱式屬性。
如果為變量o
分配了一個對象文字,然后將其用作構造函數F
的prototype
屬性,該怎么辦?
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
。
也許您認為問題是繼承? 好吧,讓我們完全廢除繼承。 從頭開始:
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
)。
讓我們稍微修改最后一個例子。 我們不會刪除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.prototype
到o
當我們訪問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規范的一些摘錄:
所有對象都有一個名為[[Prototype]]的內部屬性。 此屬性的值為null或對象,用於實現繼承。 本機對象是否可以將主機對象作為其[[Prototype]]取決於實現。 每個[[Prototype]]鏈必須具有有限長度(即,從任何對象開始,遞歸訪問[[Prototype]]內部屬性必須最終導致null值)。 為了獲取訪問權限,[[Prototype]]對象的命名數據屬性是繼承的(作為子對象的屬性可見),但不是put訪問權限。 為get訪問和put訪問繼承了命名的訪問者屬性
和:
8.12.2 [[GetProperty]](P)
當使用屬性名稱P調用O的[[GetProperty]]內部方法時,將執行以下步驟:
設prop是調用屬性名為P的O的[[GetOwnProperty]]內部方法的結果。
如果prop未定義,則返回prop。
讓proto成為O的[[Prototype]]內部屬性的值。
如果proto為null,則返回undefined。
返回用參數P調用proto的[[GetProperty]]內部方法的結果。
ECMA明確指出,有一個內部原型屬性,如果JavaScript仍然首先訪問某些內部構造函數屬性,訪問已經存在的內容,那么它們就沒有意義。 此外,可以測試的所有內容都指向構造函數是原型屬性的想法,而不是相反。
所以我想可以肯定地說,這是它的工作原理。
我還重讀了Jon Resig的引文。 我認為他the prototype is a property of the constructor
的屬性是我們可以直接訪問的原型屬性,並且每個函數都有。 他在這里也不是特別具體。 我想他只是想要一個簡單的解釋,並且不想讓人們立刻迷惑。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.