Непонятное поведение наследования JavaScript - PullRequest
2 голосов
/ 09 августа 2010
<disclaimer> 
What follows is the fruits of a thought experiment. What I'm doing 
isn't the issue; the symptoms are. Thank you.
</disclaimer>

Наконец-то я сосредоточился на конструкторах, прототипах и наследовании прототипов в JavaScript.Но метод SomethingSpectactular в приведенном ниже примере дает мне сообщение:

function FinalClass() {    
    return {
        FinalFunction : function() { return "FinalFunction"; },
        TypeName : "FinalClass",
        SomethingSpectacular : function() { 
            return FinalClass.prototype.SubFunction.call(this);
        }
    }
}
FinalClass.prototype = new SubClass();
FinalClass.constructor = FinalClass;

var f = new FinalClass();

Причины, по которым меня это беспокоит:

  1. JavaScript , по-видимому, не сканирует прототипцепочка для методов так же, как и для свойств.То есть f.SubFunction() генерирует ошибку.
  2. Чтобы перейти к методу на прототипе, вам нужно выполнять как минимум 3 операции с точками каждый раз, когда вы хотите это сделать.FinalClass DOT прототип DOT Subfunctino DOT вызова.Вы понимаете.
  3. Методы базового класса (прототипа) не отображаются в Intellisense так, как я ожидал.Это очень раздражает.

Итак, мысленный эксперимент состоял в том, чтобы определить, что произойдет, если я напишу версию inherits, которая вставит функции-заглушки в подкласс, который делегировал вам прототип.Например, он автоматически создаст следующую функцию и добавит ее в FinalClass:

function SubFunction() { SubClass.prototype.SubFunction.call(this); }

Теперь у меня есть почти все, что намечено и работает.Метод inherits, который является расширением как Object.prototype, так и Function.prototype, принимает Function в качестве единственного аргумента.Это базовый класс.Подкласс определяется путем анализа Object.prototype.inherits.caller.

. После этого я устанавливаю prototype и constructor для подкласса, а затем начинаю анализировать методы нового прототипа подкласса.Я создаю массив, содержащий методы как для прототипа, так и для открытого интерфейса базового класса.Конечным результатом является хороший массив, который содержит методы, предоставляемые либо через прототип, либо с помощью оператора return конструктора базового класса.

Как только я это получу, я начну искать каждый метод в подклассе.Если его там нет, я добавляю его в подкласс с тем же именем и подписью.Однако тело метода просто перенаправляет вызов в базовый класс.

Теперь я могу пройтись по всему этому коду, и он работает чудесно, пока я не создаю экземпляров подклассы.Вот когда все становится не так.Вот что я наблюдал, используя Visual Studio 2008 (SP1) и Internet Explorer 8:

  1. До создания экземпляра BaseClass не предоставляет открытых методов.Этого следовало ожидать.Методы, предоставляемые с помощью оператора return конструктора класса, не появятся, пока не будут созданы его экземпляры.У меня все хорошо.
  2. До создания экземпляра SubClass предоставляет методы из BaseClass.Это именно то, что я ожидал.
  3. Как только я объявляю экземпляр BaseClass, в нем есть все члены, которых я ожидаю.Он имеет свои методы typeName и BaseFunction.
  4. Как только я объявляю экземпляр SubClass, он имеет только те методы, которые возвращаются оператором return его конструктора.Нет членов из базового класса нет.Похоже, что вся работа, выполненная в методе наследования для сопоставления методов базового класса с подклассом, была потеряна.

Большая загадка для меня здесь - это исчезновение методов, которые я добавил в подклассво время исполнения наследства.Во время его выполнения я ясно вижу, что SubClass изменяется, и что функции BaseClass распространяются.Но в тот момент, когда я создаю набор SubClass, этой информации больше нет.

Я предполагаю, что это как-то связано с конструктором, порядком событий или чем-то еще, чего я просто не вижу.

Заключительное примечание: Этот проект возник как попытка понять сложности JavaScript и то, как работает его система наследования прототипов.Я знаю, что существуют библиотеки.Я знаю, что заново изобретаю колесо.Я изобретаю это специально.Иногда лучший способ понять вещь - это построить ее самостоятельно.Это уже был огромный опыт обучения, но я просто озадачен этим конкретным моментом.

КОД

sti.objects.inherits = function inherits(baseClass) {

    var subClass = sti.objects.inherits.caller;

    var baseClassName = sti.objects.getTypeName(baseClass);
    var methods = sti.objects.getMethods(baseClass); 

    if(!sti.objects.isDefined(baseClass.typeName))
        baseClass.typeName = baseClassName;

    var subClass = sti.objects.inherits.caller;
    var subClassName = sti.objects.getTypeName(subClass);

    var temp = function() {};
    temp.prototype = new baseClass();

    subClass.prototype = temp.prototype;
    subClass.constructor = subClass;
    subClass.typeName = subClassName;
    subClass.baseClass = baseClass.prototype;   //  Shortcut to the prototype's methods
    subClass.base = baseClass;  //  Cache the constructor

    for(var index = 0; index < methods.items.length; index++) {
        var resource = methods.items[index].getFunction();
        var methodName = methods.items[index].getName();
        if(methodName != "" && ! sti.objects.isDefined(subClass[methodName])) {
            var method = sti.objects.createOverride(baseClassName, resource);
            subClass[methodName] = method;
            if(typeof subClass.prototype[methodName] == "undefined") {
                subClass.prototype[methodName] = method;
            }
        }
    }
}

Object.prototype.inherits = sti.objects.inherits;
Function.prototype.inherits = sti.objects.inherits;

function BaseClass() {
    return { 
        A : function A() {return "A";} 
    };
}


function SubClass() {
    inherits(BaseClass);

    return {
        B : function B() { return "B"; }
    }    
}

var b = new BaseClass();
var s = new SubClass();

1 Ответ

4 голосов
/ 09 августа 2010

Ваши конструкторы не работают как конструкторы. Когда вы используете новое ключевое слово, Javascript создает новый объект и ожидает, что ваш конструктор заполнит его членами и т. Д. Ваши конструкторы возвращают другой новый объект, не связанный с объектом, который связан с использованием конструктора, поэтому прототип не работает.

Измените конструктор на что-то вроде

function FinalClass() {
    this.FinalFunction = function() { return "FinalFunction"; };
    this.TypeName = "FinalClass";
    this.SomethingSpectacular = function() { 
        return FinalClass.prototype.SubFunction.call(this);
    };
}

и вы должны увидеть ожидаемое наследование. Это связано с тем, что метод FinalClass в этом случае изменяет контекстный объект, созданный с помощью механизма «new». Ваш оригинальный метод создает другой объект, который не имеет типа FinalClass.

...