some cleanup...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2014-09-25 22:36:33 +04:00
parent 0553ec9845
commit e63028904c

164
js-oop.js
View File

@ -30,17 +30,41 @@
// object it resolves to the object's "prototype" and so on, these // object it resolves to the object's "prototype" and so on, these
// chians can of any length. // chians can of any length.
// //
// NOTE: there is also a second mechanism available but we'll discuss // Note that this works for reading, when writing or deleting we are
// it a bit later in // affecting ONLY the local object and attributes explicitly defined in
// it, or its' "own" attributes.
b.x = 321
b.x // -> 321
a.x // -> 1
// Notice that a.x is no longer visible from b, this is called "shadowing"
// and a.x is shadowed by b.x, now let us delete x from b to reveal the
// shadowed a.x
delete b.x
b.x // -> 1
// Trying to delete .x from b again will have no effect, this is because
// .x no longer exists in b
delete b.x
b.x // -> 1
// Now back to the mechanism that makes all of this work...
// //
// A couple of easy ways to see these sets of attributes: // A couple of easy ways to see the local and non-local sets of
// attributes:
// show local or "own" only attribute names (keys)...
Object.keys(b) // -> z Object.keys(b) // -> z
// show all accessible keys...
for(var k in b){ console.log(k) } for(var k in b){ console.log(k) }
// -> x, y, z // -> x, y, z
// Another way to test if the attribute is "local" ("own"): // Another way to test if the attribute is own/local
b.isOwnProperty('z') // -> true b.isOwnProperty('z') // -> true
b.isOwnProperty('x') // -> false b.isOwnProperty('x') // -> false
@ -51,6 +75,10 @@
b.__proto__ === a // -> true b.__proto__ === a // -> true
// NOTE: we did not see .__proto__ in the list of accessible attributes
// because it is a special attributes, it is implemented internally
// and is not enumerable.
//
// Thus, we could define our own create function like this: // Thus, we could define our own create function like this:
function clone(from){ function clone(from){
@ -61,6 +89,7 @@
var c = clone(b) var c = clone(b)
// Out of curiosity let's see if .__proto__ is defined on a basic object // Out of curiosity let's see if .__proto__ is defined on a basic object
var x = {} var x = {}
@ -74,18 +103,18 @@
// We will discuss what this means and how we can use this in the next // We will discuss what this means and how we can use this in the next
// sections... // sections...
//
//
//
// The Constructor Mechanism // The Constructor Mechanism
// ------------------------- // -------------------------
// //
// JavaScript provides a second, complementary mechanism to inherit // JavaScript provides a second, complementary mechanism to inherit
// attributes, it resembles the class/object relationship in languages // attributes, it resembles the class/object relationship in languages
// like C++ but this resemblance is on the surface only as it still // like C++ but this resemblance is on the surface only, as it still
// uses the same prototype mechanism as the above. // uses the same prototype mechanism as the above.
// //
// We will start by creating a constructor: // We will start by creating a "constructor":
function A(){ function A(){
this.x = 1 this.x = 1
@ -93,43 +122,40 @@
} }
// Technically a constructor is just a function, what makes it a // Technically a constructor is just a function, what makes it a
// "constructor" is how we use it... // "constructor" is only how we use it...
var a = new A() var a = new A()
// what 'new' does here is: // what 'new' does here is:
// 1) creates an empty object // 1) creates an empty object
// 2) sets a bunch of attributes on it // 2) sets a bunch of attributes on it, we'll skim this part for now
// 3) passes it to the constructor via 'this' // 3) passes the new object to the constructor via 'this'
// 4) after the constructor returns, this object is returned // 4) after the constructor finishes, this object is returned
// //
// We could write an equivalent (simplified) function: // We could write an equivalent (simplified) function:
function construct(func){ function construct(func){
var obj = {} var obj = {}
// set some special attributes on obj...
return func.apply(obj) return func.apply(obj)
} }
var b = construct(A) var b = construct(A)
// But what makes this interesting? At this point this all looks like // But what does make this interesting? At this point this all looks like
// all we did is moved attribute from a literal object notation to a // all we did is move attribute definition from a literal object notation
// constructor function, effectively adding complexity. What are we // into a constructor function, effectively adding complexity. What are we
// getting back from this? // getting back from this?
// //
// Let's look at a number of attributes: // Let's look at a number of attributes that new sets:
a.__proto__ // -> {} a.__proto__ // -> {}
a.constructor // -> [Function A] a.constructor // -> [Function A]
// The answer lies in the attributes that are set on the object, lets // These are what makes this fun, lets write a more complete new
// write a more complete reference implementation: // implementation:
function construct(func, args){ function construct(func, args){
var obj = {} var obj = {}
@ -147,6 +173,7 @@
var b = construct(A) var b = construct(A)
// Notice that we return the resulting object in a more complicated // Notice that we return the resulting object in a more complicated
// way, this will come in handy later. // way, this will come in handy later.
// //
@ -155,8 +182,8 @@
// First let us cover the default. Each time a function is created in // First let us cover the default. Each time a function is created in
// JavaScript it will get a new empty object assigned to it's .prototype // JavaScript it will get a new empty object assigned to it's .prototype
// attribute. // attribute.
// On the function level, in general, this is not used, but this is used // On the function level, in general, this is not used, but this is very
// when we use the function as a constructor. // useful when the function is used as a constructor.
// //
// As we can see from the code above, the resulting object's .__proto__ // As we can see from the code above, the resulting object's .__proto__
// points to the constructor's .prototype, from the previous section // points to the constructor's .prototype, from the previous section
@ -179,6 +206,7 @@
a.y // -> 321 a.y // -> 321
a.z // -> 333 a.z // -> 333
// These values are accessible from all objects constructed by A since // These values are accessible from all objects constructed by A since
// all of them point to A with both the .constructor and .__proto__ // all of them point to A with both the .constructor and .__proto__
// attributes // attributes
@ -189,10 +217,8 @@
// Double inheritance: // "Double" inheritance
// ------------------- // --------------------
//
// NOTE: this might be implementation specific. Tested and works in IE11, V8
// //
// There are actually three sources where JavaScript looks for attributes: // There are actually three sources where JavaScript looks for attributes:
// 1) the actual object // 1) the actual object
@ -200,9 +226,16 @@
// as coverd in the first section // as coverd in the first section
// 3) .constructor.prototype // 3) .constructor.prototype
// as explained in the previous section // as explained in the previous section
//
// Here is a basic inheritance structure (tree):
//
// O A
// \ /
// a
//
var O = { var O = {
o: 0 o: 0,
} }
function A(){} function A(){}
@ -218,7 +251,7 @@
// The check is done specifically in this order, thus attributes can // The check is done specifically in this order, thus attributes can
// "shadow" other attributes defined later in the chain. // "shadow" other attributes defined by the other mechanism.
// //
// To show this let us define an attribute with the same name on both // To show this let us define an attribute with the same name on both
// 'O' and 'A': // 'O' and 'A':
@ -230,9 +263,10 @@
// In both inheritance mechanisms, each step is checked via the same // In both inheritance mechanisms, each step is checked via the same
// rules recursively, this enables inheritance chains. // rules recursively, this enables inheritance chains and less
// conveniently inheritance trees (superposition of chains).
// //
// We will create a cahin: // We will create a chain:
// //
// c -> b -> a // c -> b -> a
// //
@ -333,6 +367,7 @@
// -> false // -> false
// Checking type (typeof) // Checking type (typeof)
// ---------------------- // ----------------------
// //
@ -374,14 +409,15 @@
var o = { f: f } var o = { f: f }
// Thus we call the attribute .f of object o a method. // Thus we call the attribute .f of object o a "method" of object o.
// //
// //
// 'this' is a reserved word and is available in the context of a function // 'this' is a reserved word and is available in the context of a function
// execution, not just in methods, but what value it references depends // execution, not just in methods, but what value it references depends
// on how that function is called... // on how that function is called...
// This is mostly useful and used in methods.
// //
// a simple way to think about is that 'this' always points to the // A simple way to think about this is that 'this' always points to the
// "context" of the function call. // "context" of the function call.
// //
// This context can be: // This context can be:
@ -404,9 +440,67 @@
// as first argument: // as first argument:
f.call(o) // o is the context f.call(o) // o is the context
f.apply(o) // o is the context f.apply(o) // o is the context
// Properties
// ----------
// //
// A property is a special attribute that has a getter, setter methods
// and/or other optional configuration.
// //
// // A good property example is .length of Array objects.
var o = {
get x(){
return this.data || 123
},
set x(value){
this.data = value
},
}
o.x // -> 123
o.x = 4
o.x // -> 4
o.x = undefined
o.x // -> 123
// As for any other attribute, deleting a local property x will remove
// it from the containing object...
delete o.x
o.x // -> undefined
// The above code is a shorthand for:
Object.defineProperty(o, 'y', {
get: function() {
return this.data || 123
},
set: function(name) {
this.data = value
}
})
// XXX other property attributes...
// get
// set
// value
// if set get/set are not possible...
// writable (false)
// configurable (false)
// is it possible to change this configurations later...
// enumerable (false)
// Common use-cases
// ----------------
// //
/********************************************************************** /**********************************************************************
* vim:set ts=4 sw=4 : */ * vim:set ts=4 sw=4 : */