JavaScript面向对象编程/类 - 多个实例共享相同的数据

6

我正在编写一些面向对象的Javascript代码。我有几个类的实例,并且已经将不同的数据放入其中。不幸的是,如下面的例子所示,它们似乎共享相同的数据。

是否可能获得两个单独的类实例?应该如何实现。

Index.html

 <html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript">
debugger;

// Do this because a page resart seems to keep old data
function SetGlobals()
{
    var ui;
    var el;

    // Arr00
    ui = document.getElementById("Arr00");
    el = arr0.arrayGet(0);
    ui.innerHTML = el.m_String;

    // Arr01
    ui = document.getElementById("Arr01");
    el = arr0.arrayGet(1);
    ui.innerHTML = el.m_String;

    // Arr10
    ui = document.getElementById("Arr10");
    el = arr1.arrayGet(0);
    ui.innerHTML = el.m_String;

    // Arr11
    ui = document.getElementById("Arr11");
    el = arr1.arrayGet(1);
    ui.innerHTML = el.m_String;
}

function MyOnLoad()
{
    SetGlobals();
}
</script>
</head>
<body onload="MyOnLoad()" style="width:100%; height: 100%; padding: 0 0 0 0; margin: 0 0 0 0; overflow: hidden; background:#000000">

<div id="divScreen" style="display: block; width:100%; height="100%">
    <div id="divMenu" style='float: left; background:#00FF00; border-color: #000000; border-width: 1px;'>
        <table>
            <tr>
                <td>
                    Array 0/String 0: <label id="Arr00"></label>
                </td>
            </tr>
            <tr>
                <td>
                    Array 0/String 1: <label id="Arr01"></label>
                </td>
            </tr>
            <tr>
                <td>
                    Array 1/String 0: <label id="Arr10"></label>
                </td>
            </tr>
            <tr>
                <td>
                    Array 1/String 1: <label id="Arr11"></label>
                </td>
            </tr>
        </table>
    </div>
    <div id="divMain" style='height: 100%; background:#0000FF; margin-left: 250px; border-color: #000000; border-width: 1px;'>
    </div>
</div>
</body>
</html>

Test.js

var BaseARR = function()
{
    _arr = []; // new Array();

    // Public functions that can access private members
    this.Add = function(arg)
    {
        var i, addAt;

        if(arg==null || (addAt = FindEnterPos(arg))<0)
            return false;

        // since adding and not deleting anything, nothing of value will be returned
        _arr.splice(addAt, 0, arg);

        return true;
    };
    // This finds the entry position for in
    FindEnterPos = function(arg)
    {
        return (_arr.length + 1);
    };
    this.arrayGet = function(i)
    {
        return ((_arr != null && i >= 0 && i < _arr.length) ? _arr[i] : null);
    };
};

var stringId = function(id, str)
{
    // public has a this. , privates have just var
    this.m_Id = id; // int
    this.m_String = str; // string
};

// This so allow statics
var stringIdARR = function()
{
    BaseARR.call(this);
};

你从未在任何地方调用过 new BaseARR()。如果要使用该构造函数获取新对象,您需要使用 new 关键字。 - BishopRook
哇,你是在JavaScript中编写Java的完美例子。 - Zirak
你在 _arr 上缺少了一个 var,这使它成为全局变量。 - Eric
2个回答

25

您的代码存在各种问题。让我尝试解释一下。

首先,在JavaScript中,强烈建议不要将开放块大括号放在单行上。您可能会问为什么?运行这两个代码段:

// using "braces on same line" style
(function () {
  return {
    key: 'value'
  };
})();

// using "braces on line by themself"-style
(function ()
{
  return 
  {
    key: 'value'
  }
})();

两个代码片段返回的结果不同,尽管唯一的区别在于大括号的位置。原因是分号插入。在JavaScript中,分号是可选项。因此,如果解析器发现换行符并且换行符前面的结构有意义,它将插入一个分号。在第二个示例中,在 return 语句之后就是这样。如果您将大括号放在与前面语句相同的行上,您可以避免此类错误。
接下来你犯的错误是认为JavaScript有类。JavaScript是面向对象的语言,但与大多数其他面向对象的语言不同,它没有类。在JavaScript中,对象直接从其他对象(它们所谓的原型)继承。您当前所指的“类”实际上是一个构造函数,当使用new关键字调用时,它将创建一个新对象,并从存储在构造函数原型字段中的任何对象继承。
var anObject = {
  key: 'value'
};
function MakeAnObject() {
}

