11 ответов:
EDIT Feb. 2012: ответ ниже уже не точный. __proto__ добавляется в ECMAScript 6 как "нормативный необязательный", что означает, что его не требуется реализовывать, но если это так, он должен следовать заданному набору правил. В настоящее время это не решено, но, по крайней мере, это будет официально частью спецификации JavaScript.
этот вопрос намного сложнее, чем кажется на первый взгляд, и за пределами уровня оплаты труда большинства людей в отношении знаний из внутренних Javascript.
The
prototypeсвойства объекта используется при создании новых дочерних объектов этого объекта. Изменение его не отражается в самом объекте, а скорее отражается, когда этот объект используется в качестве конструктора для других объектов и не имеет смысла в изменении прототипа существующего объекта.function myFactory(){}; myFactory.prototype = someOtherObject; var newChild = new myFactory; newChild.__proto__ === myFactory.prototype === someOtherObject; //trueобъекты имеют внутреннее свойство [[prototype]], которое указывает на текущий прототип. Как это работает, когда свойство на объект называется он будет начинаться с объекта, а затем идти вверх по цепочке [[прототип]], пока не найдет совпадение, или сбой после прототипа корневого объекта. Именно так Javascript позволяет создавать и изменять объекты во время выполнения; у него есть план поиска того, что ему нужно.
The
__proto__свойство существует в некоторых реализациях (сейчас много): любая реализация Mozilla, все известные мне webkit, некоторые другие. Это свойство указывает на внутренний [[прототип]] свойство и позволяет изменять пост-создание на объектах. Любые свойства и функции будут мгновенно переключаться в соответствии с прототипом из-за этого цепного поиска.эта функция, будучи стандартизированной сейчас, все еще не является обязательной частью JavaScript, и в языках, поддерживающих ее, есть высокая вероятность сбивания вашего кода в категорию "неоптимизированных". Двигатели JS должны сделать все возможное, чтобы классифицировать код, особенно "горячий" код, к которому обращаются очень часто, и если вы делать что-то необычное, как изменение
__proto__они не будут оптимизировать ваш код вообще.это сообщения https://bugzilla.mozilla.org/show_bug.cgi?id=607863 специально обсуждает текущие реализации
__proto__и различия между ними. Каждая реализация делает это по разному, потому что это трудная и нерешенная проблема. Все в Javascript изменчиво, кроме a.) синтаксиса B.) объектов хоста (DOM существует вне Javascript технически) и c.)__proto__. Остальное полностью в руках вас и любого другого разработчика, так что вы можете понять, почему__proto__торчит как больной палец.есть одна вещь, которая
__proto__допускает, что в противном случае невозможно сделать: назначение прототипа объектов во время выполнения отдельно от его конструктора. Это важный прецедент и является одной из основных причин__proto__еще не мертв. Это достаточно важно, чтобы это был серьезный дискуссионный момент в формулировке Гармония, или скоро будет известен как ECMAScript 6. Возможность указать прототип объекта во время создания будет частью следующей версии Javascript, и это будет колокол, указывающий__proto__дни формально пронумерованы.в краткосрочной перспективе, вы можете использовать
__proto__Если вы ориентируетесь на браузеры, которые его поддерживают (не IE, и никакой IE никогда не будет). Вероятно, он будет работать в webkit и moz в течение следующих 10 лет, поскольку ES6 не будет завершен до тех пор, пока 2013.Брендан Айх - re:подход новых методов объекта в ES5:
к сожалению, ... но устанавливаемых
__proto__, кроме случая использования инициализатора объекта (т. е. на новом объекте, еще не доступном, аналогичном объекту ES5.создать), это ужасная идея. Я пишу это, разработав и реализовав settable__proto__более 12 лет назад.... отсутствие стратификации является проблемой (рассмотрим данные JSON с помощью ключ
"__proto__"). И что еще хуже, реализация средств изменчивости должна проверять циклические цепи прототипов, чтобы избежать ilooping. [требуются постоянные проверки для бесконечной рекурсии]наконец-то, мутация
__proto__на существующем объекте могут быть нарушены неродовые методы в новом объекте-прототипе, который не может работать на объекте receiver (direct), чей__proto__устанавливается. Это просто плохая практика, форма преднамеренной путаницы типа, в генеральный.
можно использовать
constructorна экземпляр объекта, чтобы изменить прототип объекта на месте. Я считаю, что это то, что вы просите сделать.это означает, что если у вас есть
fooкоторый является экземпляромFoo:function Foo() {} var foo = new Foo();вы можете добавить свойство
barдля всех экземпляровFooследующим образом:foo.constructor.prototype.bar = "bar";вот скрипка, показывающая доказательство концепции:http://jsfiddle.net/C2cpw/. не очень уверен, насколько старше браузеры будут использовать этот подход, но я уверен, что это должно сделать работу довольно хорошо.
если вы намерены смешивать функциональность в объекты, этот фрагмент должен выполнить эту работу:
function mix() { var mixins = arguments, i = 0, len = mixins.length; return { into: function (target) { var mixin, key; if (target == null) { throw new TypeError("Cannot mix into null or undefined values."); } for (; i < len; i += 1) { mixin = mixins[i]; for (key in mixin) { target[key] = mixin[key]; } // Take care of IE clobbering `toString` and `valueOf` if (mixin && mixin.toString !== Object.prototype.toString) { target.toString = mixin.toString; } else if (mixin && mixin.valueOf !== Object.prototype.valueOf) { target.valueOf = mixin.valueOf; } } return target; } }; };
можно сделать
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/[email protected]/msg00392.html для объяснения, почему нет
Object.setPrototypeOf()и почему__proto__устарела.
вы не можете изменить прототип объекта JavaScript, который уже был создан в кросс-браузере. Как уже упоминалось, ваши варианты включают в себя:
- изменение нестандартного / кросс-браузера
__proto__свойства- копировать свойства объектов в новый объект
ни один из них не особенно велик, особенно если вам нужно рекурсивно перебирать объект во внутренние объекты, чтобы эффективно изменить элементы весь прототип.
альтернативное решение вопроса
Я собираюсь более абстрактно взглянуть на функциональность, которую вы хотите.
в основном прототип / методы просто позволяют группировать функции на основе объекта.
Вместо того, чтобы писатьfunction trim(x){ /* implementation */ } trim(' test ');вы пишите
' test '.trim();приведенный выше синтаксис был придуман термин ООП из-за объекта.синтаксис методов. Некоторые из ой главное преимущество над традиционное функциональное программирование включает в себя:
- короткие имена методов и переменных меньше
obj.replace('needle','replaced')против того, чтобы помнить имена, какstr_replace ( 'foo' , 'bar' , 'subject')и расположение различных переменных- способ сцепления(
string.trim().split().join()) потенциально легче изменить и написать затем вложенные функцииjoin(split(trim(string))к сожалению в JavaScript (как показано выше), вы не можете изменить уже существующего прототипа. В идеале выше вы могли бы изменить
Object.prototypeдля только данный объект выше, но, к сожалению, изменениеObject.prototypeпотенциально нарушит сценарии (что приведет к столкновению свойств и переопределению).между этими двумя стилями программирования нет обычно используемой середины,и нет способа организации пользовательских функций.
UnlimitJS обеспечивает золотую середину, которая позволяет определить пользовательские методы. Это позволяет избежать:
- столкновение свойств, потому что оно не распространяется Прототипы объектов
- по-прежнему позволяет использовать синтаксис цепочки ООП
- это 450 байт кроссбраузерный(IE6+,Firefox 3.0+,Chrome,Opera,Safari 3.0+) скрипт, который Unlimit большая часть проблем столкновения свойств прототипа JavaScript
используя ваш код выше, я бы просто создал пространство имен функций, которые вы собираетесь вызвать против объекта.
вот пример:
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}]; // define namespace with methods var $ = { log:function(){ console.log(this); return this; }[Unlimit](), alert:function(){ alert(''+this); }[Unlimit]() } foo[$.log]() [$.log]() [$.alert]();вы можете прочитать больше примеров здесь UnlimitJS. В основном, когда вы звоните
[Unlimit]()для функции это позволяет вызывать функцию как метод для объекта. Это как середина между ООП и функциональными дорогами.
вы можете определить свою функцию конструктора прокси, а затем создать новый экземпляр и скопировать все свойства из исходного объекта в него.
// your original object var obj = { 'foo': true }; // your constructor - "the new prototype" function Custom(obj) { for ( prop in obj ) { if ( obj.hasOwnProperty(prop) ) { this[prop] = obj[prop]; } } } // the properties of the new prototype Custom.prototype.bar = true; // pass your original object into the constructor var obj2 = new Custom(obj); // the constructor instance contains all properties from the original // object and also all properties inherited by the new prototype obj2.foo; // true obj2.bar; // trueLive demo:http://jsfiddle.net/6Xq3P/
The
Customконструктор представляет новый прототип, следовательно, егоCustom.prototypeобъект содержит все новые свойства, которые вы хотели бы использовать с исходным объектом.внутри
Customконструктор, вы просто копируете все свойства исходного объекта в новый экземпляр объекта.этот новый объект экземпляра содержит все свойства исходного объекта (они были скопированы в него внутри конструктора), а также все новые свойства, определенные внутри
Custom.prototype(потому что новый объект являетсяCustomэкземпляр).
вы не можете изменить
[[prototype]]ссылка на уже построенные объекты, насколько я знаю. Вы можете изменить свойство прототипа исходной функции конструктора, но, как вы уже прокомментировали, этот конструкторObject, и изменение основных конструкций JS-это плохо.вы можете создать прокси-объект построенного объекта, который реализует дополнительную функциональность, которая вам нужна. Вы также можете monkeypatch дополнительные методы и поведение путем присвоения непосредственно рассматриваемому объекту.
возможно, вы можете получить то, что вы хотите каким-то другим способом, если вы готовы подойти с другой стороны: что вам нужно сделать, что включает в себя возиться с прототипом?
Если вы знаете прототип, почему бы не ввести его в код?
var foo = new MyPrototype(<%= MyData %>);Итак, как только данные сериализуются, вы получаете
var foo = new MyPrototype([{"A":"1","B":"2"},{"X":"7","Y":"8"}]);теперь вам нужен только конструктор, который принимает массив в качестве аргумента.
нет никакого способа действительно наследовать от
Arrayили "подкласс" это.что вы можете сделать это (ВНИМАНИЕ: ГНОЙНЫЙ КОД ВПЕРЕДИ):
function Foo(arr){ [].push.apply(this, arr) } Foo.prototype = [] Foo.prototype.something = 123 var foo = new Foo(<%=MyData %>) foo.length // => 2 foo[0] // => {"A":"1","B":"2"} foo.something // => 123это работает, но вызовет определенные проблемы для тех, кто пересекает его путь (он выглядит как массив, но все пойдет не так, если вы попытаетесь им манипулировать).
Почему бы вам не пойти по разумному маршруту и не добавить методы / свойства непосредственно в
foo, или использовать конструктор и сохранить массив в качестве собственность?function Foo(arr){ this.items = arr } Foo.prototype = { someMethod : function(){ ... } //... } var foo = new Foo(<%=MyData %>) foo.items // => [{"A":"1","B":"2"},{"X":"7","Y":"8"}]
Если вы хотите создать прототип на лету, это один из способов
function OntheFlyProto (info){ this.items = info; this.y =-1; for(var i = 0; i < this.items.length ; i++){ OntheFlyProto.prototype["get"+this.items[i].name] = function (){ this.y++; return this.items[this.y].value; } } } var foo = [{name:"one", value:1},{name:"two", value:2}]; v = new OntheFlyProto(foo);
Comments