16

Mozilla claimed it would remove __proto__ a while back (~2008) and it is still in the browser. Is it still going to be deprecated? It works in Opera, (Safari I think) and Chrome as well. I don't need to worry about IE so I would love to keep using it.

However, I don't want my code to stop working one day, so on to my question:

__proto__ allows for dead simple inheritance:

a.__proto__ = {'a':'test'}

Is there anyway I can replicate this in a standards compliant way? I know there is functional inheritance, that's ugly, and it over-complicates the fact that I just want to create a prototype chain. Just wondering if any wizards have solved this.

Thanks

jdw
  • 1,533
  • 3
  • 17
  • 26
  • 3
    It appears that `__proto__` may be standardized in the next ECMAScript. Can't be absolutely certain until finalized. http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts ...at least it's referenced in the current draft in Annex B, Additional Features for Web Browsers. – cliffs of insanity May 07 '12 at 04:44

3 Answers3

27

Note: It's considered a bad practice to change the value of __proto__. Doing so is strongly discouraged by Brendan Eich, the creator of JavaScript, amongst others. In fact the __proto__ property has been removed entirely from a few JavaScript engines like Rhino. If you wish to know why then read the following comment by Brendan Eich.

Update: Browsers are not going to remove the __proto__ property. In fact, ECMAScript Harmony has now standardized both the __proto__ property and the setPrototypeOf function. The __proto__ property is only supported for legacy reasons. You are strongly advised to use setPrototypeOf and getPrototypeOf instead of __proto__.

Warning: Although setPrototypeOf is now a standard, you are still discouraged from using it because mutating the prototype of an object invariably kills optimizations and makes your code slower. In addition, the use of setPrototypeOf is usually an indication of poor quality code.


You don't need to worry about your existing code not working one day. The __proto__ property is here to stay.

Now, for the question at hand. We want to do something similar to this in a standards compliant way:

var a = {
    b: "ok"
};

a.__proto__ = {
    a: "test"
};

alert(a.a); // alerts test
alert(a.b); // alerts ok

Obviously you can't use Object.create to achieve this end since we are not creating a new object. We are just trying to change the internal [[proto]] property of the given object. The problem is that it's not possible to change the internal [[proto]] property of an object once it's created (except via using __proto__ which we are trying to avoid).

So to solve this problem I wrote a simple function (note that it works for all objects except for functions):

function setPrototypeOf(obj, proto) {
    var result  = Object.create(proto);
    var names   = Object.getOwnPropertyNames(obj);
    var getProp = Object.getOwnPropertyDescriptor;
    var setProp = Object.defineProperty;
    var length  = names.length;
    var index   = 0;

    while (index < length) {
        var name = names[index++];
        setProp(result, name, getProp(obj, name));
    }

    return result;
}

So we can now change the prototype of any object after it's created as follows (note that we are not actually changing the internal [[proto]] property of the object but instead creating a new object with the same properties as the given object and which inherits from the given prototype):

var a = {
    b: "ok"
};

a = setPrototypeOf(a, {
    a: "test"
});

alert(a.a); // alerts test
alert(a.b); // alerts ok
<script>
function setPrototypeOf(obj, proto) {
    var result  = Object.create(proto);
    var names   = Object.getOwnPropertyNames(obj);
    var getProp = Object.getOwnPropertyDescriptor;
    var setProp = Object.defineProperty;
    var length  = names.length;
    var index   = 0;

    while (index < length) {
        var name = names[index++];
        setProp(result, name, getProp(obj, name));
    }

    return result;
}
</script>

