Имеет ли использование прототипов в JavaScript какие-либо реальные преимущества?

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

В последнем он создает функцию, своего рода фабрику, которая выдает объекты, дополненные желаемыми методами, основанными на других объектах; что-то вроде:

var dog = function(params) {
    // animal is the 'super class' created
    // the same way as the dog, and defines some
    // common methods
    var that = animal(params);
    that.sound = 'bark';
    that.name = function () {};
    return that;
}

Поскольку все объекты, созданные таким образом, будут иметь ссылку на одни и те же функции, объем памяти будет намного меньше, чем при использовании, например, оператора new. Вопрос в том, даст ли подход прототипа какие-либо преимущества в этом случае? Другими словами, являются ли прототипы объектов каким-то образом «ближе к металлу», обеспечивающему преимущества в производительности, или это просто удобный механизм?

РЕДАКТИРОВАТЬ: я упрощу вопрос. Прототипы против их эмуляции через композицию объектов. Пока вы не требуете, чтобы все экземпляры объектов обновлялись новыми методами, что является удобством, предлагаемым только прототипами, есть ли вообще какие-либо преимущества в использовании прототипов?

Я написал Дугу Крокфорду по электронной почте, и он сказал следующее:

[Использование функционального подхода выше по сравнению с прототипами] не так уж много памяти. Если у вас есть огромное количество объектов, умноженное на огромное количество методов, вам может понадобиться прототип. Но воспоминаний в наши дни предостаточно, и только экстремальное приложение заметит это.

Прототипы могут использовать меньше памяти, но могут немного медленнее извлекать данные, особенно если цепочки очень длинные. Но в целом это не заметно.


person dmkc    schedule 29.08.2010    source источник


Ответы (3)


Все экземпляры не ссылаются на одни и те же функции и т. д. Каждый вызов "собаки" создает новый экземпляр функции "имя".

person Pointy    schedule 29.08.2010
comment
Хм. Вы правы, я как-то этого не увидел. Тогда разве это не ужасно? :) - person dmkc; 29.08.2010
comment
Однако остается вопрос: дают ли прототипы какие-либо преимущества по сравнению с простым обращением к одним и тем же функциям из нескольких объектов? - person dmkc; 29.08.2010
comment
Ну, вы экономите много памяти, не имея отдельных копий служебных функций для каждого экземпляра. - person Pointy; 29.08.2010
comment
@mitjak: я не знаю, делает ли это какая-либо реализация JS, но некоторые языки могут в этом случае просто (прозрачно) создать один статический экземпляр функции, созданной внутри dog, и каждый раз, когда вы вызываете dog, он просто даст новую ссылку на эту функцию. Если обязательно, чтобы каждая функция имела разные идентификаторы, эта функция может генерироваться каждый раз, но, вероятно, имеет небольшие накладные расходы, но каждая из этих сгенерированных функций будет иметь ссылку на один и тот же байт-код (и, возможно, другое замыкание). В любом случае, накладные расходы, вероятно, незначительны. - person L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o&#x; 29.08.2010
comment
@Pointy Мой код был плохим примером. Предположим, что ссылки поддерживались правильно, так что каждый экземпляр ссылается на один и тот же «основной» объект. Как насчет тогда? Меня интересует только то, дают ли прототипы какое-либо преимущество в производительности, поскольку их поведение можно в определенной степени эмулировать. - person dmkc; 29.08.2010
comment
Какое преимущество в производительности вы ищете? Современный движок Javascript будет настроен на производительность, чтобы поддерживать наиболее распространенные способы использования языка. Попытка сделать что-то необычное, вероятно, не является хорошей долгосрочной стратегией. - person Pointy; 29.08.2010
comment
Ну, любой и все. Я думаю, что пытаюсь задать несколько вопросов в исходной публикации, но в основном это должно было сводиться к следующему: быстрее ли создавать объекты с прототипами, чем создавать объекты со свойствами и методами других объектов, тем самым создавая ссылки вместо копий этих свойств и методы? По объему памяти подходы кажутся примерно эквивалентными. - person dmkc; 29.08.2010
comment
Я написал Дугу Крокфорду по электронной почте и обновил исходный вопрос его ответом. Я все еще не удовлетворен ответом! :-) - person dmkc; 29.08.2010

Есть много мнений по этому поводу, и мнение Крокфорда не обязательно правильное.

Основным недостатком модификации прототипов является то, что это может затруднить работу с другими библиотеками javascript.

Но недостатком функционального способа создания классов Крокфорда является то, что вы не можете так же легко добавить метод или поле ко всем экземплярам типа.

См. Закрытие: Полное руководство для некоторых критических комментариев по поводу взглядов Крокфорда на классы и наследование в javascript: //my.safaribooksonline.com/9781449381882/I_sect1_d1e29990#X2ludGVybmFsX0ZsYXNoUmVhZGVyP3htbGlkPTk3ODE0NDkzODE4ODIvNTE0

Библиотеки, такие как Dojo, Google Closure Library (которая, кажется, копирует стиль Dojo) и, возможно, YUI, имеют свою собственную систему классов, которая кажется хорошей золотой серединой. Мне больше всего нравится система Dojo, потому что в ней есть поддержка Mixins, в отличие от Closure. Некоторые другие системы классов, не привязанные к наборам инструментов графического интерфейса, включают Joose, JS.Class и JavascriptMVC (проверьте последний, особенно если используете jquery).

person edtechdev    schedule 29.08.2010
comment
Спасибо за предложения, но вопрос по этому поводу: основной недостаток модификации прототипов заключается в том, что это может затруднить работу с другими библиотеками javascript. Зачем мне изменять прототипы встроенных объектов? В основном я говорю о прототипах объектов, которые создаю сам. - person dmkc; 29.08.2010
comment
@mitjak модифицирует прототипы встроенных модулей довольно популярно; см., например, библиотеку Prototype, которая делает это с дикой энергией. - person Pointy; 29.08.2010
comment
Он популярен, но это не совсем то, о чем я пытаюсь здесь узнать. - person dmkc; 29.08.2010

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

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

Другой реальной альтернативы просто не было. Единственными альтернативами были:

  1. Создайте объект, который составляет объект, переданный в графический интерфейс (как я упоминал выше). Затем этот объект будет воспроизводить каждую функцию в исходном классе для реализации того же интерфейса и добавления событий, поэтому он фактически будет таким же, как и способ прокси-объекта.
  2. Попросите мутацию состояния отслеживания GUI в переданном объекте. Это было бы муторно и чревато ошибками.
person L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o&#x    schedule 29.08.2010