Created
August 28, 2010 07:40
-
-
Save chrisdickinson/554865 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
var sys = require('sys'); | |
// | |
// Here's how prototypical inheritance works. | |
// | |
var Klass = function(val) { | |
this.value = val; | |
}; | |
Klass.prototype.getValue = function() { | |
return this.value; | |
}; | |
var Subklass = function(val) { | |
this.value = val; | |
}; | |
// create a helper function to sit in between Klass | |
// and Subklass -- why? | |
// ------------------------------------------------ | |
// because we want to make an instance of an object | |
// with the appropriate prototype, and add set our | |
// subclasses prototype to that object. | |
// that way, when JS looks for `getValue` on an | |
// instance of Subklass, it'll look for it like so: | |
// sk.getValue ? NOPE -> | |
// sk.prototype.getValue ? NOPE -> | |
// sk.prototype.prototype.getValue ? YEAHH B) | |
// | |
// ------------------------------------------------ | |
// | |
var Helper = function () { }; | |
Helper.prototype = Klass.prototype; | |
Subklass.prototype = new Helper(); | |
Subklass.prototype.getOtherValue = function() { | |
// this.getValue is actually Klass.prototype.getValue! | |
return this.getValue()*2; | |
}; | |
var sk = new Subklass(Math.random()); | |
sys.puts(sk.getOtherValue()); | |
// | |
// Okay, harder example, probably more useful though. | |
// | |
// Let's add the `subclass` method to all Function objects: | |
// | |
Function.prototype.subclass = function(base) { | |
// create the helper function | |
var F = function() {}, | |
proto; | |
F.prototype = base.prototype; | |
proto = new F(); | |
// this is the function that is subclassing `base` | |
// NOTE: you can't access the following method from | |
// a class instance -- it's not part of that object's | |
// prototype chain. so: | |
// Dog.super === Function | |
// (new Dog()).super === undefined | |
// | |
// the `super` method takes an object instance and a method | |
// name, and returns a function that applies the function | |
// at base.prototype[method] to the previously supplied obj. | |
// | |
// whew. it's called "currying" because it's supposed | |
// to make you sweat while trying to explain it, evidentally :\ | |
this.super = function(obj, method) { | |
return function() { | |
var args = Array.prototype.slice.call(arguments); | |
return base.prototype[method].apply(obj, args); | |
}; | |
}; | |
this.prototype = proto; | |
}; | |
var Dog = function(val) { | |
this.value = val; | |
}; | |
Dog.subclass(Klass); | |
Dog.prototype.getValue = function() { | |
// return the getValue from Dog's immediate parent class and call it | |
return Dog.super(this, 'getValue')() + "HI"; | |
}; | |
var ralph = new Dog(21); | |
sys.puts(ralph.getValue()) | |
var Marmaduke = function(val) { | |
this.value = val; | |
}; | |
Marmaduke.subclass(Dog); | |
Marmaduke.prototype.getValue = function() { | |
// same here, marmaduke. | |
return "MARRRRMADUKE" + Marmaduke.super(this, 'getValue')(); | |
}; | |
var famousDog = new Marmaduke(1); | |
sys.puts(famousDog.getValue()); | |
/* | |
A little mind bending, generally. | |
But it's not that bad! Python is actually remarkably similar, | |
especially after you've had a couple of drinks. | |
Take the following class: | |
class A(object): | |
def __init__(self, value): | |
self.value = value | |
def get_value(self): | |
return self.value | |
It should seem like an old familiar friend by now. Give it a | |
value, get that value back through <obj>.get_value(). Blah, | |
blah, blah. Boring. | |
Let's see what happens when you change an instance of get_value: | |
a = A(1) | |
print(a.get_value()) # 1 | |
a.get_value = lambda : 42 | |
print(a.get_value()) # 42 | |
print(A(100).get_value()) # 100 | |
Changing the an `A` instance's `get_value` doesn't affect | |
any other `A` instance. Let's see if we can get a change | |
to affect things globally. | |
b = A(2) | |
print(b.get_value()) # 2 | |
A.get_value = lambda self : self.value * 2 | |
print(b.get_value()) # 4 | |
print(a.get_value()) # 42 | |
print(A(4).get_value()) # 8 | |
So if you change `A.get_value`, you change it for all instances | |
of `A` who haven't explicitly overridden `get_value` themselves. | |
So take the following Javascript: | |
var A = function(value) { | |
this.value = value; | |
}; | |
A.prototype.getValue = function() { | |
return this.value; | |
}; | |
And let's see how this goes. | |
a = new A(1) | |
sys.puts(a.getValue()) // 1 | |
a.getValue = function() { | |
return 42; | |
}; | |
sys.puts(a.getValue()) // 42 | |
b = new A(100) | |
sys.puts(b.getValue()) // 100 | |
A.prototype.getValue = function() { | |
return this.value * 2; | |
}; | |
b = new A(2); | |
sys.puts(b.getValue()) // 4 | |
sys.puts(a.getValue()) // 42 | |
sys.puts((new A(4)).getValue()) // 8 | |
So Javascript is actually a lot like python in this sense. | |
The other main difference here is that the python code here: | |
v = A(10) | |
v = v.get_value | |
v() # 10 | |
doesn't work the same in JS: | |
v = new A(10); | |
v = v.getValue; | |
v() // undefined! | |
because methods do *not* strongly bind by default in JS. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment