Как установить прототип объекта JavaScript, который уже был создан?



Предположим у меня есть объект foo в моем коде JavaScript. foo - это сложный объект, и он создается где-то еще. Как я могу изменить прототип

382   11  

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__ устанавливается. Это просто плохая практика, форма преднамеренной путаницы типа, в генеральный.

ES6 наконец указывает

можно использовать 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, который уже был создан в кросс-браузере. Как уже упоминалось, ваши варианты включают в себя:

  1. изменение нестандартного / кросс-браузера __proto__ свойства
  2. копировать свойства объектов в новый объект

ни один из них не особенно велик, особенно если вам нужно рекурсивно перебирать объект во внутренние объекты, чтобы эффективно изменить элементы весь прототип.

альтернативное решение вопроса

Я собираюсь более абстрактно взглянуть на функциональность, которую вы хотите.

в основном прототип / методы просто позволяют группировать функции на основе объекта.
Вместо того, чтобы писать

function trim(x){ /* implementation */ }
trim('   test   ');

вы пишите

'   test  '.trim();

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

  1. короткие имена методов и переменных меньше obj.replace('needle','replaced') против того, чтобы помнить имена, как str_replace ( 'foo' , 'bar' , 'subject') и расположение различных переменных
  2. способ сцепления(string.trim().split().join()) потенциально легче изменить и написать затем вложенные функции join(split(trim(string))

к сожалению в JavaScript (как показано выше), вы не можете изменить уже существующего прототипа. В идеале выше вы могли бы изменить Object.prototype для только данный объект выше, но, к сожалению, изменение Object.prototype потенциально нарушит сценарии (что приведет к столкновению свойств и переопределению).

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

UnlimitJS обеспечивает золотую середину, которая позволяет определить пользовательские методы. Это позволяет избежать:

  1. столкновение свойств, потому что оно не распространяется Прототипы объектов
  2. по-прежнему позволяет использовать синтаксис цепочки ООП
  3. это 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; // true

Live 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"}]);

теперь вам нужен только конструктор, который принимает массив в качестве аргумента.

foo.prototype.myFunction = function(){alert("me");}

нет никакого способа действительно наследовать от 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

    Ничего не найдено.