在使用 John Resig 的 JavaScript 继承时缓存它



我在几个项目中使用了John Resig的javascript继承模式(格式更好的版本)。

使用这种模式,我可以像this.otherMethod()一样从内部调用另一个类方法。

然而,我的类方法通常包括具有自己作用域的代码,其中定义了自己的this,例如jQuery方法,如ajax成功处理程序或$.each

像这样的东西将是典型的:

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关键字以外的类的更好方法?

正如您提到的,您对修改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),它将使用预填充的参数(通过Class.extend方法分配给参数列表)调用method

这仍然允许您使用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的博客,在JavaScript忍者的秘密中进行了深入讨论)将类似于以下内容:

/* 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_superb/ : /.*/;
// 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参数作为第一个参数。

您可以在代码片段中看到完整的工作示例,或者在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_superb/ : /.*/;
// 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);

最新更新