$merge, $extend, Class.extend, Class.implement, Native.implement
So I was asked yesterday the following:
Why does Browser use merge but Element use implement?
And after composing a lengthy reply, I thought it might be useful to post it for others:
Browser is a Hash, which doesn’t have an implement method. The .merge method here is referring to its own method which is similar to $merge, which takes two (or more) objects and recursively combines them, so:
fruits: {apple: ‘red’}
}, {
fruits: {lemon: ‘yellow’}
})
//returns {fruits: {apple: ‘red’, lemon: ‘yellow’}

$merge combines the properties of the child object. $extend on the other hand, overwrites:
fruits: {apple: ‘red’}
}, {
fruits: {lemon: ‘yellow’}
})
//returns {fruits: {lemon: ‘yellow’}

However, Hash.merge doesn't do the recursion, so it omits any collisions:
apples: {
red: 'delicious',
yellow: 'golden'
}).merge({
apples: {
green: 'sour'
},
grapes: {
purple: 'conchord'
}
});
//returns a hash with apples.red, apples.yellow, and grapes.purple, but NOT apples.green

Now, these are different things than Class.implement and Class.extend and they have nothing to do with $merge and $extend, but Class.implement and Native.implement do the same thing: they modify the object prototype. This changes all instances of the object, while .extend changes just the object you execute it on.
Class.implement will overwrite (kind of like $extend) any properties of the prototype of the class:
colors: {
apple: ‘red’
}
});
Fruits.implement({
colors: {
lemon: ‘yellow’
}
});
new Fruits().colors == {lemon: ‘yellow’}

While Class.extend merges (crazy, I know):
colors: {
lemon: ‘yellow’
}
});
new Fruits().colors == {apple: ‘red’, lemon: ‘yellow’}

Why is it Class.extend and not Class.merge? Because the idea is that you write one class and then extend it to create a child class that inherits from it. Class Bird inherits form Class Animal which inherits from Class Organism, etc. So you express this hierarchy by extending a parent to create a new child. You aren't merging something with the parent, you are extending the parent with additional functionality. It just so happens to be a trick of Mootools that you can extend a parent onto itself as in the next example.
99% of the time you should extend classes. Note that method collisions can be referenced as “this.parent”:
say: function(msg){alert(msg)}
});
Talker = new Class({
Extends: Talker,
say: function (name, msg){
this.parent(name + “ said: “ + msg);
}
});

When you use .implement, this parent reference is destroyed. So why use .implement? Two reasons:
- It affects all instances of a class that might already be running (this use case is rare as typically such modifying code is run before you create any instances of the class)
- You can blend functionality in from other classes
The second case here is the real use for .implement for classes. Consider Options or Events. You Implement:[Options, Events] into a class and they just magically get the functionality. You could do Foo.extend(optionsFunctionality), but .extend takes an object, not a function. This can still be useful (see StickyWin.Ajax in the CNET codebase), but it’s not the best solution 99% of the time.
Ok, so, now Native.implement, which is what you asked about (took me a while to get to this, yeah?). Native objects are already existent in memory by the time Mootools arrives. DOM elements, strings, numbers, arrays, functions. So if we want to add functionality to native objects we have to alter their prototype so that they all get the functionality. This, technically, is what .implement does for both Natives and Classes. It alters the prototype. This is why Class.implement can’t reference this.parent methods because it’s changing the parent.
So Browser, a Hash, uses merge because it’s using $merge to recursively combine properties, while Element, a Native, uses .implement so that all instances of elements get the properties.
Leave a Reply