Наследование JavaScript - instanceof не работает?

Я пишу простую платформенную игру, используя javascript и html5. Я использую javascript в манере OO. Чтобы получить наследование, я использую следующее:

// http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/
function copyPrototype(descendant, parent) {
    var sConstructor = parent.toString();
    var aMatch = sConstructor.match(/\s*function (.*)\(/);
    if (aMatch != null) { descendant.prototype[aMatch[1]] = parent; }
    for (var m in parent.prototype) {
        descendant.prototype[m] = parent.prototype[m];
    }
};

Для этого поста рассмотрим следующий пример:

function A() {
 this.Name = 'Class A'
}
A.prototype.PrintName = function () {
 alert(this.Name);
}

function B() {
 this.A();
}
copyPrototype(B, A);

function C() {
 this.B();
}
copyPrototype(C, B);

var instC = new C();

if (instC instanceof A)
  alert ('horray!');

Как я понимаю, я бы ожидал увидеть окно предупреждения об ужасах, потому что C является экземпляром C и B и A. Я ошибаюсь? Или я использую неправильный метод для проверки? Или имеет copyPrototype намотал экземпляр оператора?

Спасибо, как всегда за то, что нашли время, чтобы прочитать это!

Шоу.

Ответы

Ответ 1

В эти дни вам не нужно .prototype = new Thing(), я думаю, что опаздываю на вечеринку, но вы можете просто использовать Object.create на прототипе родителя, а затем переопределить интересующие вас методы в переопределении. Пример:

var IDataSource = function(){
    throw new Error("Not implemented, interface only");
};

IDataSource.prototype.getData = function(){
    throw new Error("Not implemented.");
};

var BasicDataSource = function(){};
BasicDataSource.prototype = Object.create(IDataSource.prototype);
BasicDataSource.prototype.getData = function(){
    //[do some stuff, get some real data, return it]
    return "bds data";
};

var MockDataSource = function(){};
MockDataSource.prototype = Object.create(IDataSource.prototype);
MockDataSource.prototype.getData = function(){
    //[DONT DO some stuff return mock json]
    return "mds data";
};

MockDataSource.prototype.getDataTwo = function(){
    //[DONT DO some stuff return mock json]
    return "mds data2";
};


var MockDataSource2 = function(){};
MockDataSource2.prototype = Object.create(MockDataSource.prototype);




var bds = new BasicDataSource();
console.log("bds is NOT MockDataSource:", bds instanceof MockDataSource);
console.log("bds is BasicDataSource:", bds instanceof BasicDataSource);
console.log("bds is an IDataSource:", bds instanceof IDataSource);
console.log("bds Data:", bds.getData());


var mds = new MockDataSource();
console.log("mds is MockDataSource:", mds instanceof MockDataSource);
console.log("mds is NOT a BasicDataSource:", mds instanceof BasicDataSource);
console.log("mds is an IDataSource:", mds instanceof IDataSource);
console.log("mds Data:", mds.getData());
console.log("mds Data2:",mds.getDataTwo());


var mds2 = new MockDataSource2();
console.log("mds2 is MockDataSource2:", mds2 instanceof MockDataSource2);
console.log("mds2 is MockDataSource:", mds2 instanceof MockDataSource);
console.log("mds2 is NOT a BasicDataSource:", mds2 instanceof BasicDataSource);
console.log("mds2 is an IDataSource:", mds2 instanceof IDataSource);
console.log("mds2 Data:", mds2.getData());
console.log("mds2 Data2:",mds2.getDataTwo());

Если вы запустите этот код в node, вы получите:

bds is NOT MockDataSource: false
bds is BasicDataSource: true
bds is an IDataSource: true
bds Data: bds data
mds is MockDataSource: true
mds is NOT a BasicDataSource: false
mds is an IDataSource: true
mds Data: mds data
mds Data2: mds data2
mds2 is MockDataSource2: true
mds2 is MockDataSource: true
mds2 is NOT a BasicDataSource: false
mds2 is an IDataSource: true
mds2 Data: mds data
mds2 Data2: mds data2

Не беспокойтесь о параметрах для конструкторов или о любом таком безумии.

Ответ 2

Проблема заключается в том, что функция copyPrototype только копирует свойства из прототипа конструкторов в другой, например, в конце, intenal [[Prototype]] ссылка C.prototype просто указывает на Object.prototype.

Цепочка прототипов прототипов instC и конструктора выглядит так:

                [[Prototype]]
    A.prototype -------------->|-------------------|
                               |                   |
    B.prototype -------------->|  Object.prototype | ---> null
                               |                   |
    C.prototype -------------->|-------------------|
        ^
        |
      instC

Оператор instanceof обходит цепочку прототипов, ваш объект instC, как вы можете видеть, будет иметь в своей цепочке прототипов только C.prototype и Object.prototype.

Вы можете добиться того, чего хотите, установив прототипы конструкторов как экземпляры объектов своих "родительских" конструкторов, например:

function A() {
  this.Name = 'Class A'
}

A.prototype.PrintName = function () {
  alert(this.Name);
}

function B() {
  //..
}
B.prototype = new A();
B.prototype.constructor = B; // fix constructor property


function C() {
  //..
}

C.prototype = new B();
C.prototype.constructor = C; // fix constructor property

var instC = new C();
if (instC instanceof A)
  alert('horray!');

Теперь цепочка прототипов объекта instC выглядит следующим образом:

           ---------------        ---------------        ---------------
 instC --> | C.prototype | -----> | B.prototype | -----> | A.prototype |
           ---------------        ---------------        ---------------
                                                                |
                                                                V
                                                       --------------------
                                                       | Object.prototype |
                                                       --------------------
                                                                |
                                                                V
                                                               null

Рекомендуемая статья:

Ответ 3

Хорошо. Я нашел решение, которое поддерживает работу функции instanceof, а также позволяет передавать параметры конструктора через цепочку наследования. Решение подробно описано здесь; https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Model - теперь моя структура классов выглядит следующим образом:

function A(p) {
 this.Position = p || new Vector2d(0,0);
}

function B(p) {
 this.base = A;
 this.base(p);
}
B.prototype = new A;

function C(p) {
 this.base = B;
 this.base(p);
}
C.prototype = new B;

if(C instanceof A)
  alert (' it worked!! '); // you now see this alert box!

Спасибо CMS за то, что вы подчеркнули, почему это не работает!

Вы можете проверить полный проект (хорошо старая сборка, которая на момент написания еще не увидела, что этот новый метод OO поместился полностью) вверх на http://8weekgame.shawson.co.uk/ - просто проверьте мои последние сообщения.

Ответ 4

Недавно была найдена хорошая реализация OO javascript от John Resig (The jQuery Guy!), которую я буду использовать для будущих проектов; http://ejohn.org/blog/simple-javascript-inheritance/