mindplay
11 Dec 2008, 11:01 AM
Please consider the following for future versions of ExtJS or other frameworks.
In many places throughout Ext, callbacks are invoked in "clever" ways - using Function.call() to override the calling context (this)...
This is a tempting feature in JavaScript, as it often enables you to enforce a calling context that you may think makes more sense than whatever calling context a function would have naturally been called in.
But more than often, this causes all manner of confusion, and in some cases makes it nearly impossible to get to the "real" context of whatever method you're having some component invoke as a callback.
A good example is renderers in the GridPanel.
Renderers are forcibly invoked using the <td> element as the calling context. While this may seem practical, or even natural, it can really get in the way of what you're trying to do.
Let's say I want to use a method of some other object, say of Class X, as a renderer.
And let's say that the calling context is for some reason important to me - that my rendering function needs to know which instance of X it belongs to.
The natural way to write this, would be:
Class_X = Ext.extend(Class_Y, {
constructor: function(options) {
... (assign this.renderer to column config) ...
},
renderer: function(...) {
return '...' + this.getSomething() + '...';
}
});
This will not work, as "this" in the renderer() method will be overridden at call-time.
Because the real calling context, my instance of X, is being overridden, I have effectively no way to determine which object this renderer belongs to.
The only way to work around this, is to manually create my function, as a local variable, inside my constructor, for example:
Class_X = Ext.extend(Class_Y, {
constructor: function(options) {
var me = this;
var renderer = function(...) {
return '...' + me.getSomething() + '...';
};
... (assign renderer to column config) ...
}
});
This is not a pretty workaround, and should not be necessary.
The point I'm trying to make here, is that you could just as well pass that <td> reference as an argument, instead of overriding the real calling context.
The calling context is there for a reason, and is key to a lot of aspects of good object-oriented design. Being able to identify the object to which a method belongs, is crucial.
Context override was not meant as a replacement for regular function arguments, which is how I feel it's sometimes being used in Ext.
Overriding the calling context often means that the user has to guess what the calling context might be. For example, the documentation for ColumnModel.setRenderer() does not mention that the "this" object will be overridden.
What's worse is, unless you go outside the documentation, you would never even know that it's possible for a renderer to get to the <td> element - which would have been clear, if the <td> element had been part of the callback's argument list.
In many places throughout Ext, callbacks are invoked in "clever" ways - using Function.call() to override the calling context (this)...
This is a tempting feature in JavaScript, as it often enables you to enforce a calling context that you may think makes more sense than whatever calling context a function would have naturally been called in.
But more than often, this causes all manner of confusion, and in some cases makes it nearly impossible to get to the "real" context of whatever method you're having some component invoke as a callback.
A good example is renderers in the GridPanel.
Renderers are forcibly invoked using the <td> element as the calling context. While this may seem practical, or even natural, it can really get in the way of what you're trying to do.
Let's say I want to use a method of some other object, say of Class X, as a renderer.
And let's say that the calling context is for some reason important to me - that my rendering function needs to know which instance of X it belongs to.
The natural way to write this, would be:
Class_X = Ext.extend(Class_Y, {
constructor: function(options) {
... (assign this.renderer to column config) ...
},
renderer: function(...) {
return '...' + this.getSomething() + '...';
}
});
This will not work, as "this" in the renderer() method will be overridden at call-time.
Because the real calling context, my instance of X, is being overridden, I have effectively no way to determine which object this renderer belongs to.
The only way to work around this, is to manually create my function, as a local variable, inside my constructor, for example:
Class_X = Ext.extend(Class_Y, {
constructor: function(options) {
var me = this;
var renderer = function(...) {
return '...' + me.getSomething() + '...';
};
... (assign renderer to column config) ...
}
});
This is not a pretty workaround, and should not be necessary.
The point I'm trying to make here, is that you could just as well pass that <td> reference as an argument, instead of overriding the real calling context.
The calling context is there for a reason, and is key to a lot of aspects of good object-oriented design. Being able to identify the object to which a method belongs, is crucial.
Context override was not meant as a replacement for regular function arguments, which is how I feel it's sometimes being used in Ext.
Overriding the calling context often means that the user has to guess what the calling context might be. For example, the documentation for ColumnModel.setRenderer() does not mention that the "this" object will be overridden.
What's worse is, unless you go outside the documentation, you would never even know that it's possible for a renderer to get to the <td> element - which would have been clear, if the <td> element had been part of the callback's argument list.