我有这个Javascript构造函数-
function TestEngine() {
this.id='Foo';
}
TestEngine.prototype.fooBar = function() {
this.id='bar';
return true;
}
TestEngine.prototype.start = function() {
this.fooBar();
}
TestEngine.prototype.startMethod = function() {
inter = setInterval(this.start, 200);
}
var test = new TestEngine();
test.startMethod();
给了我这个错误——
Uncaught TypeError: Object [object global] has no method 'fooBar'
我尝试了console.log
,发现当我从setInterval
内部调用this.start
时,this
指向window
对象。为什么会这样?
this
指针可以指向许多事物之一,具体取决于上下文:
- 在构造函数(函数调用前面有
new
(中,this
指向新创建的构造函数实例。 - 当函数作为对象的方法调用时(例如
obj.funct()
(,则函数内的this
指针指向该对象。 - 您可以使用
call
、apply
或bind
显式设置this
指向的内容。 - 如果以上都不是,则默认情况下
this
指针指向全局对象。在浏览器中,这是window
对象。
在您的情况下,您正在setInterval
内部呼叫this.start
。现在考虑这个setInterval
的虚拟实现:
function setInterval(funct, delay) {
// native code
}
重要的是要了解start
不被称为this.start
。它被称为funct
。就像做这样的事情:
var funct = this.start;
funct();
现在这两个函数通常执行相同的操作,但有一个小问题 - 在第二种情况下,this
指针指向全局对象,而在第一种情况下指向当前this
。
要做出的一个重要区别是,我们谈论的是 start
内部的this
指针。考虑:
this.start(); // this inside start points to this
var funct = this.start;
funct(); // this inside funct (start) point to window
这不是错误。这就是 JavaScript 的工作方式。当你调用一个函数作为一个对象的方法时(参见我上面的第二点(,函数内的this
指针指向该对象。
在第二种情况下,由于funct
不是作为对象的方法调用的,因此默认情况下应用第四条规则。因此,this
指向window
.
可以通过将start
绑定到当前this
指针,然后将其传递给setInterval
来解决此问题,如下所示:
setInterval(this.start.bind(this), 200);
就是这样。希望这个解释能帮助你更多地了解JavaScript的伟大之处。
以下是使用 javascript 执行 OOP 的巧妙方法:
//Global Namespace:
var MyNamespace = MyNamespace || {};
//Classes:
MyNamespace.MyObject = function () {
this.PublicVar = 'public'; //Public variable
var _privatVar = 'private'; //Private variable
//Public methods:
this.PublicMethod = function () {
}
//Private methods:
function PrivateMethod() {
}
}
//USAGE EXAMPLE:
var myObj = new MyNamespace.MyObject();
myObj.PublicMethod();
通过这种方式,您可以将方法和变量封装到命名空间/类中,使其更易于使用和维护。
因此,您可以像这样编写代码:
var MyNamespace = MyNamespace || {};
//Class: TestEngine
MyNamespace.TestEngine = function () {
this.ID = null;
var _inter = null;
//Public methods:
this.StartMethod = function (id) {
this.ID = id;
_inter = setInterval(Start, 1000);
}
//Private methods:
function Start() {
FooBar();
console.log(this.ID);
}
function FooBar() {
this.ID = 'bar';
return true;
}
}
//USAGE EXAMPLE:
var testEngine = new MyNamespace.TestEngine();
testEngine.StartMethod('Foo');
console.log(testEngine.ID);
最初,ID 设置为"Foo"1 秒后,ID 设置为"bar"
请注意,所有变量和方法都封装在 TestEngine 类中。
试试这个:
function TestEngine() {
this.id='Foo';
}
TestEngine.prototype.fooBar = function() {
this.id='bar';
return true;
}
TestEngine.prototype.start = function() {
this.fooBar();
}
TestEngine.prototype.startMethod = function() {
var self = this;
var inter = setInterval(function() {
self.start();
}, 200);
}
var test = new TestEngine();
test.startMethod();
setInterval
调用start
具有窗口上下文的函数。这意味着当start
被执行时,start
函数内部this
指向window
对象。窗口对象没有任何称为fooBar
的方法,你得到错误。
匿名函数方法:
最好将anonymous function
传递给setInterval
并从中调用函数。如果您的函数使用 this
,这将很有用。
我所做的是,创建一个临时变量self
并在它指向您的 TestEngine 实例并使用它调用self.start()
函数时为其分配this
。
现在start
函数中,this
将指向您的testInstance,一切都将按预期工作。
绑定方法:
Bind
将使您的生活更轻松,并提高代码的可读性。
TestEngine.prototype.startMethod = function() {
setInterval(this.start.bind(this), 200);
}