2016-12-07 75 views
1

我很困惑,爲什麼我在這裏得到這個錯誤。我想要一些啓發。JavaScript對象原型似乎破碎

function c(me) { this.me = me; } 
 
c.prototype.identify = function() { return "I am " + this.me; }; 
 

 
function d(me) { c.call(this, me); } 
 
d.prototype = Object.create(c.prototype); 
 
d.prototype.speak = function() { return "Hello, " + this.identify() + "." }; 
 

 
var d1 = new d("d1"); 
 
var d2 = new d("d2"); 
 

 
console.log(d1.speak()); 
 
console.log(d2.speak()); 
 

 
console.log(d.speak()); //returns: TypeError: d.speak is not a function

returns: TypeError: d.speak is not a function

我明確地將屬性添加到speakd.prototype。實際上,d1d2都可以訪問其原型層次結構中的屬性/函數。但是當我試圖在d上調用它時,我得到一個TypeError。我嘗試了各種「調整」,無濟於事。爲了排除任何範圍問題,我嘗試在IIFE中包裝各種部分,但它沒有效果。我從最後一行

console.log(d.speak); //returns: undefined 

,並得到undefined刪除()。這告訴我speak不是d有權訪問的屬性/函數。即。將speak更改爲blah,並獲取登錄到控制檯的相同undefined

console.log(d.blah); //returns: undefined 

我知道我錯過了一些可能對一組新的眼睛很明顯的小東西。

+1

'd.speak()'不會工作,因爲你已附上'說話()'函數到'D'的原型對象函數,而不是'd'函數本身。因此,無論是需要將'speak()'函數附加到'd'本身,比如'd.speak = function(){}'或調用'd.prototype.speak()'來調用它。 –

+1

我不知道你期望'd.speak()'返回什麼結果? – Oriol

+0

@Oriol,'你好,我沒有定義。'是我的期望。 – ppovoski

回答

3

這是預期。在大多數情況下,構造函數不是它自己的一個實例。構造函數定義了在其實例prototype中可用的方法,但是這個prototype沒有出現在構造函數本身的[[Prototype]]鏈中。

理由很簡單:

  • 一個目的只能有單一的[[Prototype]]。

  • 構造函數是一個函數,所以在[[Prototype]]鏈中必須有Function.prototype。這允許您在構造函數上調用函數方法,如callapply

  • 實例必須從構造函數的prototype繼承,但不能從Function.prototype繼承,因爲它們不是函數。

因此,通常構造函數不能從它的prototype繼承。有兩個內置的例外:FunctionObject

也就是說,你可以選擇擺脫上面的第二個或第三個不變量。然後你可以使它工作,但這是一個壞主意,因爲通常人們依賴它們,而改變[[Prototype]]的性能很差。

function c(me) { this.me = me; } 
 
c.prototype.identify = function() { return "I am " + this.me; }; 
 

 
function d(me) { c.call(this, me); } 
 
d.prototype = Object.create(c.prototype); 
 
d.prototype.speak = function() { return "Hello, " + this.identify() + "." }; 
 
Object.setPrototypeOf(d, d.prototype); 
 

 
var d1 = new d("d1"); 
 
var d2 = new d("d2"); 
 

 
console.log(d1.speak()); // "Hello, I am d1." 
 
console.log(d2.speak()); // "Hello, I am d2." 
 
console.log(d.speak()); // "Hello, I am undefined." 
 

 
console.log(d.call); // undefined :(

function c(me) { this.me = me; } 
 
c.prototype = Object.create(Function.prototype); 
 
c.prototype.identify = function() { return "I am " + this.me; }; 
 

 
function d(me) { c.call(this, me); } 
 
d.prototype = Object.create(c.prototype); 
 
d.prototype.speak = function() { return "Hello, " + this.identify() + "." }; 
 
Object.setPrototypeOf(d, d.prototype); 
 

 
var d1 = new d("d1"); 
 
var d2 = new d("d2"); 
 

 
console.log(d1.speak()); // "Hello, I am d1." 
 
console.log(d2.speak()); // "Hello, I am d2." 
 
console.log(d.speak()); // "Hello, I am undefined." 
 

 
console.log(d1 instanceof Function); // true Huh????

+0

+1,但你應該在「*選擇去除不變量*」之後添加一個免責聲明,這是一個非常糟糕的主意:-) – Bergi

+0

@Bergi當然!我想警告但忘記了,謝謝。 – Oriol

+0

釘釘!幹得不錯! – ppovoski

1

您的變量d是一個構造函數函數。使用該構造函數上的prototype屬性來訪問您的實例從中繼承的原型對象

console.log(d.prototype.speak) //=> [Function] 

演示:

function c(me) { this.me = me; } 
 
c.prototype.identify = function() { return "I am " + this.me; }; 
 

 
function d(me) { c.call(this, me); } 
 
d.prototype = Object.create(c.prototype); 
 
d.prototype.speak = function() { return "Hello, " + this.identify() + "." }; 
 

 
var d1 = new d("d1"); 
 
var d2 = new d("d2"); 
 

 
console.log(d1.speak()); 
 
console.log(d2.speak()); 
 

 
console.log(d.prototype.speak) //=> [Function]

+0

所以你是說用於該函數的變量'd'與用於原型的'd'不一樣? – ppovoski

+1

函數'd'從未用於原型,只作爲構造函數。 JavaScript中的每個函數都有自己的原型對象; 'd1'和'd2'的原型都是'd.prototype'。另一種思考方式:每次調用'new d'時,真正發生的事情是'Object.create(d.prototype)'。 – gyre