|
////// |
|
|
|
// this version supports inheritance (pretty much but not "perfect" ~ Date is still fairly intractable) |
|
|
|
// July 30, 2014 ~ start on inheritance |
|
|
|
// August 1, 2014 ~ working ! (pretty much) |
|
|
|
//Function.prototype.create || |
|
(Function.prototype.create = function create(fn, opts) { |
|
|
|
var o, __parent__, thisp; |
|
|
|
// inheritance fork |
|
if (opts && opts['extend']) { |
|
thisp = this.prototype; |
|
this.prototype = __parent__ = new opts['extend'](); |
|
} |
|
|
|
o = Object.create(this.prototype); |
|
|
|
// inheritance fork |
|
__parent__ && (function () { |
|
|
|
for (var i in thisp) { |
|
o[i] = thisp[i]; |
|
} |
|
|
|
o.__parent__ = __parent__; |
|
|
|
// Date needs these |
|
__parent__ instanceof Date && ( |
|
o.toString = function() { |
|
return this.__parent__.toString(); |
|
}, |
|
o.valueOf = function () { |
|
return this.__parent__.valueOf(); |
|
}); |
|
|
|
// Array and String need these to return a copy |
|
'concat' in __parent__ && ( |
|
o.slice = function () { |
|
return this.__parent__.slice.apply(this, arguments); |
|
}, |
|
o.concat = function () { |
|
return this.__parent__.concat.apply(this.slice(), arguments); |
|
}); |
|
}()); |
|
|
|
// set default assignments |
|
opts && typeof opts == 'object' && (function() { |
|
for (var k in opts) { |
|
if (opts.hasOwnProperty(k)) { |
|
o[k] = opts[k]; |
|
} |
|
} |
|
}()); |
|
|
|
// support zero-arg calls (no fn arg) |
|
fn && typeof fn == 'function' && fn.call(o, o); |
|
|
|
return o; |
|
}); |
|
|
|
// Now, every function has its own .create() method which takes an initializer |
|
// function, which in turn takes an instance created from the prototype of the |
|
// original function. |
|
|
|
////// |
|
|
|
// simple object with a default name property |
|
|
|
function simple(fn) { |
|
return simple.create(fn, { name: 'default-simple' }); |
|
} |
|
|
|
|
|
// zero-arg call returns bare instance of the simple "type" |
|
var anon = simple(); |
|
|
|
// call with initializer fn to override default name |
|
var named = simple(function(simple){ |
|
// EITHER |
|
simple.name = 'overridden'; |
|
// OR |
|
this.name = 'overridden'; |
|
}); |
|
|
|
console.log(anon); // simple {name: "default"} |
|
console.log(named); // simple {name: "overridden"} |
|
console.log(anon instanceof simple); // true |
|
|
|
// compound inherits simple |
|
|
|
function compound(fn) { |
|
return compound.create(fn, { extend: simple }); |
|
} |
|
|
|
var comp = compound(function() { |
|
console.log('compound created'); |
|
}); |
|
|
|
console.log(comp instanceof simple); |
|
console.log(comp instanceof compound); |
|
|
|
// deep inherits compound |
|
|
|
function deep(fn) { |
|
return deep.create(fn, { extend: compound }); |
|
} |
|
|
|
deep.prototype.log = function(s) { |
|
console.log(s); |
|
}; |
|
|
|
// return nothing, just do some work |
|
|
|
deep(function() { |
|
|
|
console.log('deep: ' + this.name); |
|
console.log(this); |
|
|
|
this.log('deep created with name: ' + this.name); |
|
this.log(this instanceof deep); |
|
this.log(this instanceof simple); |
|
this.log(this instanceof compound); |
|
this.log(this.__parent__ instanceof compound); |
|
this.log(this.__parent__ instanceof simple); |
|
this.log(this.__parent__.__parent__ instanceof simple); |
|
}); |
|
|
|
|
|
// inheritance with native Array ~ pretty close |
|
|
|
function array(fn) { |
|
array.create(fn, { extend: Array }); |
|
} |
|
|
|
array(function(array) { |
|
|
|
console.log(array); // Object[] |
|
console.log('__parent__'); |
|
console.log(array.__parent__); // [ ] |
|
|
|
array.push(1,2,3); |
|
console.log('array: ' + array); // array: 1,2,3 |
|
|
|
// CUTTING LENGTH DIRECTLY... |
|
console.log(array.length); // 3 |
|
array.length = 1; |
|
|
|
console.log(array.length); // 1 |
|
|
|
console.log(array); // Object[1] |
|
console.log('__parent__'); |
|
console.log(array.__parent__); // [ ] |
|
|
|
console.log('Array instance: ' + (this instanceof Array)); // true |
|
}); |
|
|
|
// deep inheritance with native Date ~ demonstrates the issues when inheriting |
|
// Date instance methods |
|
|
|
function date(fn) { |
|
return date.create(fn, { extend: Date }); |
|
}; |
|
|
|
function subdate(fn) { |
|
return subdate.create(fn, { extend: date }); |
|
}; |
|
|
|
var s = subdate(function(s) { |
|
console.log(s === this); |
|
|
|
console.log(s instanceof Date); |
|
console.log(s instanceof date); |
|
console.log(s instanceof subdate); |
|
|
|
console.log(s.__parent__ instanceof date); |
|
console.log(s.__parent__ instanceof Date); |
|
|
|
console.log(s.__parent__.__parent__ instanceof Date); |
|
|
|
// HOWEVER, to use real Date methods, we have to call them on Date instances: |
|
console.log('time: ' + s.__parent__.__parent__.getTime()); // works |
|
|
|
var msg = 'calling s.getTime() should fail: '; |
|
|
|
try { |
|
s.getTime(); |
|
} catch (e) { |
|
msg += e.message; |
|
} finally { |
|
console.error(msg); |
|
// calling s.getTime() should fail: Date.prototype.getTime called on incompatible Object |
|
} |
|
|
|
// 18 March 2022 |
|
// could make local methods that walk the parent prototypes |
|
s.getDate = function() { return s.__parent__.__parent__.getTime() } |
|
}); |