/********************************************************************** * * The basics of JavaScript OOP * * **********************************************************************/ // // The value of 'this' // ------------------- // function f(){ console.log(this) this.a = 1 } var o = { f: f } // // 'this' is always the "context" of the function call, in JS a context // can be: // - implicit // - root context f() // 'window' or 'module' is implied, this is // equivalent to: window.f() // - 'new' context new f() // here a context will be created and passed to // 'f's 'this', for more details on what 'new' // does see the next section. // - explicit: // - the object on the left side of "." or the [ ] attribute access: o.f() // o is the context o['f']() // the same as the above // - the object explicitly passed to .call(..) or .apply(..) methods // as first argument: f.call(o) // o is the context // // // // What's 'new'? // ------------- // function O(){ this.attr = 123 } var o = new O() // // 'new' creates a new context object and passes it to the function // being called (the constructor), this new object has several special // attributes set: // o.constructor -- references the object (constructor) used to // construct the object o (instance) // o.__proto__ -- references the constructor prototype, same as: o.constructor.prototype === o.__proto__ // this is used to identify the object via: o instanceof O // // The 'new' expression returns the context object after it has been // populated by the constructor function. // // NOTE: when using 'new', the function return value is ignored. // // // The values set on 'this' by the constructor are instance attributes, // i.e. they are stored in the specific instance being created. // // NOTE: calling the constructor is not required, but not calling it // will not run the initialization code that would otherwise // populate the object. i.e. no instance values will be created. // // // The instance has another type of attribute accessible through it that // is not stored in it, but rather in it's prototype (o.__proto__), or // rather the constructor's prototype (o.constructor.prototype). For // more details see the next section. // // // // The object's 'prototype' // ------------------------ // function f(){} console.log(f.prototype) // f {} // // The unique prototype object is created on every function definition. // The prototype is instance-like of the function to which it is assigned. // // NOTE: since the prototype has .__proto__ set to Object, technically // it's not a strict instance of the type it defines, so: f.prototype instanceof f // will return false. // // Each unresolved attribute access on the "instance" will resolve to its // prototype (o.__proto__ or o.constructor.prototype). // O.prototype.x = 321 console.log(o.x) // 321 // // Since the prototype is a JS object that adheres to the same rules as // any other object, if the attr is not resolved in it directly it will // be searched in its prototype, and so on. // This principle enables us to implement inheritance. // var OO = function(){ // call the base constructor... O.call(this) } // chain the two prototypes... OO.prototype = new O() // this is to make things coherent for oo below... OO.prototype.constructor = OO var oo = new OO() console.log(oo.x) // 321 console.log(oo.constructor.name) // 'OO' // // So in the example above local attributes (in 'oo') will be searched // first, then oo.__proto__ (instance of O), then in O.prototype, and // then in Object.prototype. // // oo -> oo.__proto__ -> (oo.__proto__).__proto__ -> ... // // // // /********************************************************************** * vim:set ts=4 sw=4 : */