Created
January 5, 2011 03:36
-
-
Save bga/765893 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
my OOP in js is more and more closer to java/c++/c# :P | |
1) now it allows create instance w/o <new> keyword (all native classes supports instance creating w/o <new> except <Date>) | |
2) removes <prototype> extra word when you access to static fields and methods(<Class.prototype = Class> magic) | |
3) automatic delegates/<Function#bind>. My delegates always is begined from '_on' prefix | |
4) has own implementation of <instanceof> operator - <Object#_hasBaseClass> | |
5) base constructors call via special <Object#_callBaseConstructor> | |
6) derivation do not uses prototype machanism(because slow resolving). I use just copy _methods/InternalTypes/CONSTS. Yes its based on codestyle. But you can use <typeof> or <Object#toString> instead. | |
*/ | |
(function($G) | |
{ | |
var _overwrite = function(dest, src) | |
{ | |
for(var i in src) | |
{ | |
if(src.hasOwnProperty(i)) | |
dest[i] = src[i]; | |
} | |
}; | |
var _extendClass = function(dest, src) | |
{ | |
for(var i in src) | |
{ | |
if(src.hasOwnProperty(i) && !/^[a-z]/.test(i) && !dest.hasOwnProperty(i)) | |
dest[i] = src[i]; | |
} | |
}; | |
Object._create = Object.create || | |
('__proto__' in {}) && function(pr){ return {__proto__: pr} } || | |
function(pr) | |
{ | |
var _fn = function(pr){}; | |
_fn.prototype = pr; | |
return new _fn(); | |
} | |
; | |
$G._defClass = function(def) | |
{ | |
var Class = | |
def._construct == null && function() | |
{ | |
//if(!this._hasBaseClass(Class)) | |
if(this.constructor !== Class) | |
{ | |
var obj = Object._create(Class); | |
_bindDelegates(obj); | |
return obj; | |
} | |
} || | |
function() | |
{ | |
//if(!this._hasBaseClass(Class)) | |
if(this.constructor !== Class) | |
{ | |
var obj = Object._create(Class); | |
_bindDelegates(obj); | |
Class._construct.apply(obj, arguments); | |
return obj; | |
} | |
else | |
{ | |
_bindDelegates(this); | |
Class._construct.apply(this, arguments); | |
} | |
} | |
; | |
_overwrite(Class, def); | |
Class.prototype = Class; | |
Class.prototype.constructor = Class; | |
var delagateNames = []; | |
for(var i in Class) | |
{ | |
if(Class.hasOwnProperty(i) && i.slice(0, 3) == '_on') | |
delagateNames.push(i); | |
} | |
var _bindDelegates = Class.__jbBindDelegates = | |
delagateNames.length > 0 && function(obj) | |
{ | |
var i = delagateNames.length; | |
while(i--) | |
obj[delagateNames[i]] = obj[delagateNames[i]].bind(obj); | |
} || | |
function(obj){} | |
; | |
if(def.extend && def.extend !== Object) | |
_extendClass(Class, def.extend.prototype); | |
return Class; | |
}; | |
Object.prototype._hasBaseClass = function(Class) | |
{ | |
var v = this.constructor; | |
while(v != null) | |
{ | |
if(v === Class) | |
return true; | |
v = v.extend; | |
} | |
return false; | |
}; | |
Object.prototype._callBaseConstructor = function(Class, args) | |
{ | |
Class.__jbBindDelegates(this); | |
(Class._construct || function(){}).apply(this, args || []); | |
}; | |
})(this); | |
// example | |
(function($G) | |
{ | |
var Class = _defClass({ | |
_construct: function(a) | |
{ | |
this.a = a; | |
}, | |
_foo: function() | |
{ | |
Class.n += this.a // access to static fields via <Class.%fieldName%> instead <that.%fieldName%> | |
}, | |
_onA: function(a) // delegate | |
{ | |
this.a = a; | |
}, | |
n: '' // static field | |
}); | |
var c = Class('a'); // no <new> ! but you can use <new> prefix if you want | |
c._foo(); | |
(c._onA)('A'); // no explicit .bind anywhere | |
Class._foo.call(c); // not <Class.prototype._foo.call(c)>! | |
console.log(Class.n); // not <Class.prototype.n>! Like in C++! | |
var Derived = _defClass({ | |
extend: Class, | |
_construct: function(a, b) | |
{ | |
this.b = b; | |
this._callBaseConstructor(Class, [a]); | |
//Class.call(this, a); | |
}, | |
_foo: function() | |
{ | |
Class.n += '<' + this.a + '>'; | |
}, | |
_bar: function() | |
{ | |
Class.n += this.b; | |
}, | |
_onB: function(b) // delegate | |
{ | |
this.b = b; | |
} | |
}); | |
console.log(String(Derived.n)); // check that <Class#n> is not copied | |
var d = Derived('a', 'b'); | |
d._foo(); | |
d._bar(); | |
(d._onB)('B'); // no explicit .bind anywhere | |
(d._onA)('A'); | |
Derived._foo.call(d); // static_cast<Derived*>(&d)->_foo(); ie cast and call | |
Derived._bar.call(d); | |
console.log(Class.n); | |
console.log(d._hasBaseClass(Class)); | |
console.log(d._hasBaseClass(Derived)); | |
console.log(d._hasBaseClass(RegExp)); | |
})(this); |
I usually use calling a constructor without the new operator as casting.
E.g.
myThingInstance = new MyThing(arg1, arg2)
myCastedThing = MyThing(existingObject, arg1, arg2)
That makes way more sense to me personally. It falls in line with how JavaScript itself actually works too.
castedAsString = String(123)
casterAsNumber = Number(true)
@subtleGradient MyClass._someMethod()
calls static method.
Array.map
is js only sugar.
Now im making better version.
I think it's very valuable to follow the existing conventions defined in the JavaScript spec itself. Cf. https://developer.mozilla.org/en/new_in_javascript_1.6#Array_and_String_generics
This convention of having generic versions of all prototype methods has been in use in MooTools for years.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
No generics?
E.g.
What happens if you do
without using call?