Предположим, у меня есть объект foo
в моем коде JavaScript. foo
представляет собой сложный объект и создается где-то еще. Как я могу изменить прототип foo
объекта?
Моя мотивация - установить соответствующие прототипы для объектов, сериализованных из .NET в литералы JavaScript.
Предположим, что я написал следующий код JavaScript на странице ASP.NET.
var foo = <%=MyData %>;
Предположим, MyData
это результат вызова .NET JavaScriptSerializer
для Dictionary<string,string>
объекта.
Во время выполнения это становится следующим:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
Как видите, foo
становится массивом объектов. Я хотел бы иметь возможность инициализировать foo
соответствующий прототип. Я не хочу изменять файл Object.prototype
nor Array.prototype
. Как я могу это сделать?
javascript
function-prototypes
Река Вивиан
источник
источник
extend
или Googlegoog.inherit
? Многие разработчики предоставляют способы построения наследования перед вызовом старогоnew
конструктора - это было до того, как нам дали,Object.create
и не нужно было беспокоиться о переопределенииObject.prototype
.Ответы:
РЕДАКТИРОВАТЬ Февраль 2012 г .: приведенный ниже ответ больше не точен. __proto__ добавляется в ECMAScript 6 как «нормативный необязательный», что означает, что его не нужно реализовывать, но если это так, он должен соответствовать заданному набору правил. В настоящее время этот вопрос не решен, но, по крайней мере, он будет официально включен в спецификацию JavaScript.
Этот вопрос намного сложнее, чем кажется на первый взгляд, и выходит за рамки уровня оплаты труда большинства людей в отношении знания внутреннего устройства Javascript.
prototype
Свойство объекта используется при создании новых дочерних объектов этого объекта. Его изменение не отражается на самом объекте, скорее, оно отражается, когда этот объект используется в качестве конструктора для других объектов, и не имеет смысла изменять прототип существующего объекта.У объектов есть внутреннее свойство [[prototype]], которое указывает на текущий прототип. Это работает так: всякий раз, когда вызывается свойство объекта, оно начинается с объекта, а затем идет вверх по цепочке [[prototype]], пока не найдет совпадение или не завершится ошибкой после корневого прототипа объекта. Вот как Javascript позволяет создавать и изменять объекты во время выполнения; у него есть план поиска того, что ему нужно.
Это
__proto__
свойство существует в некоторых реализациях (сейчас их много): в любой реализации Mozilla, во всех известных мне webkit и некоторых других. Это свойство указывает на внутреннее свойство [[prototype]] и позволяет изменять объекты после создания. Любые свойства и функции мгновенно переключаются в соответствии с прототипом благодаря этому поиску по цепочке.Эта функция, хотя сейчас и стандартизирована, по-прежнему не является обязательной частью JavaScript, и на языках, поддерживающих ее, существует высокая вероятность того, что ваш код попадет в категорию «неоптимизированных». JS-движки должны делать все возможное, чтобы классифицировать код, особенно «горячий» код, к которому обращаются очень часто, и если вы делаете что-то необычное, например, модифицируйте
__proto__
, они вообще не оптимизируют ваш код.В этой публикации https://bugzilla.mozilla.org/show_bug.cgi?id=607863 конкретно обсуждаются текущие реализации
__proto__
и различия между ними. Каждая реализация делает это по-своему, потому что это сложная и нерешенная проблема. Все в Javascript изменяемо, кроме a.) Синтаксиса b.) Объектов хоста (DOM технически существует вне Javascript) и c.)__proto__
. Остальное полностью в ваших руках и в руках любого другого разработчика, так что вы можете понять, почему__proto__
торчит, как больной палец.Есть одна вещь, которая
__proto__
позволяет сделать это иначе: обозначение прототипа объекта во время выполнения отдельно от его конструктора. Это важный вариант использования, и это одна из основных причин, по__proto__
которой он еще не умер. Достаточно важно, чтобы он стал предметом серьезного обсуждения при формулировке Harmony или вскоре станет известен как ECMAScript 6. Возможность указывать прототип объекта во время создания будет частью следующей версии Javascript, и это будет колокол, указывающий__proto__
на то, что дни официально пронумерованы.В краткосрочной перспективе вы можете использовать,
__proto__
если ориентируетесь на браузеры, которые его поддерживают (не IE, и никакой IE никогда не будет). Вероятно, он будет работать в webkit и moz в течение следующих 10 лет, поскольку ES6 не будет доработан до 2013 года.Брендан Эйх - re: Подход новых методов Object в ES5 :
источник
non-writable, configurable
устранит эти проблемы, заставив пользователя явно перенастроить свойство. В конце концов, плохие практики связаны со злоупотреблением возможностями языка, а не самими возможностями. Записываемый __proto__ не является чем-то необычным. Есть много других неперечислимых записываемых свойств, и, хотя есть опасности, есть и передовые методы. Головку молотка нельзя снимать просто потому, что ею можно ненадлежащим образом ранить кого-нибудь.__proto__
необходима, потому что Object.create будет создавать только объекты, а не функции, например, ни символы, регулярные выражения, элементы DOM или другие объекты хоста. Если вы хотите, чтобы ваши объекты были вызываемыми или особенными другими способами, но все же изменяли их цепочку прототипов, вы застряли без устанавливаемых__proto__
или прокси-__proto__
в JSON». Другие опасения Брендана Эйха смехотворны. Рекурсивные цепочки прототипов или использование прототипа с неадекватными методами для данного объекта являются ошибками программиста, а не языковыми ошибками и не должны быть фактором, и, кроме того, они могут происходить с Object.create так же хорошо, как и со свободно устанавливаемым__proto__
.__proto__
.Наконец, ES6 определяет Object.setPrototypeOf (объект, прототип), который уже реализован в Chrome и Firefox.
источник
Вы можете использовать
constructor
экземпляр объекта для изменения прототипа объекта на месте. Я считаю, что это то, что вы просите сделать.Это означает, что если у вас есть
foo
экземплярFoo
:Вы можете добавить свойство
bar
ко всем экземплярамFoo
, выполнив следующие действия:Вот скрипка, показывающая доказательство концепции: http://jsfiddle.net/C2cpw/ . Не очень уверен, насколько старые браузеры будут использовать этот подход, но я уверен, что он должен хорошо справиться со своей задачей.
Если вы собираетесь смешивать функциональность с объектами, этот фрагмент должен выполнить свою работу:
источник
foo.__proto__.bar = 'bar';
Вы можете это сделать
foo.__proto__ = FooClass.prototype
, AFAIK, который поддерживается Firefox, Chrome и Safari. Имейте в виду, что__proto__
недвижимость нестандартная и в какой-то момент может уйти.Документация: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto . Также см. Http://www.mail-archive.com/jsmentors@googlegroups.com/msg00392.html, чтобы узнать, почему его нет
Object.setPrototypeOf()
и почему__proto__
он устарел.источник
Вы можете определить функцию конструктора прокси, а затем создать новый экземпляр и скопировать в него все свойства исходного объекта.
Живая демонстрация: http://jsfiddle.net/6Xq3P/
Custom
Конструктор представляет новый прототип, эрго, егоCustom.prototype
объект содержит все новые свойства , которые вы хотели бы использовать с исходным объектом.Внутри
Custom
конструктора вы просто копируете все свойства из исходного объекта в новый объект-экземпляр.Этот новый объект-экземпляр содержит все свойства исходного объекта (они были скопированы в него внутри конструктора), а также все новые свойства, определенные внутри
Custom.prototype
(поскольку новый объект являетсяCustom
экземпляром).источник
Вы не можете изменить прототип объекта JavaScript, который уже был создан кроссбраузерным способом. Как уже упоминали другие, ваши варианты включают:
__proto__
свойстваНи то, ни другое не особенно хороши, особенно если вам нужно рекурсивно перебирать объект во внутренние объекты, чтобы эффективно изменить весь прототип элемента.
Альтернативное решение вопроса
Я собираюсь более абстрактно взглянуть на функциональные возможности, которые, по вашему мнению, вам нужны.
В основном прототип / методы просто позволяют группировать функции на основе объекта.
Вместо того, чтобы писать
ты пишешь
Вышеупомянутый синтаксис был придуман термином ООП из-за синтаксиса object.method (). Некоторые из основных преимуществ ООП перед традиционным функциональным программированием включают:
obj.replace('needle','replaced')
вместо запоминания таких имен, какstr_replace ( 'foo' , 'bar' , 'subject')
и расположение различных переменных.string.trim().split().join()
) потенциально легче изменить и написать вложенные функцииjoin(split(trim(string))
К сожалению, в JavaScript (как показано выше) вы не можете изменить уже существующий прототип. В идеале выше вы можете изменить
Object.prototype
только данный объект, указанный выше, но, к сожалению, изменениеObject.prototype
потенциально может нарушить скрипты (что приведет к конфликту свойств и переопределению).Между этими двумя стилями программирования не существует общепринятой середины, а также нет способа ООП для организации пользовательских функций.
UnlimitJS обеспечивает золотую середину, позволяющую определять собственные методы. Это позволяет избежать:
Используя ваш код выше, я бы просто создал пространство имен функций, которые вы собираетесь вызывать для объекта.
Вот пример:
Вы можете прочитать больше примеров здесь UnlimitJS . В основном, когда вы вызываете
[Unlimit]()
функцию, она позволяет вызывать функцию как метод объекта. Это как золотая середина между ООП и функциональными дорогами.источник
[[prototype]]
Насколько мне известно, вы не можете изменить ссылку на уже построенные объекты. Вы можете изменить свойство prototype исходной функции конструктора, но, как вы уже отметили, этот конструктор естьObject
, а изменение основных конструкций JS - Плохая вещь.Однако вы можете создать прокси-объект сконструированного объекта, который реализует дополнительные функции, которые вам нужны. Вы также можете исправить дополнительные методы и поведение, назначив их непосредственно рассматриваемому объекту.
Возможно, вы сможете получить то, что хотите, каким-то другим способом, если вы готовы подойти под другим углом: что вам нужно сделать, чтобы возиться с прототипом?
источник
__proto__
свойство. Это будет работать только в браузерах, поддерживающих__proto__
нотацию, а именно Chrome и Firefox, и они устарели . Короче говоря, вы можете изменить[[prototype]]
объект, но, вероятно, не должны этого делать.Если вы знаете прототип, почему бы не вставить его в код?
Итак, как только данные сериализованы, вы получите
теперь вам нужен только конструктор, который принимает массив в качестве аргумента.
источник
Нет никакого способа наследовать
Array
или «подклассифицировать» его.Что вы можете сделать, так это ( ВНИМАНИЕ: КОД ЗАГРУЗКИ ВПЕРЕДИ ):
Это работает, но вызовет определенные проблемы для любого, кто пересекает его путь (он выглядит как массив, но что-то пойдет не так, если вы попытаетесь манипулировать им).
Почему бы вам не пойти разумным путем и не добавить методы / свойства напрямую
foo
или не использовать конструктор и не сохранить свой массив как свойство?источник
если вы хотите создать прототип на лету, это один из способов
источник
источник
prototype
статичен? Я не уверен, что ты имеешь в виду.prototype
это свойство функции, а не объекта.Object.prototype
существуют;{}.prototype
нет.Object.getPrototypeOf(foo)
для получения объекта-прототипа конструктора. Вы можете изменить его свойства, чтобы изменить прототип объекта. Он работает только в последних версиях браузеров, но не могу сказать, в каких именно.Object.prototype
напрямую, потому что это, скорее всегоObject.getPrototypeOf(foo)
, вернется. Не очень полезно.