Подтвердить что ты не робот

Наследование прототипа Крокфорда - Использование

Я создаю небольшую структуру JS для использования на своей работе, и я бы хотел использовать прототипные образцы наследования Дугласа Крокфорда. Я думаю, что я получаю общее представление о том, как работает объект-прототип, но неясно, как использовать этот шаблон за простейшим примером.

Я разберусь до такой степени, что я это понимаю.

(function () {

    'use strict';

    var Vehicles = {};

    Vehicles.Vehicle = function () {
        this.go = function () {
            //go forwards
        };

        this.stop = function () {
            //stop
        };
    };

    Vehicles.Airplane = Object.create(Vehicles.Vehicle());

}());

Итак, теперь мои объекты Vehicle.Airplane могут идти() и останавливать(), но я хочу больше. Я хочу добавить к этому объекту методы takeOff() и land(). Я мог бы просто использовать уродливую нотацию:

Vehicles.Airplane.takeOff = function () {
    //take off stuff
}

Но это кажется неправильным, особенно если я должен добавить много методов или свойств. Вопрос заданный здесь, похоже, очень похож на мой, но ответ не совсем верен для меня. Ответ предполагает, что перед использованием Object.create я должен построить литерал объекта и передать этот литерал объекта в метод create. Однако в приведенном примерном коде выглядит, что их новый объект вообще ничего не наследует.

Я надеюсь, что это синтаксис, похожий на:

Vehicles.Airplane = Object.create(Vehicles.Vehicle({
    this.takeOff = function () {
        //takeOff stuff
    };
    this.land = function () {
        //land stuff
    };
}));

Я знаю, что этот синтаксис будет сильно нарушен с Object.create прямо сейчас, потому что, конечно, я пропускаю Vehicle.Vehicle как функцию, а не литерал объекта. Это не важно. Мне интересно, каким образом я должен строить новые свойства в объект, который наследуется от другого, не указывая их по очереди за точечной нотацией после факта.


EDIT:

Берги, после некоторой мучительной мысли по этой теме, я думаю, что я действительно хочу пойти с тем, что вы описали как "Классический паттерн". Вот мой первый удар в нем (теперь с фактическими фрагментами кода, а не с фальсификацией гипотез - вы даже можете увидеть мои загроможденные заглушки):
CS.Button = function (o) {
    o = o || {};

    function init(self) {
        self.domNode = dce('a');
        self.text = o.text || '';
        self.displayType = 'inline-block';
        self.disabled = o.disabled || false;

        self.domNode.appendChild(ctn(self.text));
        if (o.handler) {
            self.addListener('click', function () {
                o.handler(self);
            });
        }
    }

    this.setText = function (newText) {
        if (this.domNode.firstChild) {
            this.domNode.removeChild(this.domNode.firstChild);
        }
        this.domNode.appendChild(ctn(newText));
    };

    init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
    constructor: {value: CS.Button, configurable: true}
});

CS.Displayable = function (o) { // o = CS Object
    o = o || {};

    var f = Object.create(new CS.Element(o));

    function init(self) {
        if (!self.domAnchor) {
            self.domAnchor = self.domNode;
        }
        if (self.renderTo) {
            self.renderTo.appendChild(self.domAnchor);
        }
    }

    //Public Methods
    this.addClass = function (newClass) {
        if (typeof newClass === 'string') {
            this.domNode.className += ' ' + newClass;
        }
    };
    this.addListener = function (event, func, capture) {
        if (this.domNode.addEventListener) {
            this.domNode.addEventListener(event, func, capture);
        } else if (this.domNode.attachEvent) {
            this.domNode.attachEvent('on' + event, func);
        }
    };
    this.blur = function () {
        this.domNode.blur();
    };

    this.disable = function () {
        this.disabled = true;
    };

    this.enable = function () {
        this.disabled = false;
    };

    this.focus = function () {
        this.domNode.focus();
    };

    this.getHeight = function () {
        return this.domNode.offsetHeight;
    };

    this.getWidth = function () {
        return this.domNode.offsetWidth;
    };

    this.hide = function () {
        this.domNode.style.display = 'none';
    };

    this.isDisabled = function () {
        return this.disabled;
    };

    this.removeClass = function (classToRemove) {
        var classArray = this.domNode.className.split(' ');
        classArray.splice(classArray.indexOf(classToRemove), 1);
        this.domNode.className = classArray.join(' ');
    };

    this.removeListener = function () {
        //Remove DOM element listener
    };

    this.show = function () {
        this.domNode.style.display = this.displayType;
    };

    init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
    constructor: {value: CS.Displayable, configurable: true}
});

