Skip to content

Instantly share code, notes, and snippets.

@chrisdickinson
Created August 28, 2010 07:40
Show Gist options
  • Save chrisdickinson/554865 to your computer and use it in GitHub Desktop.
Save chrisdickinson/554865 to your computer and use it in GitHub Desktop.
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