MakeAnObject.prototype = anObject;

var o = new MakeAnObject();

console.log(o.key); // will output 'value'

如果您设置一个属性,该属性将始终设置在对象本身上,它永远不会访问原型链,在设置属性时。
如果从没有该属性的对象中读取属性,则JavaScript将搜索对象的原型链(即所有相互继承的对象)以查找该属性,并在找到时返回它。
如果一个对象本身具有属性,则其原型链不会被搜索,因此您可以通过在对象本身上设置属性来“覆盖”对象继承的属性。
请看以下示例:
function MakeThing() {
}

MakeThing.prototype = {
  key: 'value'
};

var o1 = new MakeThing(), o2 = new MakeThing();

console.log(o1); // will output 'value'
console.log(o2); // will output 'value'

o2.key = 'other';

console.log(o1); // will output 'value'
console.log(o2); // will output 'other'

MakeThing.prototype.key = 'changed';

console.log(o1); // will output 'changed'
console.log(o2); // will output 'other'

delete o2.key;

console.log(o1); // will output 'changed'
console.log(o2); // will output 'changed'

考虑到这一点,我必须告诉你:JavaScript 中不存在对象的公有和私有成员。成员将始终是公有的。有一些模式尝试使用闭包隐藏对象中的某些信息,但它们与传统编程语言中的私有成员的功能非常不同。更糟糕的是:这些模式效率低下,产生可怕的代码。如果没有绝对的必要,请不要使用它们。
那么,这意味着什么呢?首先,如果您想在多个对象之间共享属性和方法,则它们必须从相同的原型继承,并且该原型必须包含这些属性和方法。其次,如果您在“this”上设置了某个东西,则它将在当前实例上设置,而不是在原型上设置。第三,只有按照约定来实现私有和公有成员。如果您绝对需要从某个子系统中严格隐藏某些信息,则可以使用相关模式(Crockford 密封器-解封器应该能够产生可用的结果)。
总之,在这里快速尝试修复您的对象:
function BaseAAR {
    this._arr = []; // note >this<. You created a global array in your code.
};

BaseAAR.prototype.add = function(arg) {
    var i, addAt;

    // always use identity (triple) operators when comparing to null!
    if (arg === null || (addAt = this.findEnterPos(arg))<0)
        return false;

// since adding and not deleting anything, nothing of value will be returned
    this._arr.splice(addAt, 0, arg);
    return true;
};
    // This finds the entry position for in
BaseAAR.prototype.findEnterPos = function() {
return (this._arr.length + 1);
};
BaseAAR.prototype.arrayGet = function(i) {
    return ((this._arr !== null && i >= 0 && i < this._arr.length) ? this._arr[i] : null);
};


function StringIdAAR(id, str) {
    BaseAAR.call(this); // invoke the constructor of the base object
    this.m_Id = id; // int
    this.m_String = str; // string
}

StringIdAAR.prototype = BaseAAR.prototype; // innherit from StringIdAAR prototype

我不确定这段代码是否仍然能够实现你想要的功能,但你应该明白JavaScript中面向对象模式应该是什么样子的。

如果你想了解更多关于如何编写良好的JavaScript的知识,你绝对应该阅读Douglas Crockford的书籍《JavaScript权威指南》。

更新:我还写了一篇关于JavaScript面向对象和基于原型继承的文章。这可能对经过这里的任何人都会感兴趣。


2
谢谢,这真是一堂JavaScript的大师课。我是一个自学的程序员,之前以为已经开始理解JS了。但这个回复让我更加清楚地认识到实际情况。 - Rewind

1

如果你需要一个很好的JavaScript继承示例,可以看一下John Resig的Simple Inheritance实现。我已经使用它的修改版本有一段时间了。这篇文章更详细地描述了它。

另一个提供类似功能的库是base2,但可能对你的需求过于复杂。

最后,我过去也使用过另一种流行的方法,即模块模式。此文章也提供了该模式的深入解释。


谢谢。我会阅读那些链接。我学得越多,就会理解得越多。 - Rewind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接