正如您提到的,您可以修改 John 的 Class.extend 实现,理论上您可以将 _this 变量作为函数参数注入。
这当然很大程度上取决于您希望对当前运行的代码进行多少更改。
在我的示例中,我将_this 参数添加到函数参数列表中,作为所有函数的第一个参数。这将通过以下方式更改您的示例类(完整示例可以在下面的 sn-p 中找到):
var Example = Class.extend({
method: function(_this, timeout) {
// some code
},
callback: function(_this, arg1) {
// some code
}
});
var example = new Example();
example.method(5);
如您所见,_this 现在始终是第一个参数。但是,您调用类中方法的方式不会改变,您仍然可以调用example.method(15),它会使用预填充的_this 参数调用method(通过Class.extend 方法分配给参数列表)
这仍然允许您使用this._super() 实现,它甚至可以提供正确的回调(nl:之后派生类的回调,例如这里)
var Sample = Example.extend({
method: function(_this, timeout) {
this._super(timeout);
console.log('I was called from sample');
},
callback: function(_this) {
console.log('I am supplied from Sample');
}
});
var sample = new Sample();
sample.method(10);
John 的代码所需的更改(最初来自 John's blog,在 Secrets of a JavaScript Ninja 内部进行了更深入的讨论),类似于以下内容:
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
function convertArgumentsToArray( args ) {
return Array.prototype.slice.apply( args );
}
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, [this].concat( convertArgumentsToArray( arguments ) ));
this._super = tmp;
return ret;
};
})(name, prop[name]) :
(function(fn) {
return function() {
var ret = fn.apply(this, [this].concat( convertArgumentsToArray( arguments ) ));
return ret;
}
})(prop[name]);
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
主要变化是this 上下文自动注入到参数列表中。
仍然可以进行的优化是检查参数列表中是否定义了_this 变量,目前,如果您有任何参数,则应始终将_this 参数添加为第一个参数。
您可以在此处的代码 sn-p 或 jsfiddle here 中查看完整的工作示例
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
function convertArgumentsToArray( args ) {
return Array.prototype.slice.apply( args );
}
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, [this].concat( convertArgumentsToArray( arguments ) ));
this._super = tmp;
return ret;
};
})(name, prop[name]) :
(function(fn) {
return function() {
var ret = fn.apply(this, [this].concat( convertArgumentsToArray( arguments ) ));
return ret;
}
})(prop[name]);
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
var Example = Class.extend({
method: function(_this, timeout) {
console.log('timeout defined is ' + timeout);
setTimeout(function() {
_this.callback( 15 );
}, timeout);
},
callback: function(_this, arg1) {
console.log('callback from _this argument, current context: ', this);
console.log(_this === this);
console.log(arg1 === 15);
}
});
var Sample = Example.extend({
method: function(_this, timeout) {
this._super(timeout);
console.log('I was called from sample');
},
callback: function(_this) {
console.log('I am supplied from Sample');
}
});
var example = new Example();
example.method(5);
var sample = new Sample();
sample.method(10);