prototype: Clean up the new class API.

This commit is contained in:
Sam Stephenson 2007-10-11 05:02:07 +00:00
parent 8ca43a5c88
commit c35598ca2b
3 changed files with 109 additions and 25 deletions

View File

@ -1,5 +1,10 @@
*SVN*
* Clean up the new class API. [sam, Tobie Langel]
- Add Class#addMethods for adding instance methods to classes.
- Remove Class.extend and Class.mixin.
- Class.create now takes a variable number of arguments: if the first argument is a class, the newly created class inherits from that class; all other arguments are treated as successive calls to addMethods.
* Fix contentloaded event initialization in IE. Closes #9457, #9488, #9707. [Mislav Marohnić]
* Deprecate document.getElementsByClassName and Element#getElementsByClassName since native versions return a NodeList and we can only return an Array. Please use $$ or Element#select instead. [sam]

View File

@ -1,34 +1,40 @@
/* Based on Alex Arnell's inheritance implementation. */
var Class = {
create: function(parent, properties) {
if (arguments.length == 1 && !Object.isFunction(parent))
properties = parent, parent = null;
create: function() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
this.initialize.apply(this, arguments);
}
Object.extend(klass, Class.Methods);
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
var subclass = function() { };
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
if (properties) Class.extend(klass, properties);
for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
return klass;
},
extend: function(destination, source) {
var ancestor = destination.superclass && destination.superclass.prototype;
}
};
Class.Methods = {
addMethods: function(source) {
var ancestor = this.superclass && this.superclass.prototype;
for (var property in source) {
var value = source[property];
@ -41,14 +47,10 @@ var Class = {
toString: function() { return method.toString() }
});
}
destination.prototype[property] = value;
this.prototype[property] = value;
}
return destination;
},
mixin: function(destination, source) {
return Object.extend(destination.prototype, source);
return this;
}
};

View File

@ -73,7 +73,66 @@
// empty subclass
var Mouse = Class.create(Animal, {});
//mixins
var Sellable = {
getValue: function(pricePerKilo) {
return this.weight * pricePerKilo;
},
inspect: function() {
return '#<Sellable: #{weight}kg>'.interpolate(this);
}
};
var Reproduceable = {
reproduce: function(partner) {
if (partner.constructor != this.constructor || partner.sex == this.sex)
return null;
var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female';
return new this.constructor('baby', weight, sex);
}
};
// base class with mixin
var Plant = Class.create(Sellable, {
initialize: function(name, weight) {
this.name = name;
this.weight = weight;
},
inspect: function() {
return '#<Plant: #{name}>'.interpolate(this);
}
});
// subclass with mixin
var Dog = Class.create(Animal, Reproduceable, {
initialize: function($super, name, weight, sex) {
this.weight = weight;
this.sex = sex;
$super(name);
}
});
// subclass with mixins
var Ox = Class.create(Animal, Sellable, Reproduceable, {
initialize: function($super, name, weight, sex) {
this.weight = weight;
this.sex = sex;
$super(name);
},
eat: function(food) {
if (food instanceof Plant)
this.weight += food.weight;
},
inspect: function() {
return '#<Ox: #{name}>'.interpolate(this);
}
});
new Test.Unit.Runner({
testFunctionArgumentNames: function() { with(this) {
@ -419,7 +478,7 @@
testClassCreate: function() { with(this) {
assert(Object.isFunction(Animal), 'Animal is not a constructor');
assertEnumEqual([Cat, Mouse], Animal.subclasses);
assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses);
Animal.subclasses.each(function(subclass) {
assertEqual(Animal, subclass.superclass);
});
@ -472,17 +531,17 @@
assertEqual("Gonzo: hello honk honk", gonzo.say("hello"));
}},
testClassExtend: function() { with(this) {
testClassAddMethods: function() { with(this) {
var tom = new Cat('Tom');
var jerry = new Mouse('Jerry');
Class.extend(Animal, {
Animal.addMethods({
sleep: function() {
return this.say('ZZZ');
}
});
Class.extend(Mouse, {
Mouse.addMethods({
sleep: function($super) {
return $super() + " ... no, can't sleep! Gotta steal cheese!";
},
@ -498,14 +557,32 @@
assertUndefined(tom.escape);
assertUndefined(new Animal().escape);
Class.extend(Animal, {
Animal.addMethods({
sleep: function() {
return this.say('zZzZ');
}
});
assertEqual("Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
}}
}},
testBaseClassWithMixin: function() { with(this) {
var grass = new Plant('grass', 3);
assertRespondsTo('getValue', grass);
assertEqual('#<Plant: grass>', grass.inspect());
}},
testSubclassWithMixin: function() { with(this) {
var snoopy = new Dog('Snoopy', 12, 'male');
assertRespondsTo('reproduce', snoopy);
}},
testSubclassWithMixins: function() { with(this) {
var cow = new Ox('cow', 400, 'female');
assertEqual('#<Ox: cow>', cow.inspect());
assertRespondsTo('reproduce', cow);
assertRespondsTo('getValue', cow);
}}
}, 'testlog');