-
Notifications
You must be signed in to change notification settings - Fork 12
/
02-class.txt
288 lines (227 loc) · 10 KB
/
02-class.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
====== Classes ======
Here is [[http://docs.mootools.net/Class/Class|the documentation page for Class.js]].
MooTools contains a robust Class creation and inheritance system. Creating a new class is actually pretty easy.
===== Classes vs Functions =====
Classes let you:
* reuse and modify code without breaking things that depend on it
* create a stateful object that can store information in its own scope
* create more robust applications by developing small, reusable and extendable classes.
Stand alone functions let you:
* create collections of executions that should happen together
* create shortcuts for other functions that have a lengthy syntax
* actually execute something
Functions are used sparingly with MooTools because Classes are so much more powerful. If you start off with a //Class// and try and keep things generic, and think of what you write as being reusable, you'll find that later you'll extend something you've already written and save yourself a lot of time and create a more robust solution for your problems. Others can make use of this as well.
MooTools itself is a great example of how using classes lets you create complex applications with less code. At the foundation of MooTools is the //Class// object. Almost every other piece of javascript in the library extends this object to create more complex functionality.
==== Functions in Classes ====
Classes can contain functions within them in the same way that they can contain variables (like "this.options"). Example:
<code javascript>
var Animal = new Class({
initialize: function(options){
this.options = options; //save our options.
//if the options specify that the animal
//is asleep when we create it, call sleep()
if(this.options.sleepOnStart) this.sleep();
//else it's awake
else this.awake();
},
sleep: function() {
this.isAsleep = true;
},
awake: function(){
this.isAsleep = false;
}
});
</code>
Now we can instantiate our animal and have it go to sleep or wake up when we want:
<code javascript>
var Cat = new Animal({
color: "black",
sleepOnStart: true
});
//kitty is asleep, let's wake her up
Cat.awake();
alert(Cat.isAsleep);
//-> alerts "false"
</code>
==== Extending Classes ====
Classes can be extended to create more complex functionality. In previous examples we've created an object (//Animal//) and an instance of that object (//Cat//), but by extending the base class we can create more complex functionality while reusing our code. Example:
<code javascript>
var Animal = new Class({
initialize: function(options){
this.options = options;
this.isAlive = true;
}
});
var Mammal = new Class({
Extends: Animal,
initialize: function(options){
//this executes the initialize() function in the Animal Class
//let's also pass the options to the parent's initialize function
this.parent(options);
this.isWarmBlooded = true;
this.hasFur = true;
this.producesMilk = true;
},
sleep: function() {
this.isAsleep = true;
},
awake: function(){
this.isAsleep = false;
}
});
var Mouse = new Class({
Extends: Mammal
});
var Cat = new Class({
Extends: Mammal,
initialize: function(options) {
//call initialize of Mammal
//pass along any options for
//the Mammal and Animal classes
this.parent(options)
this.hasClaws = true;
this.hasTail = true;
this.isCarnivorous = true;
},
catchMouse: function(mouse){
mouse.isAlive = false;
return mouse;
}
});</code>
Ok, so now we have an //Animal// class, a //Mammal// class (that extends //Animal//), and //Cat// and //Mouse// classes (that extend //Mammal//). The code above creates the classes but doesn't actually execute anything. So let's put this code to use:
<code javascript>var Kitty = new Cat({
color: "black"
});
var Mickey = new Mouse({
color: "black",
pants: true,
shoes: true
});
Kitty.catchMouse(Mickey);
alert(Mickey.isAlive);
//->false
</code>
By breaking things up into classes this way, at any point in the future we can implement more types of //Animals//, //Mammals//, or even different types of //Cats// and //Mice//. It also means that if we find a bug in, say, the //Mammal// class, fixing it in that one place means we don't have to fix it in all the instances of //Mammal//.
Note that in using //Extends// you extend the parent class into the child. It is possible to do this to the class itself. For example:
<code javascript>
//from the example above...
var Cat = new Class({
Extends: Mammal,
initialize: function(options) {
//call initialize of Mammal
//pass along any options for
//the Mammal and Animal classes
this.parent(options)
this.hasClaws = true;
this.hasTail = true;
this.isCarnivorous = true;
},
catchMouse: function(mouse){
mouse.isAlive = false;
return mouse;
}
});
//... then later
Cat = new Class({
Extends: Cat,
initialize: function(options){
this.parent(options);
this.energy = 0;
},
catchMouse: function(mouse){
this.parent(mouse);
this.energy++;
}
});
</code>
Here we had a class for //Cat//, but later we wanted to add a bit of functionality to it. Rather than duplicating our code, and rather than creating another namespace ("BetterCat" or something), we just implement new code into the class (using //.extend//) and call //this.parent// to execute the functionality already in the class.
==== Implementing Classes ====
Extending classes allows you to take a base class (like //Animal// above) and create a new class with that one as a template (as in our //Mammal// class above). This doesn't alter the //Animal// class in any way.
This isn't always what we're after though. Sometimes you want to alter the parent class in a given context. Let's say, for example, that you're using the //Animal// class above, but you want to add a function to it for your environment. Extending the //Animal// class to create something like //AnimalExtended// doesn't work for you because you're still going to use //Cat// and //Mouse// and //Mammal// and you don't want to rewrite those.
Here's where //.implement// comes in handy. This allows you to add functionality into a class, altering the class itself. You can't use the "this.parent()" function like you can with extend because you aren't creating a child - you're modifying the class. Additionally, if you use the same namespace as one that's already there, you'll overwrite it. Example:
<code javascript>//this is the same Animal class as above
var Animal = new Class({
initialize: function(options){
this.options = options;
this.isAlive = true;
}
});
//let's change it to include some additional functionality:
Animal.implement({
eat: function() {
if(typeof this.energy == "undefined") this.energy = 0;
this.energy++;
}
});</code>
Now //Animal// has a function called //eat()// that increments its energy value.
Let's say that later we want to add the ability to pass in a starting value in the options and we want to implement that in to the class as well. Using //.implement//, if we want to alter the //.eat// function, we'll have to write it all over again:
<code javascript>//let's let you pass in a starting value
Animal.implement({
eat: function() {
if(typeof this.energy == "undefined") {
if(typeof this.options.startingEnergy != "undefined") this.energy = this.options.startingEnergy;
else this.energy = 0;
this.energy++;
}
});</code>
Implement is used in MooTools mostly to extend classes that are in the "Native" group - specifically the String, Array, Function, and Element prototype. This means that if you have this code:
<code javascript>String.implement({
alert: function() {
alert(this);
}
});</code>
Then any string would inherit that function:
<code javascript>"hey howdy!".alert();</code>
In this way, MooTools gives you a lot of shortcuts for things you often do to things like Strings, Arrays, etc.
There's one other way to use implement that's pretty handy. You can create a class and then implement it into another, like so:
<code javascript>//this is the same Mammal and Cat class as above
var Carnivore = new Class({
isCarnivore : true,
energy : 0,
eat: function() {
this.energy++;
}
});
var Cat = new Class({
Extends: Mammal,
Implements: Carnivore,
initialize: function() {
this.parent() //call initialize of Mammal
this.hasClaws = true;
this.hasTail = true;
this.isCarnivorous = true;
},
function: catchMouse(mouse){
mouse.isAlive = false;
return mouse;
}
});
</code>
Now all the //Cats// that you create will have the functionality defined in //Carnivore//. This allows you to create
more bite-sized (to play the pun) code that can be added and implemented where it's needed.
==== Implement vs. Extend ====
The main difference between //Extends// and //Implements// is that //Implement// changes the class's prototype, while //Extend// creates a copy. This means that if you implement a change into a class all instances of that class will inherit that change instantly, while if you use //Extend// then all existing instances will remain the same.
This is why we use //Implement// when we implement classes into each other. Here's a simple illustration:
<code javascript exec>var Thingy = new Class({
go: function(){
alert('hi');
}
});
var myClass = new Thingy();
myClass.go(); /* alerts 'hi' */
Thingy.implement({
go: function(){
alert('implemented');
}
});
myClass.go(); /* alerts 'implemented' */
Thingy = Thingy.extend({
go: function(){
alert('extended');
}
});
myClass.go(); /* alerts 'implemented'
because extend only affects
new instances. */</code>
**NOTE: <del>There's a bug in MooTools 1.2 that prevents this example from working currently, but this is how it's //supposed// to work. Expect a patch soon that will restore this functionality. For now, implement does not retroactively affect instances</del> This bug does not affect Mootools 1.2.3.**
Note that in general you shouldn't use implement for this purpose (to retroactively change all the instances of a class). It's very hard to predict how such a change will affect everything else that's instantiated. It's up to you to figure out when it is best for your purposes to do this sort of thing, but be careful as it can be difficult to predict consequences.