prototype: Clean up the new class API.
This commit is contained in:
parent
8ca43a5c88
commit
c35598ca2b
@ -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]
|
||||
|
40
src/base.js
40
src/base.js
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user