2016-12-07 6 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

Я явно добавив свойство speak в d.prototype. Фактически, d1 и d2 оба имеют доступ к свойству/функции в своей иерархии прототипов. Тем не менее, когда я пытаюсь вызвать его на d, я получаю TypeError. Я пробовал разные «хитрости» безрезультатно. Я попробовал обернуть различные части в IIFE, пытаясь исключить любые проблемы, связанные с определением области обзора, но это не повлияло. Я удалил () из последней строки

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

и получить undefined. Это говорит мне, что speak не имеет свойства/функции, к которым имеет доступ d. то есть. Измените speak на blah и получите тот же самый undefined, записанный на консоль.

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

Я знаю, что мне не хватает чего-то малого, что может быть очевидно для нового набора глаз.

+1

'd.speak()' не будет работать, потому что вы присоединили 'Speak()' функцию к объекту прототипа 'd' функция, а не сама функция 'd'. Таким образом, вам нужно прикрепить функцию 'speak()' к 'd', как' d.speak = function() {} ', или вызвать' d.prototype.speak() ', чтобы вызвать его. –

+1

Интересно, что вы ожидали возвращения 'd.speak()'? – Oriol

+0

@Oriol, 'Привет, я не знаю. 'Было моим ожиданием. – ppovoski

ответ

3

Это, как ожидается. В большинстве случаев конструктор не является экземпляром самого себя. Конструктор определяет методы, доступные в экземплярах в своем prototype, но этот prototype не отображается в цепочке [[Prototype]] самого конструктора.

Причина проста:

  • Объект может иметь только один [[прототип]].

  • Конструктор - это функция, поэтому в цепи [[Prototype]] должно быть Function.prototype. Это позволяет вам вызвать методы функции, такие как call или apply на конструкторе.

  • Экземпляры должны наследовать от prototype конструктора, но не от Function.prototype, потому что они не являются функциями.

Следовательно, конструктор не может наследовать его prototype. Есть два встроенных исключения: Function и Object.

Таким образом, вы можете избавиться от второго или третьего инварианта выше. Тогда вы можете заставить его работать, но это плохая идея, потому что обычно люди полагаются на них, а изменение [[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

Смежные вопросы