mirror of
https://github.com/flynx/Slang.git
synced 2025-10-29 10:40:07 +00:00
some cleanup...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
0553ec9845
commit
e63028904c
166
js-oop.js
166
js-oop.js
@ -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.
|
||||||
// A couple of easy ways to see these sets of 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 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 : */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user