I'm not that into dynamic programming languages but I've written my fair share of JavaScript code. I never really got my head around this prototype-based programming, does any one know how this works?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
I remember a lot discussion I had with people a while back (I'm not exactly sure what I'm doing) but as I understand it, there's no concept of a class. It's just an object, and instances of those objects are clones of the original, right?
But what is the exact purpose of this ".prototype" property in JavaScript? How does it relate to instantiating objects?
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
Also these slides really helped a lot.
Every JavaScript object has an internal "slot"(i.e. hidden property) called [[Prototype]]
(square brackets are deliberate) whose value is either null
or an object
. The value is colloquially known as "the prototype of that object."
These prototypes connect objects into prototype chains for looking up properties. When you try to access a property from an object by either obj.propName
or obj['propName']
, and the object does not own(obj.hasOwnProperty('propName')
) the property, the runtime will try to find the property from the prototype chain of that object by recursively referencing the [[Prototype]]
slots until a null
is reached.
An object's [[Prototype]]
is initially set during object creation, and modern JavaScript allows read and write access to the [[Prototype]]
in the following ways:
new Ctor()
syntax, which sets [[Prototype]]
to Func.prototype
for the newly created object.extends
keyword, which configures the prototype chain for the class syntax.Object.create
, which will set the supplied argument as the [[Prototype]]
of the resulting object.Object.getPrototypeOf
and Object.setPrototypeOf
(get/set the [[Prototype]]
after object creation)__proto__
. (but it has unusual behaviours when an object has a prototype of null
.)Note that the .prototype
is a genuine property, not an internal slot. Therefore, all classes, and all functions that can be used with the new
operator, have a property named .prototype
in addition to their own [[Prototype]]
internal slot. This dual use of the word "prototype" is the source of endless confusion amongst newcomers to the language.
Before JavaScript introduced the class syntax, people used the syntax new Ctor()
to simulate classical inheritance by prototypical inheritance:
.prototype
property.For example, here's one way:
function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }
function inherit(child, parent) {
child.prototype = Object.create(parent.prototype)
child.prototype.constructor = child
return child;
}
Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'
...and here's another way:
function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }
function inherit(child, parent) {
function tmp() {}
tmp.prototype = parent.prototype
const proto = new tmp()
proto.constructor = child
child.prototype = proto
return child
}
Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'
But if you're careful, you might have found that Parent
's instance fields are not considered in these two cases. Fortunately, in ES2015 these details are hidden and we can just write a one-liner version thanks to the extends
syntax:
class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'
...the resulting object's [[Prototype]]
will be set to an instance of Parent
, whose [[Prototype]]
, in turn, is Parent.prototype
.
Finally, if you create a new object via Object.create(foo)
, the resulting object's [[Prototype]]
will be set to foo
.