我在几个项目中使用John Resig's javascript inheritance pattern(better formatted version)。
使用这种模式,我可以从另一个类this.otherMethod()
中调用一个类方法。
但是,我的类方法通常包含具有自己的作用域的代码,并且已经定义了自己的this
,例如ajax成功处理程序或$.each
等jQuery方法。
这样的事情很典型:
var AppWidget = Class.extend({
/**
* Initialize and set any defaults
*/
init: function(options) {
var _this = this;
var defaults = {
};
_this.options = $.extend(true, defaults, options);
return _this;
},
/**
* Do something involving ajax
*/
someAjaxyFunction:function(){
var _this = this;
$.ajax({
type: "POST",
url: "page.php",
data: dataString,
success: function(data) {
_this.someFollowupFunction(data)
}
});
},
/**
* Do some followup work
*/
someFollowupFunction:function(data){
var _this = this;
$.each(data,function () {
_this.someOtherFunction(this);
});
},
/**
* Do some other work
*/
someOtherFunction:function(thing){
var _this = this;
//...
}
});
它会被称为:
var widget = new AppWidget();
widget.someAjaxyFunction();
但是,我想知道是否有更好的方法来处理在方法中使用的缓存this
。将var _this = this;
添加到每个类方法的顶部都可以,但是非常繁琐,而且在编写新方法时经常会忘记它。
是否有一些聪明的方法可以使_this
在所有类方法中自动可用,或者更方便的方法是引用除this
关键字以外的类?
答案 0 :(得分:1)
正如您所提到的那样,您可以从John修改Class.extend
实现,理论上可以将_this
变量作为函数参数注入。
这当然在很大程度上取决于您希望对当前运行的代码进行多少更改。
在我的示例中,我将_this
参数添加到函数参数列表中,作为所有函数的第一个参数。这将以下列方式更改您的示例类(完整示例可以在下面的代码段中找到):
var Example = Class.extend({
method: function(_this, timeout) {
// some code
},
callback: function(_this, arg1) {
// some code
}
});
var example = new Example();
example.method(5);
如你所见,_现在这是第一个参数。但是,如何调用类中的方法不会改变,您仍然可以调用example.method(15)
,它将使用预先填充的_this参数调用method
(通过{{1分配给参数列表)方法)
这仍然允许你使用Class.extend
实现,它甚至可以提供正确的回调(nl:之后的派生类,例如这里)
this._super()
John的代码所需的更改(最初来自John's blog,在Secrets of a JavaScript Ninja内进行了更深入的讨论),与此类似:
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);
主要的变化是/* 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
变量,目前,如果你有任何参数,你应该总是添加{{1}参数作为第一个参数。
您可以在代码段或jsfiddle here
中看到完整的工作示例
_this