“如何创建JavaScript”;“类”;将方法添加到原型并使用';这';正确地
我一直被教导在JavaScript中模拟类的正确方法是在将成为类的函数之外向原型添加方法,如下所示:“如何创建JavaScript”;“类”;将方法添加到原型并使用';这';正确地,javascript,methods,prototype,this,Javascript,Methods,Prototype,This,我一直被教导在JavaScript中模拟类的正确方法是在将成为类的函数之外向原型添加方法,如下所示: function myClass() { this.myProp = "foo"; } myClass.prototype.myMethod = function() { console.log(this); } myObj = new myClass(); myObj.myMethod(); 我的方法中的this解决了全局窗口对象的问题 我试过做var=thistrick
function myClass()
{
this.myProp = "foo";
}
myClass.prototype.myMethod = function()
{
console.log(this);
}
myObj = new myClass();
myObj.myMethod();
我的方法中的this
解决了全局窗口对象的问题
我试过做var=this
trick Koch提到,但由于我的方法在我的类之外,所以我的变量不再在范围内。也许我只是没有完全理解它
有没有一种方法可以在JavaScript中创建一个类,在这个类中,每个实现都不会重新创建方法,并且这个始终指向对象
编辑:
上面的简化代码可以工作,但我已经多次声明了与上面完全相同的“类”,当我调用myObj.myMethod()
时,它会作为窗口
对象返回。我已经阅读了我能找到的关于这个的所有解释,比如我链接到的解释,但仍然不理解为什么有时会发生这个问题。有没有想过代码可以像上面那样编写,而这个
是指窗口
以下是我目前遇到的问题,但当我将其简化为上面的几行时,我不再有问题:
HTML文件:
<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>
main.js
var dataObj = new DataUnifier();
$(document).ready(function ev_document_ready() {
dataObj.startAutoUpdating(5);
}
我删掉了一些本不重要的代码,但也许确实重要。当页面加载并调用dataObj.startAutoUpdating(5)时,它在this.stopAutoUpdate()处中断;行,因为此
引用了窗口
对象。就我所见(根据提供的链接),这个
应该引用DataUnifier对象。我已经阅读了很多关于这个
关键字的资料,不明白为什么我一直遇到这个问题。我不使用内联事件注册。是否有任何原因导致这种格式的代码出现此问题
编辑2:对于那些有类似问题的人,请参阅本Mozilla文档页面下半页的“该问题:我使用下面的样式:
function MyClass(){
var privateFunction = function(){
}
this.publicFunction = function(){
privateFunction();
}
}
对我来说,这比使用原型方法直观得多,但结合apply()
方法也会得到类似的结果
另外,你还有另一个好的模式
但是,如果您想更改对函数的this
关联的引用,请使用,您可以查看将全局this
更改为对象的方法。没有“正确”的方法来模拟类。您可以使用不同的模式。
你可以坚持使用你现在正在使用的那个。您发布的代码工作正常。或者你可以换成另一种模式
例如,道格拉斯·克罗克福德(Douglas Crockford)主张这样做
function myClass() {
var that = {},
myMethod = function() {
console.log(that);
};
that.myMethod = myMethod;
return that;
}
在youtube上观看他的演讲。
答案
问题不在于this.stopAutoUpdate()代码>,它具有:
setInterval(this.getUpdates, interval * 1000);
当您将这样的函数传递给setInterval
时,它将从事件循环中调用,而您不知道这里的this
。请注意,此
与函数的定义方式无关,而与函数的调用方式有关。您可以通过传入匿名函数绕过它:
var self = this;
setInterval(function(){ self.getUpdates(); }, interval * 1000);
在任何现代发动机中,您都可以使用更好的:
如果您首先选择绑定,也可以在旧引擎中使用bind
了解问题
我建议您阅读和,以便更好地理解
请注意,当您正常调用函数时,不使用bind、call或apply,那么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'
我最喜欢的定义类的方法如下:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
var MyClass = defclass({
constructor: function () {
this.myProp = "foo";
},
myMethod: function () {
console.log(this.myProp);
}
});
使用defclass
功能,您可以定义MyClass
,如下所示:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
var MyClass = defclass({
constructor: function () {
this.myProp = "foo";
},
myMethod: function () {
console.log(this.myProp);
}
});
顺便说一句,你的实际问题不在于课程。这是您调用此的方式。从setTimeout
获取更新:
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;
}
}
});
简洁,不是吗?如果您需要继承,请查看
编辑:如注释中所述,将附加参数传递到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的InternetExplorer版本上有条件地执行,因此它是完全不引人注目的。只需在所有其他脚本之前包含它,您的代码就可以在每个浏览器上运行。Scope与此
无关;所以Function.prototype.apply()
与作用域无关,它与被调用函数中的this
值有关。这是初学者常见的误解。您(现在已更正)的风格有一个缺点,即每次调用构造函数时,您都在创建(此处:两个)新的函数
实例。现在,要创建一百个myClass
实例,您需要创建三百个对象并为它们保留堆内存。如果您真的需要实例私有部分,这种模式是有意义的;在所有其他情况下,这都是过激的,并构成了巨大的开销。顺便说一句,按照惯例,构造函数的标识符以大写开头。我不这样认为