9
Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};

来自JavaScript 中的原型继承

我一直在使用这段代码来创建从以前的对象继承的新对象。然而,我遇到了一个小惊喜。

a = {
    foo: [1,2,3]
}

b = Object.create(a);
// b.foo -> [1,2,3]
b.foo = "test";
// b.foo -> "test"
// a.foo -> [1,2,3]

c = Object.create(a);
// c.foo -> [1,2,3]
c.foo[0] = 'test';
// c.foo -> ["test",2,3]
// a.foo -> ["test",2,3]

在尝试更改时,c.foo我改为更改a.fooc.foo显示更改,因为它继承自a. 我现在看到的唯一解决方案是只修改以下的直接属性b

d = Object.create(a);
d.foo = Object.create(a.foo);
d.foo[0] = 'text';

我确定我缺少更好的解决方案!如何从旧对象创建新对象而不冒修改原始对象的风险?

4

5 回答 5

1

+1 引用 Crockford =D

我认为数组总是通过引用传递,并且永远不会复制数组。我认为 Array.slice 会复制它,但这是一个边缘案例......

create 函数主要用于复制函数等。我从不使用该方法,因为我只是创建了一个构造函数:

function a() {
    this.foo = [0, 1, 2];
}

这样,每次调用都会得到一个新数组。只需执行 b = new a(); 这个对我有用...

我尽量避免继承,因为它总是有问题。我只会使用构造函数,然后为其分配新变量(而不是原型)。多重继承虽然变得棘手......

于 2011-05-27T03:37:02.753 回答
1

当您从原型创建新对象时,不会进行复制。甚至没有复制属性:

function F() {}
F.prototype.a = 1
new F().hasOwnProperty("a") // => false
new F().a // => 1

如果你这样做

var f = new F();
f.a = 2;

然后您不会更改 中的属性F.protoype,而是将新属性添加到f

var f = new F();
f.a = 2;
f.a // => 2;
delete f.a;
f.a // => 1

因此,这适用于您分配属性的每个值。如果你想克隆一个值,你必须明确地这样做,最简单的方法是在构造函数中设置一个新属性:

function F() {
  this.a = [];
}
var f = new F();
var g = new F();
f.a === g.a // => false

仅当原型包含可变值时才会出现此问题,不能修改其他值(属性可以更改值)。

如果你想“子类化”F,记得从新的构造函数中调用它:

function F() { this.a = []; }
function G() { F.call(this); }
G.prototype = new F();
于 2011-05-27T03:47:48.277 回答
1

我见过的最好的继承模式是 Google Closure Library 的。它基于构造函数。这是示例:

//with this method we will inherit one "class" (there are no real classes in JS) from another.
var inherit = function (c, p) {
    function F() {}
    F.prototype = p.prototype;
    c.prototype = new F;
    c.prototype.constructor = c;
};

//create one "class"
var A = function(){
    this.foo = [1,2,3];
}
//create its instance
var a = new A();

//create another "class"
var C = function(){
    //call the parent constructor
    A.call(this);
}
//make inheritance
inherit(C, A);
//create example of inherited "class"
var c = new C();

结果如你所愿:

console.log(c.foo);
//-> [1,2,3]
c.foo[0] = 'test';
console.log(c.foo);
//-> ['test',2,3]
console.log(a.foo);
//-> [1,2,3]
于 2011-05-27T04:08:11.217 回答
0

嗯,是:

1)

b.foo = "test";
// b.foo -> "test"

用“test”替换你的 F.prototype 引用,这样你就看不到错误,但实际上这比 2 更糟

2)

c = Object.create(a);
// c.foo -> [1,2,3]
c.foo[0] = 'test';
// c.foo -> ["test",2,3]
// a.foo -> ["test",2,3]

修改 a 对象,因为 c.foo[0] & a.foo 指向它(它们是指向 a 值的引用/指针)

解决方案在这里

于 2011-05-27T03:38:56.523 回答
0

这是解决方案:


Object.create = function (o) {
    function F() {
        for(var prop in this)
            if(o.hasOwnProperty(prop))
                this[prop] = Object.create(o[prop]);
    }
    F.prototype = o;
    return new F();
};

此解决方案的一般限制是原型不应具有递归引用。

也可以在没有限制的情况下解决这个问题,但解决方案会更复杂。为了防止无限递归对象创建,您可以在初始化过程中使用某种临时地图存储。此存储将是初始化对象之间的缓存引用。

于 2011-05-27T04:07:20.603 回答