Simple and efficient (and we didn't use the __proto__ property).

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • 3
    You're literally telling people to write broken code, against the explicit advice of both the standards bodies and JS engine implementors. Depending on bugs like this is how the Windows APIs and internals got to be such a mess. If you can avoid it, you really shouldn't. And I really don't think you should be recommending it without any caveats. Neither you nor I are a higher authority than the specification or the implementors of the language. Remember: There isn't actually a version of JavaScript that includes `__proto__` — it's an internal detail that some implementations happen to expose. – Chuck May 07 '12 at 07:39
  • 8
    @Chuck, the `__proto__` property is being considered to become a standard in the next version of JavaScript as @cliffsofinsanity pointed out above. The implementors advise against using it because it's not a standard yet. Hence it may not work in all JavaScript engines. It's always a good idea to understand why a certain feature is not advised to be used instead of just criticizing people. Using the `__proto__` property on the web is perfectly alright since all the major browsers support it. I don't see any caveat of using it save that it won't work in Rhino. It has nothing to do with Windows. – Aadit M Shah May 07 '12 at 11:37
  • @AaditMShah It is considered to be _optional_ in implementations of JS according to harmony. I completely agree with Chuck. See https://mail.mozilla.org/pipermail/es-discuss/2010-April/010917.html – Benjamin Gruenbaum Mar 30 '13 at 21:56
  • @BenjaminGruenbaum - I don't think my answer deserves a negative vote. Read it carefully. I'm not __encouraging__ you to use `__proto__`. I'm simply saying that you __can__ use it today without having to worry about your code not working in the future. That's just half my answer. The other half deals with changing the internal `[[proto]]` of an object without setting `__proto__` and I think that alone deserves an upvote - it's encourages people to think outside the box and certainly doesn't encourage using `__proto__`, and I'm not a `__proto__` evangelist anymore: github.com/javascript/augment – Aadit M Shah Mar 31 '13 at 00:27
  • (I'm sorry, it seems like you put a lot of effort into this answer,I appreciate it) You are not discouraging the use proto either, which is just as bad in my book. I'm glad you're not a 'proto evangalist anymore' but I would appreciate being more critical of the use of settable proto. Setting proto introduces logical problems in inheritance (Does a Dog suddenly _stop_ being an Animal?). More than that, OP asked for standards complaint code, an Object.create approach solves his issue correctly. By the way, your setPrototypeOf doesn't work http://codepen.io/anon/pen/rwvke . – Benjamin Gruenbaum Mar 31 '13 at 00:43
  • @BenjaminGruenbaum - You need to use it as `a = setPrototypeOf(a, e)` - see the last part of my answer. It creates a copy of the object you pass to it with the new prototype. Hence you need to reassign the variable `a` to point to the new object. It's impossible to change the internal `[[proto]]` of `a` without using `__proto__`. This is the next best thing. See the working demo: http://jsfiddle.net/LCWC7/ – Aadit M Shah Mar 31 '13 at 01:25
  • First, I can't, a down-vote is locked after 10 minutes, reverting it would require an edit. Second I would appreciate, in this sort of answer, some pros and cons about settable proto, at least _mention_ the fact that people like Eich, Crockford and Resig all believe it is a _horrible_ idea. If you at least _mention_ the pros and cons instead of implying it is ok to use, I'll gladly revert that down-vote to an up-vote. – Benjamin Gruenbaum Mar 31 '13 at 12:31
  • Great, much better. although the problem isn't with changing proto, it's with changing proto _except_ in the object creation case. Which is still very much possible in ES5 with Object.create :) Good discussion. – Benjamin Gruenbaum Mar 31 '13 at 14:43
  • 1
    @BenjaminGruenbaum - I agree. In fact I had [proposed a solution](https://github.com/ringo/ringojs/issues/181 "Add native support for an instantiate function in JavaScript · Issue #181 · ringo/ringojs") to solve this problem. Unfortunately it fell on deaf ears. May I trouble you to read it and tell me your thoughts? I believe that my [instantiate](http://stackoverflow.com/q/11490606/783743 "Instantiate JavaScript functions with custom prototypes - Stack Overflow") function is a powerful asset for JavaScript programmers, and since it doesn't use `__proto__` it's safe to use. – Aadit M Shah Mar 31 '13 at 16:46
  • If you believe you have a strong case to argue, you're welcome to the JavaScript chat room here in SO. Some room regulars there know people from the ECMA committee and W3C. – Benjamin Gruenbaum Mar 31 '13 at 17:13
  • Could you please update this answer with the outcome of your efforts (ES6)? – Bergi Jun 29 '15 at 12:51
  • 1
    @Bergi I updated my answer by removing a lot of outdated information and adding the outcome of ES6 proposal. – Aadit M Shah Jul 01 '15 at 18:26
  • 1
    @AaditMShah: Thanks a lot! Not sure why you called `setPrototypeOf` "unsafe" though? Btw, it seems ES6 standardized `Reflect.setPrototypeOf`, while all browsers support `Object.setPrototypeOf`. I see further confusing upcoming :-) – Bergi Jul 01 '15 at 18:39
  • 1
    @Bergi It's not really "unsafe". I wanted a synonym for "discouraged". I didn't know about `Reflect.setPrototypeOf` but since it seems to be related to the reflection API I don't see how it would cause any confusion. Only the most skillful JavaScript programmers would use the reflection API and there wouldn't be any shadow of doubt in their minds when they would refer to `Reflect.setPrototypeOf`. – Aadit M Shah Jul 01 '15 at 18:47
  • Thanks, that's what I figured. "*`__proto__` and the unsafe `setPrototypeOf`*" just sounded as if it was even less "safe" than `__proto__` :) – Bergi Jul 01 '15 at 19:02
7

I think the actual point Mozilla wanted to make is that it's nonstandard, so the implementors would be perfectly within their rights removing it.

The cleaner way to do prototype chains is Object.create. The equivalent of your code, to create an object a with the prototype {'a': 'test'}, is:

a = Object.create({'a':'test'})

There are also shims to mimic this function in browsers that don't support it, if you ever need to work with one, which is another advantage over directly messing around with __proto__.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • I don't think the OP wants to create a new object with a given prototype. I think he wants to change the prototype of an existing object without modifying `__proto__`. – Aadit M Shah May 07 '12 at 05:48
  • 1
    @AaditMShah: His description was that he "just want[s] to create a prototype chain" in contrast to "functional inheritance." This suggests that he wants pure prototype inheritance, which is what Object.create is for. Dynamically twiddling prototypes is useful, but not as commonly needed. – Chuck May 07 '12 at 06:03
  • Also, mutating object by reassigning prototype is much slower and discouraged. Object.create is much faster way. – Nitin Jadhav Oct 17 '14 at 02:31
1

I don't see how:

var a = {};
a.__proto__ = A; // A is object because no constructor is needed

Is simpler than:

var a = new A(); // A is Constructor function

Now setting up A in the latter case can be more verbose if you don't need a constructor function, but in that case you could automate it to be just as simple:

var A = Constructor({
    method: function(){}
}); // A is function, meant to be used with new

Compared to:

var A = {
    method: function(){}
}; //A is object, meant to be set as __proto__

And if you need some initialization logic, it will probably end up being easier just to use the normal way where the constructor function needs to be declared anyway

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • For init logic, a factory can be used instead of relying on a constructor. You need to dig a little deeper to see the value. If your two versions had prototypes, Object.create is a little simpler. See: function Constructor(){}; Constructor.prototype = { /*methods*/ }; var newObject = new Constructor(); **VS** var proto = { /*methods*/ }; var newObject = Object.create(proto);. I just created a new prototypal object in less code than defining my classical inheritance one. – Drew Dec 01 '12 at 16:18
  • 1
    @Drew First of all, the first one doesn't use classical inheritance. Secondly, it's only the one-time setup cost that takes more code, the actual creation of objects is not only less code but allows init logic without creating a separate factory. Remember that the constructor in JS runs like any other function, it is not limited like constructors in classical language, so many reasons to create a factory in those languages don't apply to js. – Esailija Dec 01 '12 at 16:40