2

考虑示例 html 代码:

<head>
    ....
    <script src="/js/MyClass.js"></script>
    <script src="/js/extend.js"></script>
    <script>
        $(document).ready(function($) {
            var $obj=new MyClass();
            $obj.run();
        });
    </script>
</head>
<body>

MyClass.js 文件:

var MyClass=function()
{
    this.run=function() {
        alert("MyClass");
    }
}

扩展.js 文件:

MyClass.prototype.run=function() {
    alert("Extend");
}

为什么此代码会提示“MyClass”而不是“Extend”?如何正确重写(重载)类方法?

4

3 回答 3

5

这一切都与JS如何<object>.<property/function>解析表达式有关。我之前已经详细解释了这一点,但这是适用于您的案例的示意图:

[    MyClass.run    ]<=========================================================\ \
MyClass[run] ===> JS checks instance for property run                           | |
 /\ ||                                                                          | |
 || || --> property found @instance, resolve value------------------------------| |
 || ||                                                                          | |
 || ===========> MyClass.prototype.run could not be found? check prototype      | |
 ||      ||                                                                     | |
 ||      ||--> OR property found @MyClass.prototype, return---------------------| |
 ||      ||                                                                     | |
 ||      ==========> Object.prototype.run: not found check prototype?           | |
 ||          ||                                                                 | |
 ||          ||--> property found @Object.prototype, return---------------------|-|
 ||          ||                                                                 |=|
 ||          =======> chech prototype of Object.prototype -> undefined~~~~~~~~~~|X|
 ||                                                                             \ /
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~< TypeError can't read property run of undefined

这是 JS 可以检查run属性的所有地方。因为您的构造函数run在实例 ( this.run = function(){};) 上定义了一个属性,所以查找永远不会超过第一步:“JS 检查实例的属性run
原型链永远不会发挥作用。

你问是否以及如何重载JS 方法。简短的回答是:不是,不是真的。重载是一种有效的 OOP 技术,在传统的基于类的 OO 模型中非常方便。JS 不是这样玩的,而是使用原型模型。尝试强制原型系统像传统的 OO 语言一样工作可能的(由于原型系统的灵活性),但它需要付出太多努力才能跟上,而且通常不值得。
您可以将其与使用普通轿车/轿车耕种田地进行比较。起初,您也许可以,但用不了多久您就会陷入困境,并且不得不使用拖拉机将您拖出田野。

如果您仍然想尝试一下,方法如下:

function MyClass()
{
    this.run = function()
    {
        console.log('child method');
        var backup = this.run;//store reference to instance property
        delete this.run;//delete instance property
        this.run();//call again
        //but now, the instance property is missing, JS will use the prototype
        this.run = backup;//restore instance property
    };
}
MyClass.prototype.run = function()
{
    console.log('prototype run method');
};
var foo = new MyClass;
foo.run();
//logs:
//child method
//prototype run method

您可能会发现看这里很有用,这是我在前面的答案中更详细地解释了 JS 解析表达式的方式。在我的答案的底部,我还添加了一些关于此事的更多链接,可能也值得一看......

于 2013-10-09T11:06:07.727 回答
1

只有当对象本身还没有该属性时,才使用原型。

在您的情况下,您将run对象本身放在上面,这就是run将要解决的问题。

在某种程度上,您的 MyClass 正在覆盖它自己的原型。

也许你想要

var MyClass=function(){};
MyClass.prototype.run = function() {
    alert("MyClass");
}

var overridden = new MyClass();
overridden.run = function() {
     alert("Extend");
}

那将是一个具有与其原型不同的方法的实例。

或者您可以链接原型来模拟类层次结构。

于 2013-10-09T10:36:09.107 回答
0

您的 MyClass 构造函数正在覆盖原型。以下代码将依次提醒“MyClass”、“Extended”和“Overriden”。最后一个警报显示更改实例的功能不会更改原型的功能。

var MyClass=function()
{
    this.run();
    MyClass.prototype.run = function() { alert( "Extended" ) };
    this.run();
    this.run=function() {
        alert("Overriden");
    };
    this.run();
};
MyClass.prototype.run = function() { alert( "MyClass" ); };
var myinstance = new MyClass();
alert( myinstance.run === MyClass.prototype.run );
于 2013-10-09T10:58:32.433 回答