1

Case 1 :- Initially in object literal I assign __proto__ to null and then I'm trying to reassign it to animal, which is not working.

'use strict'
let animal = {
    eats: true,
    walk(){
        console.log('Animal Walk');
    }
}

let rabbit = {
    jumps: true,
    __proto__: null
}

rabbit.__proto__ = animal
console.log(Object.getPrototypeOf(rabbit))
rabbit.walk()

Case 2:- Now i assign __proto__ in object literal as animal and later i try to reassign it to null, it changes __proto__ to null in this case.

'use strict'
let animal = {
    eats: true,
    walk(){
        console.log('Animal Walk');
    }
}

let rabbit = {
    jumps: true,
    __proto__: animal
}

rabbit.__proto__ = null
console.log(Object.getPrototypeOf(rabbit))
rabbit.walk()

Case 3:- When i use Object.setPrototypeOf() it works fine

'use strict'
let animal = {
    eats: true,
    walk(){
        console.log('Animal Walk');
    }
}

let rabbit = {
    jumps: true,
    __proto__: null
}

Object.setPrototypeOf(rabbit, animal)
console.log(Object.getPrototypeOf(rabbit))
rabbit.walk()

Why isn't the assignment working in the first case?

Nathan Rice
  • 3,091
  • 1
  • 20
  • 30
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
  • Shouldn't it be animal.__proto__ instead of just animal ? – TheoWckr Apr 04 '20 at 09:42
  • @TheoWckr naah, it does work fine i remove `rabbit.__proto__ = null` line. and i need to set prototype as `animal` not as `animal's prototype` – Code Maniac Apr 04 '20 at 09:44
  • 1
    @TheoWckr - Not if Code Maniac wants `animal`, not its prototype, to be the prototype of `rabbit`, which they seem to (since `animal` has `walk` but its prototype doesn't). – T.J. Crowder Apr 04 '20 at 09:49

2 Answers2

4

Okay, this is a bit tricky. :-) There are two different __proto__ meanings being used by your code:

  1. The __proto__ token in object literals that can specify the object's initial prototype, and
  2. The __proto__ accessor property (when you get/set it after object creation)

The reason rabbit.__proto__ = animal doesn't change rabbit's prototype is that __proto__ is an accessor property defined by Object.prototype. Since you create rabbit without any prototype (by using __proto__: null in the object literal), rabbit doesn't inherit that accessor, so rabbit.__proto__ = animal just creates a property on rabbit called __proto__ rather than changing its prototype.

Both of those meanings for __proto__ are legacy web-only (Annex B) compatibility things. Use Object.create (to create an object with a specific prototype), Object.getPrototypeOf, and (if you can't avoid changing an object's prototype) Object.setPrototypeOf. JavaScript engines outside of browsers aren't required to implement Annex B features like __proto__ (though in my experience, engines don't disable most of them outside browsers).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • `The __proto__ property can also be used in an object literal definition to set the object [[Prototype]] on creation, as an alternative to Object.create(). See: object initializer / literal syntax.`. i am wondering about these lines from [`__proto__ MDN`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) – Code Maniac Apr 04 '20 at 09:53
  • @CodeManiac - Right, that's the [first meaning I linked above](https://tc39.es/ecma262/#sec-__proto__-property-names-in-object-initializers). It's an Annex B feature, which means that (officially) only JavaScript engines in web browsers are supposed to support it (although in my experience, engines don't disable most Annex B features outside browsers). You're successfully using that feature creating `rabbit` and as a result, `rabbit` doesn't have any prototype -- which means it doesn't inherit the *other* meaning of `__proto__`, so assigning to `__proto__` doesn't do anything special. – T.J. Crowder Apr 04 '20 at 09:56
  • `If propKey is the String value "__proto__" and if IsComputedPropertyKey(PropertyName) is false, then Let isProtoSetter be true.` isn't it mean that we are setting prototype here ? so if i set `__proto__ : animal` and i remove the `rabbit.__proto__ = null` the code works fine and `rabbit.walk()` is accessible. – Code Maniac Apr 04 '20 at 10:01
  • 1
    @CodeManiac - Yes. As I said above: *"Since you create `rabbit` without any prototype (by using `__proto__: null` in the object literal)..."* The reason assigning to `__proto__` later doesn't work is that after it's created, `rabbit` doesn't inherit from `Object.prototype`, which is what defines the accessor property (the second meaning linked above). – T.J. Crowder Apr 04 '20 at 10:02
  • Ahh it mean if i use `__proto__: null` it just doesn't inherit prototype same as `Object.create(null)`, so now if i do `rabbit.__proto__ = animal` it just creates a new key and do not modify actual prototype. and on the other hand if set it `__proto__ : animal` it does add it to `prototype` and when i do `rabbit.__proto__ = null` it does modify actual prototype, am i getting it right ? – Code Maniac Apr 04 '20 at 10:09
  • @CodeManiac - Right! Exactly! :-) When you start out with `__proto__: animal`, `rabbit` inherits from `animal` which inherits from `Object.prototype` which has the accessor, so `rabbit.__proto__ = null` uses the accessor (and then you won't be able to use it again, since `rabbit` no longer inherits from `Object.prototype`). – T.J. Crowder Apr 04 '20 at 10:20
  • Ahh got it thanks for your time and well detailed explanation, always learn something good from you ✌️ – Code Maniac Apr 04 '20 at 10:33
-1

I think, cause __proto__ can be set only in constructor property. you cant set it. The setter won't work outside of the constructor. However, setPrototypeOf is an extended util function to create a prototype property. Here setPrototypeOf set proto(override) if available, else it create new class with the prototype.

From developer.mozilla.org

if (!Object.setPrototypeOf) {
    // Only works in Chrome and FireFox, does not work in IE:
     Object.prototype.setPrototypeOf = function(obj, proto) {
         if(obj.__proto__) {
             obj.__proto__ = proto;
             return obj;
         } else {
             // If you want to return prototype of Object.create(null):
             var Fn = function() {
                 for (var key in obj) {
                     Object.defineProperty(this, key, {
                         value: obj[key],
                     });
                 }
             };
             Fn.prototype = proto;
             return new Fn();
         }
     }
}
xdeepakv
  • 7,835
  • 2
  • 22
  • 32
  • 2
    *"I think, cause __proto__ is only in constructor property. you cant set it."* Yes, you can. You just can't if you don't inherit from `Object.prototype` (or otherwise define `__proto__` as an accessor property). – T.J. Crowder Apr 04 '20 at 09:48
  • but see mozila defination, they also not creating. Else here create a new class. – xdeepakv Apr 04 '20 at 09:49
  • 1
    *"but see mozila defination, they also not creating"* I'm afraid I have no idea what you mean by that, but I have a deep understanding of the plumbing of `__proto__` from reading the specification. See my answer for what's going on in the OP's code. – T.J. Crowder Apr 04 '20 at 10:04
  • @xdeepakv, it's not the case here, T.J's answer has good explanation of what's going on in my code, anyways thanks for your time i appreciate it ✌️ p.s. : i am not the downvoter though :p – Code Maniac Apr 04 '20 at 10:36
  • @CodeManiac: i had downvoted too ur question :) p.s back ;) :P so no worries. keep coding!! – xdeepakv Apr 04 '20 at 13:52
  • @t.j nice! Thanks for sharing. – xdeepakv Apr 04 '20 at 13:57