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

Является ли безопасность Джона Ресига OO внедрением JavaScript безопасным?

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

Я пытаюсь ответить на следующие вопросы.

4b9b3361

Ответ 1

Да. Это выглядит намного сложнее, чем мне нужно.

На самом деле, более внимательно, я действительно делаю исключение из того, что он делает, предоставляя this._super() в то время как метод, чтобы вызвать метод суперкласса.

Код вводит зависимость от typeof==='function' (ненадежный для некоторых объектов), Function#toString (argh, декомпозиция функции также ненадежна) и решая, следует ли обертывать, основываясь на том, использовалась ли вы последовательность байтов _super в теле функции (даже если вы использовали его только в строке, и если вы попробуете, например. this['_'+'super'], это не удастся).

И если вы сохраняете свойства в своих объектах функций (например, MyClass.myFunction.SOME_PRIVATE_CONSTANT, которые вы можете сделать, чтобы сохранить пространства имен чистыми), обертка остановит вас от получения этих свойств. И если исключение выбрано в методе и поймано в другом методе того же объекта, _super в конечном итоге укажет на неправильную вещь.

Все это просто для того, чтобы упростить вызов своего суперклассного метода. Но я не думаю, что это особенно сложно сделать в JS. Он слишком умен для своего же блага и в процессе делает его менее надежным. (Ох и arguments.callee недействительны в строгом режиме, хотя это и не его вина, поскольку это произошло после того, как он опубликовал его.)

Вот то, что я использую для классов на данный момент. Я не утверждаю, что это "лучшая" система класса JS, потому что есть множество различных способов сделать это и множество различных функций, которые вы можете добавить или не добавить. Но он очень легкий и нацелен на то, чтобы быть JavaScriptic, если это слово. (Это не так.)

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw 'Constructor function requires new operator';
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    if (this!==Object) {
        Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
        Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    }
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

Он обеспечивает:

  • защита от случайного отсутствия new. Альтернативой является тихое перенаправление X() на new X(), поэтому отсутствует new. Это лучший результат; Я пошел на явную ошибку, так что вы не привыкли писать без new и вызывают проблемы с другими объектами, не определенными так. В любом случае лучше, чем неприемлемое значение JS, позволяющее свойствам this. выпадать на window и таинственным образом будет ошибочным позже.

  • наследуемый метод _init, поэтому вам не нужно писать конструктор-функцию, которая ничего не делает, кроме вызова функции конструктора суперкласса.

и это действительно все.

Здесь вы можете использовать его для реализации примера Resig:

var Person= Object.makeSubclass();
Person.prototype._init= function(isDancing) {
    this.dancing= isDancing;
};
Person.prototype.dance= function() {
    return this.dancing;
};

var Ninja = Person.makeSubclass();
Ninja.prototype._init= function() {
    Person.prototype._init.call(this, false);
};
Ninja.prototype.swingSword= function() {
    return true;
};

var p= new Person(true);
p.dance(); // => true

var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true

// Should all be true
p instanceof Person &&
n instanceof Ninja && n instanceof Person

Вызов суперкласса выполняется путем конкретного наименования метода, который вы хотите, и call ing, немного похожего на Python. Вы могли бы добавить элемент _super к функции конструктора, если хотите избежать повторного именования Person (так что вы скажете Ninja._super.prototype._init.call или, возможно, Ninja._base._init.call).

Ответ 2

JavaScript основан на прототипе, а не на классе. Моя рекомендация - не бороться с ним и объявлять подтипы способом JS:

MyDerivedObj.prototype = new MySuperObj();
MyDerivedObj.prototype.constructor = MyDerivedObj;

Ответ 3

Посмотрите, как далеко вы можете получить, не используя наследование вообще. Относитесь к нему как к взлому производительности (для неохотно применяемого там, где это действительно необходимо), а не к принципу дизайна.

В высокодинамичном языке, таком как JS, редко бывает необходимо знать, является ли объект Person. Вам просто нужно знать, имеет ли он свойство firstName или метод eatFood. Вы не часто должны знать, является ли объект массивом; если он имеет свойство длины и некоторые другие свойства, названные после целых чисел, которые обычно достаточно хороши (например, объект Arguments). "Если он ходит, как утка и шарлатанцы, как утка, это утка".

// give back a duck
return { 
  walk: function() { ... }, 
  quack: function() { ... } 
};

Да, если вы создаете очень большое количество мелких объектов, каждый из которых имеет десятки методов, то, во всяком случае, назначает эти методы прототипу, чтобы избежать накладных расходов на создание десятков слотов в каждом экземпляре. Но относитесь к этому как к способу сокращения накладных расходов памяти - просто оптимизации. И сделайте своим пользователям пользу, скрывая ваше использование new за какой-то функцией factory, поэтому им даже не нужно знать, как создается объект. Им просто нужно знать, что у него есть метод foo или свойство bar.

(И обратите внимание, что вы действительно не будете моделировать классическое наследование в этом сценарии. Это просто эквивалент определения одного класса для повышения эффективности общей таблицы vtable.)