Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Модификация прототипа «по требованию» — плюсы, минусы, подводные камни (https://javascript.ru/forum/misc/44261-modifikaciya-prototipa-po-trebovaniyu-%E2%80%94-plyusy-minusy-podvodnye-kamni.html)

Antonius 13.01.2014 02:45

Модификация прототипа «по требованию» — плюсы, минусы, подводные камни
 
Решаю такую задачу.

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

Сейчас я это делаю так:
MyObj.prototype.method = function() {
    if (typeof this._data != 'object') {
        this.constructor.prototype._data = {};
        // инициализация данных
    }

    // обращение к данным
};


Но ввиду неопытности и плохого понимания особенностей работы js хочу спросить — не стреляю ли я себе в ногу?

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

Меня смущает ситуация, если пользователь скрипта будет создавать свои объекты, наследуясь от моего. Тут, похоже, этот подход сломается. Если базовый класс уже проинициализировал данные — все хорошо, метод увидит, что они есть в цепочке прототипов, и успокоится. Но если метод будет вызван у класса-потомка, то данные будут инициализированы именно у него, а класс-предок о них ничего не узнает, верно? Можно, конечно, не заморачиваться по этому поводу, но если есть способ сделать надежнее — подскажите, пожалуйста.

А может я вообще неправильно подхожу к проблеме, и есть уже существующие проверенные способы добиться того, что мне нужно. Буду благодарен за разъяснения или ссылки.

Спасибо.

Antonius 13.01.2014 16:29

Пока склоняюсь вместо `this.constructor` использовать все-таки имя конструктора. Вроде неприятностей создать не должно, а проблему при наследовании решает.

Яростный Меч 13.01.2014 17:24

Цитата:

Сообщение от Antonius
Но если метод будет вызван у класса-потомка, то данные будут инициализированы именно у него, а класс-предок о них ничего не узнает, верно?

this._data увидит значение в любом из прототипов цепочки.

Тут другой момент. Сначала надо разобраться, действительно ли _data должна быть в прототипе. Ведь в этом случае оно будет общее для всех экземпляров. Если это неправильно, то лучше присваивать в объект, а не в прототип: this._data = {}; - это будет работать одинаково для класса-предка и класса-потомка (я говорю о типе объекта).

Ну а если _data и вправду должна быть общей, то разница в работе будет: при вызове метода для объекта класса-предка создастся общая _data для всех, а в случае класса-потомка - только для классов-потомков, предки потом могут создать свою _data. Потому в этом случае правильнее явно указать имя класса:
MyObj.prototype._data = {};

Antonius 13.01.2014 19:39

Да, я тоже в итоге пришел к мнению, что лучше указывать имя класса (конструктора) явно, при рефакторинге вряд-ли оно потеряется и не будет переименовано, не буду раньше времени об этом беспокоиться.

Ситуация в целом такая: в прототипе в одном из полей есть объект с парами ключ-значение, которые представляют собой что-то вроде словаря, этот объект задан литералом и меняться, скорее всего, не будет (не могу придумать ситуацию, когда это понадобится). Типичное использование — взятие значения по ключу.

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

Поэтому и хочу сохранять данные в прототипе корневого класса — они общие для всех объектов этого типа. Если потомку понадобится модифицировать эти данные — он вполне сможет создать их копию у себя и модифицировать ее как угодно.

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

Вообще, такой подход как у меня имеет право на жизнь?

nerv_ 13.01.2014 21:30

Цитата:

Сообщение от Antonius
Есть относительно большая структура данных, к которой происходит обращение из одного метода объекта. Поскольку обращений при использовании может не быть вовсе, не хочется инициализировать структуру просто так. Хочется инициализировать при первом вызове метода. При этом хочется, чтобы все созданные впоследствие объекты этого типа уже могли использовать эти данные.

синглтон / singleton
и можешь хранить свой большой объект где угодно и обращаться как захочешь

Antonius 13.01.2014 21:41

Спасибо, читаю эту тему.

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

Я в любом случае продолжу искать сам, но пока не очень продвинулся в понимании :(

Пока не понял, чем это отличается от того, что я хочу сделать принципиально, какие даст преимущества.

nerv_ 13.01.2014 21:48

http://www.dofactory.com/javascript-...n-pattern.aspx

Antonius 16.01.2014 04:39

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


Часовой пояс GMT +3, время: 08:53.