Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__proto__ and prototype difference [duplicate]

From what I know a function should inherit the properties from its prototype object, which can be accessed using .prototype or __proto__ property.

//my prototype Object
var myObj = {
    a: 1,
    b: 2
};

var myFunc = function () {};

// setting function's `prototype` property
myFunc.prototype = myObj;
alert(myFunc.a);
//returns undefined (Why???) I was expecting 1

But when I tried the following,

//setting function __proto__ property
myFunc.__proto__ = myObj;
//returns 1
alert(myFunc.a);

So why does it work when I set myFunc.__proto__ and not when I set myFunc.prototype?

I did refer to __proto__ VS. prototype in JavaScript but couldn't figure out.

like image 403
Flake Avatar asked Jul 10 '15 12:07

Flake


2 Answers

__proto__

You can actually access the internal [[Prototype]] property of an object with __proto__. You can think of [[Prototype]] as the actual parent of the current object, in the inheritance hierarchy.

prototype

This is a special property, when set on a (constructor) function object, used to establish the inheritance chain for instances created from the constructor. For example,

function Foo() {}
Foo.prototype = {a: 1};

Now, when you create a new object of type Foo, the newly created object's internal [[Prototype]] property will refer the Foo.prototype object. You can confirm that like this

console.assert((new Foo()).__proto__ === Foo.prototype);

In your case,

myFunc.prototype = myObj;

you are creating a prototype property on the function object and this will be used only when you are creating new objects with this function (constructor function). You might want to think of it as a template for the new objects. So, when you do myFunc.a, JS engine tries to find a in myFunc and its parents in the prototype chain and it doesn't find it, that is why it returns undefined.

But, when you do

myFunc.__proto__ = myObj;

you are setting the parent of myFunc, in the prototype chain, to myObj. So, when you do myFunc.a, JS engine first tries to find a in myFunc object itself, and it is not there. So, it tries to find it in its immediate parent, which is myObj. That is why it returns 1 in this case.


Note: You can use the following function to understand the prototype chain better

function printPrototypeChain(object) {
    while (object !== null) {
        console.log(object);
        object = Object.getPrototypeOf(object);
    }
}

Now, let us print the prototype chain when the object is set as the prototype property of the function object.

function myFunc() {}
myFunc.prototype = {
    a: 1,
    b: 2
};
printPrototypeChain(myFunc);

The output will be

[Function: myFunc]
[Function: Empty]
{}

None of these objects have a defined, so undefined is returned. But, in this case,

function myFunc() {}
myFunc.__proto__ = {
    a: 1,
    b: 2
};
printPrototypeChain(myFunc);

the prototype chain becomes like this

[Function: myFunc]
{ a: 1, b: 2 }
{}

and a is found in myFunc's immediate parent. So, corresponding value 1 is returned.

Note: Don't use __proto__ in your actual code, as it is retained in the latest versions of JavaScript specification just for backward compatibility. Read more about it here. Use Object.getPrototypeOf and Object.setPrototypeOf instead.

like image 134
thefourtheye Avatar answered Nov 16 '22 11:11

thefourtheye


You're confusing myFunc, which is a constructor, with an instance of myFunc.

If you do something like this:

var o = new myFunc();
alert(o.a);

It will alert 1 because o is an instance of myFunc, so its a property comes from the myFunc prototype.

If you set __proto__, you're literally swapping out the Function prototype that myFunc was inheriting from before and replacing it with your prototype object. In fact, after you do that, any methods you can normally use on functions, such as call, won't be found on myFunc anymore.

myFunc.__proto__ = myObj;
myFunc.call(null); // Will throw an error
like image 21
Josiah Keller Avatar answered Nov 16 '22 12:11

Josiah Keller