Безопасна ли реализация OO JavaScript Джона Резига? - PullRequest
24 голосов
/ 29 сентября 2010

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

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

  • Является ли простая структура наследования Джона Ресига безопасной для производства?
  • Есть ли способ узнать, насколько хорошо он был протестирован?
  • Помимо Джус Какие еще варианты у меня есть для этой цели? Мне нужен тот, который прост в использовании, быстрый и надежный. Он также должен быть совместим с jQuery.

Ответы [ 3 ]

19 голосов
/ 29 сентября 2010

Ха. Для меня это выглядит намного сложнее, чем должно быть.

На самом деле, присматриваясь, я действительно возражаю против того, что он делает, предоставляя 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() {};

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

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

  2. наследуемый _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 его использования, как в Python. Вы можете добавить член _super в функцию конструктора, если хотите избежать повторного присвоения имени Person (так вы бы сказали Ninja._super.prototype._init.call или, возможно, Ninja._base._init.call).

5 голосов
/ 29 сентября 2010

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

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

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

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

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

5 голосов
/ 29 сентября 2010

JavaScript основан на прототипах, а не на классах.Я не рекомендую бороться с этим и объявлять подтипы способом JS:

MyDerivedObj.prototype = new MySuperObj();
MyDerivedObj.prototype.constructor = MyDerivedObj;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...