如何创建一个JavaScript“类”,将方法添加到原型中,并正确使用“this”
How to create a JavaScript "class" that adds methods to prototype AND uses 'this' correctly
我一直被教导在 JavaScript 中模拟类的正确方法是在将成为你的类的函数之外向原型添加方法,如下所示:
function myClass()
{
this.myProp = "foo";
}
myClass.prototype.myMethod = function()
{
console.log(this);
}
myObj = new myClass();
myObj.myMethod();
我一直遇到一个问题,即我的方法中的this
解析为全局 Window 对象,正如在 quirksmode 上最好解释的那样。
我尝试过做 Koch 提到的var that = this;
技巧,但由于我的方法在我的类之外,我的 that
变量不再在范围内。 也许我只是不完全理解它。
有没有办法在 JavaScript 中创建一个类,其中方法不会在每个实现中重新创建,并且this
将始终指向该对象?
编辑:
上面的简化代码有效,但我有很多次像上面一样声明一个"类",当我调用myObj.myMethod()
时,它会作为Window
对象返回。 我已经阅读了我能找到的所有this
解释,例如我链接到的解释,但仍然不明白为什么有时会发生此问题。 任何关于代码可以像上面一样编写的情况的想法,this
会引用Window
?
这是我目前遇到问题的实现,但是当我像上面这样将其简化为几行时,我不再有问题:
文件:
<script type="text/javascript" src="./scripts/class.Database.js"></script>
<script type="text/javascript" src="./scripts/class.ServerFunctionWrapper.js"></script>
<script type="text/javascript" src="./scripts/class.DataUnifier.js"></script>
<script type="text/javascript" src="./scripts/main.js"></script>
.class。DataUnifier.js:
function DataUnifier()
{
this._local = new Database();
this._server = new ServerFunctionWrapper();
this.autoUpdateIntervalObj = null;
}
DataUnifier.prototype.getUpdates = function()
{
this._server.getUpdates(updateCommands)
{
console.log(updateCommands);
if (updateCommands)
{
executeUpdates(updateCommands);
}
}
}
//interval is in seconds
DataUnifier.prototype.startAutoUpdating = function(interval)
{
this.stopAutoUpdating();
this.autoUpdateIntervalObj = setInterval(this.getUpdates,interval * 1000);
}
DataUnifier.prototype.stopAutoUpdating = function()
{
if (this.autoUpdateIntervalObj !== null)
{
clearInterval(this.autoUpdateIntervalObj);
this.autoUpdateIntervalObj = null;
}
}
主.js
var dataObj = new DataUnifier();
$(document).ready(function ev_document_ready() {
dataObj.startAutoUpdating(5);
}
我已经删除了一些无关紧要的代码,但也许确实如此。 当页面加载并调用 dataObj.startAutoUpdating(5( 时,它会在 this.stopAutoUpdating((;行,因为this
引用Window
对象。 据我所知(根据提供的链接(,this
应该引用DataUnifier对象。 我已经阅读了许多关于this
关键字的资源,但不明白为什么我一直遇到这个问题。 我不使用内联事件注册。 像这样格式化的代码有什么原因会出现这个问题吗?
编辑 2:对于那些有类似问题的人,请参阅此 Mozilla 文档页面页面中半途的"this
问题":http://developer.mozilla.org/en-US/docs/Web/API/Window.setInterval
我最喜欢的定义类的方法如下:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
使用 defclass
函数,您可以按如下方式定义MyClass
:
var MyClass = defclass({
constructor: function () {
this.myProp = "foo";
},
myMethod: function () {
console.log(this.myProp);
}
});
顺便说一句,您的实际问题不在于类。这是您从setTimeout
调用this.getUpdates
的方式:
this.autoUpdateIntervalObj = setInterval(this.getUpdates, interval * 1000);
相反,它应该是:
this.autoUpdateIntervalObj = setInterval(function (self) {
return self.getUpdates();
}, 1000 * interval, this);
因此,您的DataUnifier
类可以编写为:
var DataUnifier = defclass({
constructor: function () {
this._local = new Database;
this._server = new ServerFunctionWrapper;
this.autoUpdateIntervalObj = null;
},
getUpdates: function () {
this._server.getUpdates(function (updateCommands) {
console.log(updateCommands);
if (updateCommands) executeUpdates(updateCommands);
});
},
startAutoUpdating: function (interval) {
this.stopAutoUpdating();
this.autoUpdateIntervalObj = setInterval(function (self) {
return self.getUpdates();
}, 1000 * interval, this);
},
stopAutoUpdating: function () {
if (this.autoUpdateIntervalObj !== null) {
clearInterval(this.autoUpdateIntervalObj);
this.autoUpdateIntervalObj = null;
}
}
});
简洁不是吗?如果您需要继承,请查看 augment
.
编辑:正如注释中指出的那样,将其他参数传递给setTimeout
或setInterval
在低于9的Internet Explorer版本中不起作用。以下填充程序可用于解决此问题:
<!--[if lt IE 9]>
<script>
(function (f) {
window.setTimeout = f(window.setTimeout);
window.setInterval = f(window.setInterval);
})(function (f) {
return function (c, t) {
var a = [].slice.call(arguments, 2);
return f(function () {
c.apply(this, a);
}, t);
};
});
</script>
<![endif]-->
由于代码仅在低于 9 的 Internet Explorer 版本上有条件地执行,因此完全不引人注目。只需在所有其他脚本之前包含它,您的代码就可以在每个浏览器上运行。
答案
问题不在于this.stopAutoUpdating();
,而在于:
setInterval(this.getUpdates, interval * 1000);
当你将这样的函数传递给setInterval
时,它将从事件循环中调用,不知道你在这里this
。请注意,this
与函数的定义方式无关,而与函数的调用方式有关。您可以通过传入匿名函数来绕过它:
var self = this;
setInterval(function(){ self.getUpdates(); }, interval * 1000);
在任何现代引擎中,您都可以使用更好的绑定:
setInterval(this.getUpdates.bind(this), interval * 1000);
如果您先填充bind
,也可以在旧引擎中使用它。
了解问题
我建议您阅读有关电话并申请的信息,以便更好地理解。
请注意,当您正常调用函数而不使用 bind、调用或 apply 时,this
将仅设置为从中调用函数的任何对象上下文(即,.
之前的任何内容(。
希望这能帮助你理解我所说的this
不是关于函数是如何定义的,而是关于它是如何被调用的。下面是一个示例,您可能不希望this
工作,但它确实有效:
// This function is not defined as part of any object
function some_func() {
return this.foo;
}
some_func(); // undefined (window.foo or an error in strict mode)
var obj = {foo: 'bar'};
// But when called from `obj`'s context `this` will refer to obj:
some_func.call(obj); // 'bar'
obj.getX = some_func;
obj.getX(); // 'bar'
您可能希望它工作但事实并非如此的示例,以及使其再次工作的几个解决方案:
function FooBar() {
this.foo = 'bar';
}
FooBar.prototype.getFoo = function() {
return this.foo;
}
var fb = new FooBar;
fb.getFoo(); // 'bar'
var getFoo = fb.getFoo;
// getFoo is the correct function, but we're calling it without context
// this is what happened when you passed this.getUpdates to setInterval
getFoo(); // undefined (window.foo or an error in strict mode)
getFoo.call(fb); // bar'
getFoo = getFoo.bind(fb);
getFoo(); // 'bar'
没有"正确"的方法来模拟类。您可以使用不同的模式。您可以坚持使用现在使用的那个。您发布的代码工作正常。或者您可以切换到另一种模式。
例如,道格拉斯·克罗克福德(Douglas Crockford(主张做这样的事情
function myClass() {
var that = {},
myMethod = function() {
console.log(that);
};
that.myMethod = myMethod;
return that;
}
在YouTube上观看他的演讲。http://youtu.be/6eOhyEKUJko?t=48m25s
我使用下面的样式:
function MyClass(){
var privateFunction = function(){
}
this.publicFunction = function(){
privateFunction();
}
}
对我来说,这比使用原型方式要直观得多,但结合apply()
方法,你会得到类似的结果。
此外,您还有另一种很好的模式,文字对象表示法,揭示模块模式
但是,如果要更改对函数关联this
引用,请使用 Function.prototype.apply((,您可以在此示例中看到一种更改对象的全局this
的方法。
- 混合jQuery,原型和“this”
- Javascript,从原型函数自己的字段中获取“this”
- “this”在不是类原型的函数中
- 在原型中声明 TypeScript 成员,而不是添加到“this”中
- 从原型中的事件访问“this”
- 通过引用调用原型函数时,类会丢失“this”作用域
- 原型方法中的“this”在继承时未按预期解析
- Javascript原型和访问“this”的父对象
- 使用从 async.series 调用的原型函数中的“this”
- 我可以用javascript提供我/the/self=this的原型定义吗
- 传递一个原型's函数作为参数而不丢失'this'上下文
- 什么是原型,$.扩展,并返回this"不得不这么做
- this.collection.reset引发错误-未捕获类型错误:无法读取属性'原型'的未定义
- JavaScript数组.原型如何设置'this'到由方法获得的新数组
- 引用“this"在对象原型方法中的setInterval/setTimeout
- 如何访问我的实例'this'从揭示原型
- Javascript传递"this"从原型到另一个函数
- 什么是'this'当它在原型中时引用
- 原型“this"不工作(JS)
- Javascript:获取父元素's原型' this '