12

MDN has a huge scary warning about modifying the prototype in your code:

Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine. The effects on performance of altering inheritance are subtle and far-flung, and are not limited to simply the time spent in the Object.setPrototypeOf(...) statement, but may extend to any code that has access to any object whose [[Prototype]] has been altered. If you care about performance you should avoid setting the [[Prototype]] of an object. Instead, create a new object with the desired [[Prototype]] using Object.create().

MDN > JavaScript > Object.setPrototypeOf()

I’m wondering if there are any situations where you can modify an object’s prototype without causing cascading optimization effects that kill your program's performance. It seems like there should be. For example if you only modify the prototype right after the object is created (and before its used by anything else). But I imagine this is very engine-dependent.

So does anyone know if there are efficient ways to modify an object’s prototype?

Edit: The motivation for this question comes from a desire to create objects that actually inherit from Function. And the only way I know how to do that is by modifying the prototype of a function. See the very bottom of Raynos’s answer here: javascript class inherit from Function class

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
B T
  • 57,525
  • 34
  • 189
  • 207
  • You could do `Object.assign(Object.create(obj), { obj2, obj3, obj4 })` which will create a new object with the prototypes of obj. I believe it comes from a book by Eric Elliot. It nets better memory footprint. I wish I could give you a better definition, but it has something to do with Object.create() doesn't actually instantiate a new object, so it's faster than getting the prototypes from another way. Try Googling something like "Object.create memory performance". I think you might find lots of good material. – agm1984 Sep 29 '17 at 00:54
  • 4
    Any question including the terms "efficiency" and "performance" should include benchmarks for the comparisons of the various patterns of code which were tried; the code from which the benchmarks were derived; and the results of the code at different browsers. Else the inquiry is based on pure speculation, where a definitive answer is affirmatively possible. – guest271314 Sep 29 '17 at 01:46
  • 1
    Btw, there [is a way to create objects inheriting from `Function` without setting the prototype](https://stackoverflow.com/a/36871498/1048572), but setting the prototype is much more comfortable still. And yes, "*if you only modify the prototype right after the object is created*" should indeed be an (or: the only) exception to this rule, however I cannot prove it. – Bergi Sep 29 '17 at 01:50
  • 1
    See also [Why is mutating the \[\[prototype\]\] of an object bad for performance?](https://stackoverflow.com/q/23807805/1048572) for details. – Bergi Sep 29 '17 at 01:50

1 Answers1

3

The motivation for this question comes from a desire to create objects that actually inherit from Function.

It seems what you are trying to do is the following:

  • Create new objects
  • 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

You should never need to change any object’s prototype as long as you can correctly set it to the desired reference at the time the object is created.


Answer One: How to Set an Object's Prototype

You can set any object’s prototype upon creation using Object.create(). Note that assigning any object's prototype to Function.prototype will not cause the object to be a callable function. But if you still wanted to assign the prototype to refer to Function.prototype, or to some other object, you could do the following:

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

This will:

  • Create a new object, 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)

This does not require you to use Object.setPrototypeOf() to change an existing object's prototype because it assigns the prototype at the moment the object is created. See MDN docs for Object.create() for more examples and info -- this does not carry the same scary warnings as setPrototypeOf(). You can use Object.create() to set any new object's prototype.


Answer Two: Functions are Already Objects

That said, if you want an object that behaves like a function, you can simply create a function. A function is still an object, but with additional function-specific properties and behaviors; you can still treat it like an object by assigning properties to it. If you're looking to create a function that also acts like an object, or an object that can also do the same things as a function, then a function already does that. You can do things like:

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

The best solution to your question may be to take advantage of the fact that functions are, in fact, already objects with added function-abilities.

moriah
  • 323
  • 4
  • 16
  • 1
    It doesn't make sense to let an object that is not callable inherit the `Function.prototype` methods. And `Object.create` cannot create callable objects. – Bergi Jan 19 '18 at 14:17
  • Agreed! That said, it’s still important to know how to use Object.create() to assign a new object’s prototype so I think it’s worth including – moriah Jan 20 '18 at 07:08