Отличается ли шаблон Кайла Симпсона «OLOO (объекты, связывающиеся с другими объектами)» от шаблона проектирования прототипа? Помимо того, что он придумал что-то, что конкретно указывает на «связывание» (поведение прототипов) и разъясняет, что здесь не происходит «копирование» (поведение классов), что именно вводит его шаблон?
Вот пример паттерна Кайла из его книги "You Don't Know JS: this & Object Prototypes":
var Foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
var Bar = Object.create(Foo);
Bar.speak = function() {
alert("Hello, " + this.identify() + ".");
};
var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");
b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."
Ответы:
OLOO охватывает цепочку прототипов как есть, без необходимости накладывать на другую (запутанную IMO) семантику для получения связи.
Итак, эти два фрагмента дают ТОЧНЫЙ тот же результат, но по-разному.
Форма конструктора:
Форма OLOO:
В обоих фрагментах
x
объект[[Prototype]]
связан с объектом (Bar.prototype
илиBarObj
), который, в свою очередь, связан с третьим объектом (Foo.prototype
илиFooObj
).Отношения и делегирование во фрагментах идентичны. Использование памяти во всех фрагментах идентично. Возможность создавать множество «дочерних элементов» (то есть множество объектов, например,
x1
сквозныхx1000
и т. Д.) Во всех сниппетах идентична. Производительность делегирования (x.y
иx.z
) во всех фрагментах идентична. Производительность создания объекта является медленнее Oloo, но проверяя корректность , что показывает , что снижение производительности на самом деле не является проблемой.Я считаю, что OLOO предлагает то, что гораздо проще просто выразить объекты и напрямую связать их, чем косвенно связать их через конструктор /
new
механизмы. Последнее претендует на то, чтобы относиться к классам, но на самом деле это просто ужасный синтаксис для выражения делегирования ( примечание: таковclass
синтаксис ES6 !).OLOO просто исключает посредника.
Вот еще одно сравнение с
class
OLOO.источник
Object.create(...)
во много раз медленнееnew
. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…Я прочитал книгу Кайла, и я нашел ее действительно информативной, особенно подробности о том, как
this
она связана.Плюсы:
Для меня есть пара больших плюсов OLOO:
1. Простота
OLOO полагается на
Object.create()
создание нового объекта,[[prototype]]
связанного с другим объектом. Вам не нужно понимать, что у функций естьprototype
свойство, или беспокоиться о каких-либо потенциальных проблемах, связанных с их изменением.2. Более чистый синтаксис
Это спорно, но я чувствую , что синтаксис Oloo является (во многих случаях) аккуратнее и более краткий , чем «стандартный» JavaScript подход, особенно когда речь идет о полиморфизме (
super
-Style вызовов).Минусы:
Я думаю, что есть один сомнительный элемент дизайна (тот, который фактически способствует пункту 2 выше), и он связан с затенением:
Идея заключается в том, что у объектов есть свои собственные более конкретные функции, которые затем внутренне делегируются функциям ниже по цепочке. Например, у вас может быть
resource
объект сsave()
функцией, которая отправляет JSON-версию объекта на сервер, но у вас также может бытьclientResource
объект сstripAndSave()
функцией, которая сначала удаляет свойства, которые не должны отправляться на сервер. .Потенциальная проблема: если кто-то другой приходит и решает создать
specialResource
объект, не полностью осведомленный обо всей цепочке прототипов, он может разумно * решить сохранить временную метку для последнего сохранения в названном свойствеsave
, которое затеняет базовуюsave()
функциональность наresource
объект две ссылки вниз по цепочке прототипов:Это особенно надуманный пример, но суть в том, что отсутствие затенения других свойств может привести к некоторым неловким ситуациям и частому использованию тезауруса!
Возможно, лучшей иллюстрацией этого был бы
init
метод - особенно острый, поскольку OOLO уклоняется от функций типа конструктора. Поскольку каждому связанному объекту, вероятно, потребуется такая функция, присвоение им соответствующих имен может оказаться утомительным занятием, а уникальность может затруднить запоминание того, что использовать.* На самом деле это не особо разумно (
lastSaved
было бы намного лучше, но это всего лишь пример).источник
[[Prototype]]
системы, а не конкретно OLOO.b.timeStampedSave();
вместоa.timeStampedSave();
последней строки фрагмента кода.Обсуждение в «Вы не знаете JS: это и прототипы объектов» и презентация OLOO заставляют задуматься, и я многому научился, просматривая книгу. Достоинства шаблона OLOO хорошо описаны в других ответах; однако у меня есть следующие жалобы на него (или мне не хватает чего-то, что мешает мне эффективно его применять):
1
Когда «класс» «наследует» другой «класс» в классическом шаблоне, две функции могут быть объявлены с одинаковым синтаксисом ( «объявление функции» или «оператор функции» ):
Напротив, в шаблоне OLOO для определения базовых и производных объектов используются разные синтаксические формы:
Как вы можете видеть в приведенном выше примере, базовый объект может быть определен с использованием буквальной нотации объекта, тогда как ту же нотацию нельзя использовать для производного объекта. Меня беспокоит эта асимметрия.
2
В шаблоне OLOO создание объекта состоит из двух шагов:
Object.create
вызвать какой-нибудь нестандартный нестандартный метод для инициализации объекта (который вы должны запомнить, поскольку он может отличаться от одного объекта к другому):
Напротив, в шаблоне прототипа вы используете стандартный оператор
new
:3
В классическом шаблоне я могу создавать «статические» служебные функции, которые не применяются непосредственно к «моменту», назначая их непосредственно функции «класса» (в отличие от ее
.prototype
). Например, как функцияsquare
в приведенном ниже коде:Напротив, в шаблоне OLOO любые "статические" функции доступны (через цепочку [[prototype]]) и для экземпляров объекта:
источник
Как объясняет Кайл, когда два объекта
[[Prototype]]
связаны, они на самом деле не зависят друг от друга; вместо этого они являются индивидуальным объектом. Вы связываете один объект с другим с помощью[[Prototype]]
связи, которую вы можете изменить в любое время. Если вы возьмете два[[Prototype]]
связанных объекта, созданных с помощью стиля OLOO, как зависимые друг от друга, вы также должны подумать о тех, которые созданы с помощьюconstructor
вызовов.А теперь подумайте на секунду, что вы думаете,
foo
bar
иbaz
как зависимы друг от друга?Теперь давайте сделаем то же самое с этим
constructor
кодом стиля -Единственное различие ч / б последнего и бывший код является то , что в последнем одном
foo
,bar
,baz
bbjects связаны друг-друга через произвольные объекты ихconstructor
функции (Foo.prototype
,Bar.prototype
,Baz.prototype
) , но в прежней (OLOO
стиль) они связаны напрямую. Оба способа вы просто связывающийfoo
,bar
,baz
друг с другом, прямо в прежнем , так и косвенно , в последнем. Но в обоих случаях объекты независимы друг от друга, потому что на самом деле это не похоже на экземпляр какого-либо класса, который после создания экземпляра не может быть унаследован от другого класса. Вы всегда можете изменить, какой объект объект должен делегировать.Так что все они независимы друг от друга.
Да, это действительно возможно-
Давайте использовать
Tech
в качестве служебного объекта -создайте столько объектов, сколько хотите, связанных с
Tech
-Как вы думаете
html
,css
,js
объекты связаны друг-друга? Нет, это не так. Теперь давайте посмотрим, как мы могли бы это сделать с помощьюconstructor
функции -создайте столько объектов, сколько хотите, связанных с
Tech.proptotype
-Некоторая проверка (избегая console.log) -
Как вы думаете , эти
constructor
-Style объектов (html
,css
,js
) объекты отличаются отOLOO
-style коды? Фактически они служат той же цели. ВOLOO
-стиле один объект делегирует объектTech
(делегирование было задано явно), в то время как вconstructor
-стиле один объект делегирует емуTech.prototype
(делегирование было установлено неявно). В конечном итоге вы связываете три объекта, не имеющие связи друг с другом, с одним объектом, напрямую используяOLOO
-style, косвенно используяconstructor
-style.Нет,
ObjB
здесь не похоже на экземпляр (в классических языках) какого-либо классаObjA
. Можно сказать, чтоobjB
объект становится делегатомObjA
объекта во время его создания " . Если бы вы использовали конструктор, вы бы сделали то же самое« соединение », хотя и косвенно, используя.prototype
s.источник
@Marcus @bholben
Возможно, мы сможем сделать что-то подобное.
Конечно, создание объекта Point3D, который ссылается на прототип объекта Point2D, довольно глупо, но это не относится к делу (я хотел быть совместимым с вашим примером). В любом случае, что касается жалоб:
Асимметрию можно исправить с помощью ES6 Object.setPrototypeOf или того,
__proto__ = ...
что я не одобряю . Теперь мы также можем использовать super на обычных объектах, как показано на рисункеPoint3D.init()
. Другой способ - сделать что-то вродехотя мне не особо нравится синтаксис.
Мы всегда можем просто обернуть,
p = Object.create(Point)
а затемp.init()
в конструктор. напрPoint.create(x,y)
. Используя приведенный выше код, мы можем создатьPoint3D
«экземпляр» следующим образом.Я только что придумал этот хак для имитации статических методов в OLOO. Не уверен, нравится мне это или нет. Это требует вызова специального свойства в верхней части любых «статических» методов. Например, я сделал
Point.create()
метод статическим.Кроме того, с помощью символов ES6 вы можете безопасно расширять базовые классы Javascript. Таким образом, вы можете сэкономить немного кода и определить специальное свойство в Object.prototype. Например,
источник
@james emanon - Итак, вы имеете в виду множественное наследование (обсуждается на стр. 75 в книге «Вы не знаете JS: это и прототипы объектов»). И этот механизм мы можем найти, например, в функции подчеркивания "extension". Названия объектов, которые вы указали в своем примере, немного смешивают яблоки, апельсины и конфеты, но я понимаю суть вопроса. По моему опыту, это будет версия OOLO:
Это простой пример, но показанный момент состоит в том, что мы просто объединяем объекты в довольно плоскую структуру / формирование и все еще имеем возможность использовать методы и свойства из нескольких объектов. Мы достигаем того же, что и с подходом класс / «копирование свойств». Подытожил Кайл (стр. 114, «Это и прототипы объектов»):
Я понимаю, что для вас более естественным способом было бы указать все "родительские" (осторожные :)) объекты в одном месте / вызове функции, а не моделировать всю цепочку.
Для этого требуется изменение мышления и моделирования проблем в наших приложениях в соответствии с этим. Я тоже к этому привыкаю. Надеюсь, это поможет, и окончательный вердикт от самого Кайла будет отличным. :)
источник
@Marcus, как и вы, я был увлечен OLOO и мне не нравилась асимметрия, описанная в вашем первом пункте. Я играл с абстракцией, чтобы вернуть симметрию. Вы можете создать
link()
функцию, которая будет использоваться вместоObject.create()
. При использовании ваш код может выглядеть примерно так ...Помните, что у
Object.create()
него есть второй параметр, который можно передать. Вот функция ссылки, которая использует второй параметр. Он также позволяет немного настраивать конфигурацию ...Конечно, я думаю, что @Kyle не одобрил бы затенение
init()
функции в объекте Point3D. ;-)источник
Object.assign()
сObject.create()
, мы можем значительно упроститьlink()
описанную выше функцию. На его месте, мы могли бы использовать это:function create(delegate, props) { return Object.assign(Object.create(delegate), props); }
. Или еще лучше, мы можем использовать подчеркивание или Lodash , чтобы сделать его действительно кратким:_.create(delegate, props)
.Есть ли способ OLOO более чем "двух" объектов .. все примеры, которые я составляю, основаны на примере (см. Пример OP). Допустим, у нас были следующие объекты, как мы можем создать «четвертый» объект, который имеет атрибуты «других» трех? аля ...
эти объекты произвольны и могут включать в себя все виды поведения. Но суть в том, что у нас есть n объектов, и нашему новому объекту нужно что-то из всех трех.
вместо приведенных примеров аля:
НО, наш newObject = (Button, Bike, Shoe) ......
Как это работает в OLOO?
источник
Object.assign()
- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Если вы пишете на ES5, вы можете использовать Underscore_.extend()
или Lodash_.assign()
. Вот отличное видео для объяснения ... youtu.be/wfMtDGfHWpA . Если у вас есть конфликтующие свойства, побеждает последнее, поэтому порядок имеет значение.