Prototype 1 !FREE!
It should not be confused with the func.prototype property of functions, which instead specifies the [[Prototype]] to be assigned to all instances of objects created by the given function when used as a constructor. We will discuss the prototype property of constructor functions in a later section.
Classes are syntax sugar over constructor functions, which means you can still manipulate Box.prototype to change the behavior of all instances. However, because classes are designed to be an abstraction over the underlying prototype mechanism, we will use the more-lightweight constructor function syntax for this tutorial to fully demonstrate how prototypes work.
It may be interesting to note that due to historical reasons, some built-in constructors' prototype property are instances themselves. For example, Number.prototype is a number 0, Array.prototype is an empty array, and RegExp.prototype is /(?:)/.
You may also see some legacy code using Object.create() to build the inheritance chain. However, because this reassigns the prototype property and removes the constructor property, it can be more error-prone, while performance gains may not be apparent if the constructors haven't created any instances yet.
We can now use the new operator to create an instance of doSomething() based on this prototype. To use the new operator, call the function normally except prefix it with new. Calling a function with the new operator returns an object that is an instance of the function. Properties can then be added onto this object.
As seen above, the [[Prototype]] of doSomeInstancing is doSomething.prototype. But, what does this do? When you access a property of doSomeInstancing, the runtime first looks to see if doSomeInstancing has that property.
If doSomeInstancing does not have the property, then the runtime looks for the property in doSomeInstancing.[[Prototype]] (a.k.a. doSomething.prototype). If doSomeInstancing.[[Prototype]] has the property being looked for, then that property on doSomeInstancing.[[Prototype]] is used.
Otherwise, if doSomeInstancing.[[Prototype]] does not have the property, then doSomeInstancing.[[Prototype]].[[Prototype]] is checked for the property. By default, the [[Prototype]] of any function's prototype property is Object.prototype. So, doSomeInstancing.[[Prototype]].[[Prototype]] (a.k.a. doSomething.prototype.[[Prototype]] (a.k.a. Object.prototype)) is then looked through for the property being searched for.
If the property is not found in doSomeInstancing.[[Prototype]].[[Prototype]], then doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]] is looked through. However, there is a problem: doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]] does not exist, because Object.prototype.[[Prototype]] is null. Then, and only then, after the entire prototype chain of [[Prototype]]'s is looked through, the runtime asserts that the property does not exist and conclude that the value at the property is undefined.
The lookup time for properties that are high up on the prototype chain can have a negative impact on the performance, and this may be significant in the code where performance is critical. Additionally, trying to access nonexistent properties will always traverse the full prototype chain.