在 JS 对象的不同实例之间正确隔离数据

Isolating data correctly between different instances of JS objects

本文关键字:之间 隔离 数据 实例 JS 对象      更新时间:2023-09-26

直到今天我才知道这个问题,我想知道解决这个问题的最佳方法是什么。问题是,当您从同一构造函数实例化两个不同的对象时,它们共享相同的原型,这意味着如果一个对象设置了该原型,则所有其他对象也将更改。

例如:

function A(obj) {
}
A.prototype = {
    events: {
        one: 1
    }
};

var b = new A();
console.log(b.events);
var c = new A();
console.log(c.events);
b.events.x = 2;
console.log(b.events);
console.log(c.events);  //whoops, c also got b's event x

什么鬼?解决这个问题的最佳方法是什么?

这就是我想出的,但我想知道是否有更好的方法?

var _ = require('underscore');
function A(obj) {
    if (obj == null) {
        obj = {};
    }
    if(obj.events == null){
        obj.events = {};
    }
    this.events = _.extend(obj.events, A.prototype.events);
}
A.prototype = {
    events: {
        one: 1
    }
};

var b = new A({events:{three:3}});
console.log(b.events);
var c = new A({events:{four:4}});
console.log(c.events);
b.events.x = 2;
console.log(b.events);
console.log(c.events); //now it's better...(this is crazy)

正如您所发现的,原型在给定对象的所有实例之间共享。 这样做有几个原因,包括存储效率。 因此,您应该只将打算在所有实例之间相同共享的内容放在原型中。

如果您希望某些内容成为实例变量,该变量可以为每个单独的实例具有唯一值,则它不属于原型。 它属于实例变量,通过this指针设置。

因此,如果您的 event 属性旨在成为每个实例可能不同的实例变量,请在构造函数中初始化它,它对于每个单独的实例都是唯一的。

function A(obj) {
    this.events = {one: 1};
}

并将其从原型中删除。 请记住,原型是为给定类型对象的所有实例之间相同共享的内容而设计的。 这就是为什么它非常适合方法,很少用于可修改的数据。

工作代码示例:

function A() {
    this.events = {one: 1};
}
var x = new A();
var y = new A();
log(x.events);
log(y.events);
log("--------------");
x.events.one = 2;
y.events.one = 3;
log(x.events);
log(y.events);
function log(x) {
    var d = document.createElement("div");
    if (typeof x === "object") {
        x = JSON.stringify(x);
    }
    d.textContent = x;
    document.body.appendChild(d);
}

为什么这么复杂?

function A(obj) {
    this.events = {
        one: 1
    };
}

你不应该在原型中保存变量,而应该在你的构造函数中使用它。在您的情况下,解决方案将是:

    function A(obj) {
        this.events = {
            one: 1
        };
    }

    var b = new A();
    console.log(b.events);
    var c = new A();
    console.log(c.events);
    b.events.x = 2;
    console.log(b.events);
    console.log(c.events);

第一步是学习原型遗传。

所有原型继承都是对值的实时引用......

而在被子里,

A.prototype.method = function foo () { };
var a = new A();
a.__proto__.method === a.constructor.prototype.method;
a.__proto__ === A.prototype;

除非您将".prototype"的值更改为新对象......然后,旧实例将指向旧原型对象,新实例将指向新原型对象。

这是基本的深拷贝与浅拷贝,价值与参考的东西。私有变量没有Java/C#继承,除了通过闭包(看起来不像Java/C#)。

如果你要把东西放在原型上,它们应该只是方法,以及每个实例静态可用的常量/枚举(因为在创建实例时对.constructor.prototype的值的实时引用)。

实际上,除非你处于核心内存池优化模式,否则在 ES6 类可用之前(人们理解它们---它们只是.prototype的包装器),这是比它的价值更精神上的麻烦,并且通常(尽管并非总是)更雄辩地简单地构建你需要的东西,或使用 mixins,而不是瞄准看起来像 Java 的 JS(但行为非常, 非常不同)。