Я должен быть предельно ясным и сказать, что он еще не совсем работает, но в основном я бы хотел, чтобы ваше мнение о том, действительно ли я на правильном пути. Вы упомянули "свойства и методы, специфичные для экземпляра" в комментарии в вашем примере. Означает ли это, что мой метод this.setText и другие ошибочно размещены и не будут доступны для потомков в цепочке прототипов?

Кроме того, при использовании кажется, что теперь имеет смысл порядок объявления (я не могу получить доступ к CS.Displayable.prototype, потому что (я думаю) CS.Button указан первым, а CS.Displayable - undefined at время, когда я пытаюсь ссылаться на него). Это что-то, что мне нужно, чтобы справиться и разобраться (поместить вещи в порядок родословной в коде, а не в алфавитном порядке OCD), или есть что-то, что я тоже вижу там?

4b9b3361

Ответ 1

Vehicles.Airplane = Object.create(Vehicles.Vehicle());

Эта строка неверна. Кажется, вы хотите использовать new Vehicles.Vehicle - никогда не вызывать конструктор без new!

Тем не менее, я не уверен, какой шаблон вы хотите использовать. Мне приходят в голову два:

Классический паттерн

Вы используете функции конструктора, как в стандартном JS. Наследование выполняется путем наследования объектов-прототипов друг от друга и применения родительского конструктора в дочерних экземплярах. Ваш код должен выглядеть следующим образом:

Vehicles.Vehicle = function () {
    // instance-specific properties and methods,
    // initialising
}
Vehicles.Vehicle.prototype.go = function () {
     //go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
    //stop
};

Vehicles.Airplane = function() {
    // Vehicles.Vehicle.apply(this, arguments);
    // not needed here as "Vehicle" is empty

    // maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
    constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property

Vehicles.Airplane.prototype.takeOff = function () {
   //take off stuff
};

// usage:
var airplane = new Vehicles.Airplace(params);

Чистый прототипический шаблон

Вы используете простые объекты вместо функций-конструкторов - без инициализации. Чтобы создавать экземпляры и настраивать наследование, используется только Object.create. Это похоже на наличие только объектов-прототипов и пустых конструкторов. instancof здесь не работает. Код будет выглядеть так:

Vehicles.Vehicle = {
    go: function () {
         //go forwards
    },
    stop: function () {
         //stop
    }
}; // just an object literal

Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
   //take off stuff
};

// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me

Ответ 2

У вас Object.create неправильный. Первым аргументом должен быть объект (возможно, поэтому люди предложили вам передать литерал).

В вашем первом примере вы фактически проходите undefined:

Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
                                                       // return undefined

Следующие будут работать, но это не очень Crockford-ish:

Vehicles.Airplane = Object.create(new Vehicles.Vehicle());

Как я полагаю, Крокфорд сделал бы это (или, по крайней мере, не стал бы жаловаться):

var Vehicles = {};

Vehicles.Vehicle = {
    go : function() {
        // go stuff
    },
    stop : function() {
        // go stuff
    }
};

Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
    takeOff : { 
        value : function() {
            // take-off stuff
        }
    },
    land : {
        value: function() {
            // land stuff
        }
    }
});

Обратите внимание, что Vehicles.Vehicle является просто литералом, который будет использоваться в качестве прототипа для других объектов. Когда мы вызываем Object.create, мы пропускаем Vehicles.Vehicle в качестве прототипа, а takeOff и land будут собственными свойствами Vehicles.Airplane. Затем вы можете вызвать Object.create снова, передав Vehicles.Airplane в качестве прототипа, если вы хотите создать, например. Boeing.

Собственные свойства, переданные как второй параметр, упаковываются в объект, который содержит представление своих дескрипторов свойств . Внешние ключи - это имена ваших свойств/методов, и каждый указывает на другой объект, содержащий фактическую реализацию, как value. Вы также можете включить другие клавиши, такие как enumerable; если вы этого не сделаете, они будут принимать значения по умолчанию. Подробнее о дескрипторах можно прочитать на странице MDN о Object.defineProperty.