object in prototype is inherited as reference

2019-08-22 17:02发布

我想用原型继承新的对象实例。

测试用例:

var MyObj = function() {}
MyObj.prototype.objName = {} 
// I want this to be a different object for each instance of MyObj

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

o1.objName['a'] = 1;
o2.objName['a'] = 2;

alert(o1.objName['a']) // 2
alert(o1.objName === o2.objName) // true

这意味着,在原型对象不继承它的拷贝,而是作为参考。

我知道,通常你可以不喜欢这样。

var MyObj = function() {
    this.objName = {}
}

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

alert(o1.objName === o2.objName) // false

这工作得很好,但对我来说,这是不是一种选择。 我真的需要定义OBJNAME的MyObj中的功能外。

我设法“解决”这个问题

MyObj.prototype.objName = function() {
    if ( this._objName === undefined ) {
        this._objName = {};
    }
    return this._objName;
}

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

o1.objName()['a'] = 1;
o2.objName()['a'] = 2;

alert(o1.objName()['a']) // 1

但是,这是不是很漂亮,这个代码的性能要差很多。

有没有什么办法,以更优雅解决这个问题?

Answer 1:

这意味着,在原型对象不继承它的拷贝,而是作为参考。

在原型没有被复制-原型继承的整个概念是属性引用原型对象的共享属性。 所以,如果你想有一个属性为每个实例是独立的,你必须明确地将其分配给对象和阴影原型财产; 就像你正在与做_objName在你的代码性能。

但是,这是不是很漂亮,这个代码的性能要差很多。

如果你想它漂亮,它移动到构造函数(或使构造寻找类似的init方法来调用,如果存在,那么你可以创建的原型,init方法。

为了使性能更好一点,你可以改变吸气功能

MyObj.prototype.getObj = function() {
    var obj = {};
    this.getObj = function(){ return obj; }; // overwrite itself
    return obj;
};

但它仍然有函数调用的开销。 对于更优雅,你可以使用删除自身在第一次访问一个getter属性(在旧的浏览器不支持):

Object.defineProperty(MyObj.prototype, "objName", {
    get: function() {
        var obj = {};
        Object.defineProperty(this, "objName", {
            value: obj,
            writable: true //?
        });
        return obj;
    },
    enumerable: true,
    configurable: true
});

现在,你可以省略函数调用括号。



Answer 2:

这意味着,在原型对象不继承它的拷贝,而是作为参考。

只要是明确的。 首先在JavaScript中的所有对象都通过引用传递,而不是价值。 只有原语是按值传递。 其次,你实际上并没有“复制”或“传递”任何东西。 当您设置一个prototype ,你要创建一个原型的链。 这意味着,在您的情况:

var MyObj = function() {};
MyObj.prototype.objName = {} ;

var o1 = new MyObj ();
var o2 = new MyObj ();

无论o1o2没有叫任何财产objName ,你可以简单地测试它:

console.log(Object.keys(o1)); // []

当JS看到这样一个代码o1.objName ,作为第一件事情检查对象具有这个属性,如果有,使用它。 如果没有,开始寻找在原型车的链条,通过原型开始o1 ,这是MyObj.prototype :它发现性能objName ,并将其返回。 如果没有找到它,那么JS会继续检查原型MyObj.prototype ,等等。 所以,这里的要点: MyObj.prototype 它是一个对象:与您共享之间的物体o1o2 。 这就是为什么实例objName是一样的。 这正是具有相同的逻辑:

function objName(obj) {
    return "objName" in obj ? obj.objName : O.objName;
}

var O = { objName: [] };
var foo = {};
var bar = {};

objName(foo).push(0);
objName(bar).push(1);

所以,你不能把在prototype是不是要跨对象共享使用原型创建的任何对象。 我要说的是共享状态一样,也是应该避免的一个不好的做法,这就是为什么在一般的prototype应该不会有这样的属性。 它仍然是我不明白你为什么不能修改构造函数,但问题是:当你创建对象的实例,你要“建立”了。 通常情况下,调用构造函数,但是任何功能是好的。 这也让当你要支持继承,并呼吁“超级”构造函数初始化对象。



文章来源: object in prototype is inherited as reference