如何创建一个JavaScript"类",将方法添加到原型并正确使用"this"

dal*_*lin 7 javascript methods prototype this

我总是被告知在JavaScript中模拟类的正确方法是在函数外添加方法,这将是你的类,如下所示:

function myClass()
{
    this.myProp = "foo";
}

myClass.prototype.myMethod = function()
{
    console.log(this);
}

myObj = new myClass();
myObj.myMethod();
Run Code Online (Sandbox Code Playgroud)

我一直在遇到this我的方法解析为全局Window对象的问题,最好在quirksmode上解释.

我已经尝试过做var that = this;Koch提到的技巧,但由于我的方法不在我的课堂上,我的that变量不再在范围内了.也许我只是不完全理解它.

有没有办法可以在JavaScript中创建一个类,其中每个实现都不重新创建方法,并且this总是指向对象?

编辑:

上面的简化代码可以工作,但我已经多次在上面声明一个与上面完全相同的"类",当我调用时myObj.myMethod(),它作为一个Window对象返回.我已经阅读了this我能找到的每一个解释,例如我链接到的那个,但仍然不明白为什么这个问题有时会发生.是否可以像上面this那样编写代码的情况Window

这是我目前遇到问题的实现,但当我将其简化为几行时,我不再有问题:

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>
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

main.js

var dataObj = new DataUnifier();

$(document).ready(function ev_document_ready() {
    dataObj.startAutoUpdating(5);
}
Run Code Online (Sandbox Code Playgroud)

我已经删除了一些不重要的代码,但可能确实如此.当页面加载并且dataObj.startAutoUpdating(5)被调用时,它会在this.stopAutoUpdating()中断开; line因为this指的是Window对象.据我所知(并根据提供的链接),this应该参考DataUnifier对象.我已经阅读了this关键字的许多来源,并且不明白为什么我一直遇到这个问题.我不使用内联事件注册.是否有任何原因这样格式化的代码会有这个问题?

编辑2:对于那些有类似问题的人,请参阅this此Mozilla文档页面中页面下方的" 问题":http://developer.mozilla.org/en-US/docs/Web/API/Window.setInterval

Aad*_*hah 2

我最喜欢的定义类的方式如下:

function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}
Run Code Online (Sandbox Code Playgroud)

使用该defclass函数,您可以定义MyClass如下:

var MyClass = defclass({
    constructor: function () {
        this.myProp = "foo";
    },
    myMethod: function () {
        console.log(this.myProp);
    }
});
Run Code Online (Sandbox Code Playgroud)

顺便说一句,你的实际问题不在于课程。this.getUpdates这是您拨打电话的方式setTimeout

this.autoUpdateIntervalObj = setInterval(this.getUpdates, interval * 1000);
Run Code Online (Sandbox Code Playgroud)

相反,它应该是:

this.autoUpdateIntervalObj = setInterval(function (self) {
    return self.getUpdates();
}, 1000 * interval, this);
Run Code Online (Sandbox Code Playgroud)

因此你的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;
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

是不是很简洁?如果您需要继承,请查看augment.

编辑:正如评论中所指出的,将附加参数传递给Internet Explorer 版本低于 9 的版本setTimeoutsetInterval在 Internet Explorer 版本低于 9 的版本中不起作用。可以使用以下填充程序来解决此问题:

<!--[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]-->
Run Code Online (Sandbox Code Playgroud)

由于该代码仅在低于 9 的 Internet Explorer 版本上有条件地执行,因此完全不引人注目。只需将其包含在所有其他脚本之前,您的代码就可以在每个浏览器上运行。