Существуют ли эффективные способы использования `__proto__` или `setPrototypeOf()` в javascript?

У MDN есть огромное страшное предупреждение об изменении прототипа в вашем коде:

Изменение [[Prototype]] объекта в силу того, как современные движки JavaScript оптимизируют доступ к свойствам, является очень медленной операцией в каждом браузере и движке JavaScript. Влияние изменения наследования на производительность неуловимо и широко распространено и не ограничивается просто временем, затрачиваемым на оператор Object.setPrototypeOf(...), но может распространяться на любой код, который имеет доступ к любому объекту, [[Prototype]] которого был изменен. Если вы заботитесь о производительности, вам следует избегать установки [[Prototype]] объекта. Вместо этого создайте новый объект с нужным [[Prototype]], используя Object.create().

MDN > JavaScript > Object.setPrototypeOf()

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

Итак, кто-нибудь знает, есть ли эффективные способы модификации прототипа объекта?

Изменить: Мотивация для этого вопроса связана с желанием создать объекты, которые на самом деле наследуются от Function. И единственный известный мне способ сделать это — изменить прототип функции. Смотрите самую нижнюю часть ответа Райноса здесь: класс javascript наследуется от класса Function


comment
Вы можете сделать Object.assign(Object.create(obj), { obj2, obj3, obj4 }), который создаст новый объект с прототипами obj. Я полагаю, что это взято из книги Эрика Эллиота. Это обеспечивает лучший объем памяти. Я хотел бы дать вам лучшее определение, но оно как-то связано с тем, что Object.create() на самом деле не создает экземпляр нового объекта, поэтому это быстрее, чем получение прототипов другим способом. Попробуйте поискать в Google что-нибудь вроде производительности памяти Object.create. Я думаю, вы могли бы найти много хорошего материала.   -  person agm1984    schedule 29.09.2017
comment
Любой вопрос, включающий термины «эффективность» и «производительность», должен включать контрольные показатели для сравнения различных шаблонов кода, которые были опробованы; код, из которого были получены тесты; и результаты кода в разных браузерах. В противном случае исследование основано на чистой спекуляции, на которую вполне возможен окончательный ответ.   -  person guest271314    schedule 29.09.2017
comment
Кстати, существует способ создания объектов, наследуемых от Function, без установки прототипа, но установка прототипа все же намного удобнее . И да, если вы изменяете прототип только сразу после создания объекта, действительно должно быть (или: единственным) исключением из этого правила, однако я не могу это доказать.   -  person Bergi    schedule 29.09.2017
comment
Подробнее см. в разделе Почему изменение [[prototype]] объекта плохо влияет на производительность?.   -  person Bergi    schedule 29.09.2017


Ответы (1)


Мотивация для этого вопроса исходит из желания создавать объекты, которые на самом деле наследуются от Function.

Кажется, вы пытаетесь сделать следующее:

  • Создание новых объектов
  • Set these objects' prototype (or __proto__ property) to refer to Fuction.prototype such that they inherit properties from Function.prototype, e.g.:
    • .apply
    • .call
    • .bind

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


Ответ первый: как установить прототип объекта

Вы можете установить прототип любого объекта при создании, используя Object.create(). Обратите внимание, что присвоение прототипа любого объекта Function.prototype не делает объект вызываемой функцией. Но если вы все же хотите присвоить прототипу ссылку на Function.prototype или на какой-то другой объект, вы можете сделать следующее:

var obj1 = Object.create(Function.prototype);

Это будет:

  • Создайте новый объект, obj1
  • Set its prototype (or __proto__ property) to refer to Function.prototype or whatever object you pass in as the argument
    • i.e., it will delegate failed property lookups to Function.prototype via the prototype chain (this is what a JavaScript prototype is and what __proto__ refers to: a place to delegate failed property lookups)

Это не требует, чтобы вы использовали Object.setPrototypeOf() для изменения прототипа существующего объекта, потому что прототип назначается в момент создания объекта. См. документы MDN для Object.create() для получения дополнительных примеров и информации -- это не содержит таких страшных предупреждений, как setPrototypeOf(). Вы можете использовать Object.create() для установки прототипа любого нового объекта.


Ответ второй: функции уже являются объектами

Тем не менее, если вам нужен объект, который ведет себя как функция, вы можете просто создать функцию. Функция по-прежнему является объектом, но с дополнительными специфическими для функции свойствами и поведением; вы по-прежнему можете обращаться с ним как с объектом, назначая ему свойства. Если вы хотите создать функцию, которая также действует как объект, или объект, который также может делать то же самое, что и функция, то функция уже делает это. Вы можете делать такие вещи, как:

var func = function() { return 'hello world'; }; // func is a function
func.property1 = 'foo'; // func is also an object
func.property2 = 'bar';

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

person moriah    schedule 19.01.2018
comment
Не имеет смысла позволять объекту, который нельзя вызывать, наследовать методы Function.prototype. И Object.create не может создавать вызываемые объекты. - person Bergi; 19.01.2018
comment
Согласованный! Тем не менее, по-прежнему важно знать, как использовать Object.create() для назначения прототипа нового объекта, поэтому я думаю, что это стоит включить. - person moriah; 20.01.2018