Prototype property
Every object in JavaScript has a prototype. This prototype can be seen by accessing __proto__ property in recent modern browsers except IE.
var n = 5; console.log(n.__proto__); var myObj = {}; console.log(myObj.__proto__);
Function prototype property
There is another similar term that is quite confusing in JavaScript world, it is the prototype property. This is not the real prototype property like the one above. However this is a property that is found in every function in JavaScript. I prefer to call this property; function prototype.
function Person(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; this.fullName = function () { return this.firstName + ' ' + this.lastName; } } console.log(Person.prototype); // below will yield 'undefined' because it is not a function var n = 5; console.log(n.prototype); // this will be false as a function prototype is not the same as its prototype property Person.prototype == Person.__proto__;
Creating object with new keyword
When we initialise a new object with new keyword, the object's real prototype will point to the constructor function's function prototype property.
var john = new Person('john', 'king'); // john's prototype will point to Person's function prototype property console.log(john.__proto__ == Person.prototype) // trueThe new object will have whatever defined inside the constructor function plus access to whatever in its prototype. So if we add more properties to the constructor function's function prototype, these will be accessible by the created objects. This will be explained more in details below.
A very important rule that need to be remembered from here is:
newObject.__proto__ = TheFunction.prototype
Prototypes chain
In JavaScript, when an object property is called, if it is not found within the object, JavaScript will try to traverse up it's prototypes chain to find it. If it already reaches the root but still cannot find the property, undefined will be returned.
var animal = { name: 'animal', eats: true }; var fish = { name: 'fish', swims: true }; // set fish prototype to animal fish.__proto__ = animal; var tuna = { name: 'tuna', canBeCanned: true }; // set tuna prototype to fish tuna.__proto__ = fish; console.log(tuna.swims); console.log(tuna.eats);Below is the structure created (on Firebug):
As we can see tuna's prototype is fish object and fish's prototype is animal object. The text in bold are properties that are available to the containing objects.
Adding a property to a prototype chain
When we have an established prototypes chain then if we added a new property to one of the prototype objects in the chain, this new property would be accessible by the lower objects in the chain.
// based on the example above we add a new property to animal animal.moves = true; console.log(tuna.moves) // lower objects in the chain can access it // we can also add a new property through a prototype link // this example below add a new property at animal's level because we have set fish.__proto__ = animal fish.__proto__.breathes = true; console.log(tuna.breathes)
Using these prototype behaviours for object oriented approach
Having knowledge of prototype behaviours described above, we can implement object oriented concept in our JavaScript codes. We will also need to use function prototype and new keyword to set up relationships as the __proto__ property is not available in some browsers. As an example, we could have something like this:
function Coder() { this.name = 'coder'; this.code = function () { return "coding now"; } } function JSCoder() { this.name = 'JSCoder'; this.writeSomeJSCodes = function () { return "writing: this.__proto__ = . . ."; } } // set up the relationship JSCoder.prototype = new Coder(); // this is saying that every newly created JSCoder object will have its prototype property points to a Coder object var jack = new JSCoder(); // because we have set above JSCoder.prototype = new Coder() // and because of the rule: newObject.__proto__ = TheFunction.prototype // then this one below is implemented implicitly when jack is created: // jack.__proto__ = JSCoder.prototype which equals to a Coder object ( new Coder() ) console.log(jack.writeSomeJSCodes()); console.log(jack.code()); // we can also add a new property to a function prototype then it will be accessible by existing child object(s) JSCoder.prototype.debugging = function () { return 'debugging some codes' }; // this is saying that because of the rule: newObject.__proto__ = TheFunction.prototype // when a new JSCoder object is created: var jack = new JSCoder() // then this will apply: jack.__proto__ = JSCoder.prototype // and JSCoder.prototype has been set to a Coder object ( new Coder() ) // so when a new property is added, // JSCoder.prototype is a Coder object plus the new property // thus jack's prototype ( jack.__proto__ ) is a Coder object plus the new property console.log(jack.debugging());
What will happen if we modify the function prototype to point to a new object? Existing objects will not be affected, however new objects created after the modification will have their prototypes point to the new object.
// modify JSCoder function prototype to point to a new object JSCoder.prototype = { sing: function () { return 'singing' } }; console.log(jack); // existing objects' prototypes do not change // however new objects' prototypes will point to the new object var jim = new JSCoder(); console.log(jim);
References and further readings:
A Plain English Guide to JavaScript Prototypes
Understanding “Prototypes” in JavaScript
Understanding JavaScript Prototypes
Prototypal inheritance
OOP in JS, Part 2 : Inheritance
No comments:
Post a Comment