Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
来自Prototypal Inheritance in 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.foo
,c.foo
显示了更改,因为它继承自a
。我现在看到的唯一解决方案是仅修改b
的直接属性:
d = Object.create(a);
d.foo = Object.create(a.foo);
d.foo[0] = 'text';
我确信我有一个更好的解决方案! 如何从旧对象创建新对象而不冒修改原始文件的风险?
答案 0 :(得分:1)
+1引用Crockford = D
我认为数组总是通过引用传递,并且永远不会复制数组。我认为Array.slice会复制它,但那是一个边缘情况......
create函数主要用于复制函数等。我不会使用该方法,因为我只是创建一个构造函数:
function a() {
this.foo = [0, 1, 2];
}
这样,您将始终为每个呼叫获取一个新阵列。只做b = new a();它对我有用......
我尽量避免继承,因为它总是存在问题。我只想使用构造函数,然后为其分配新变量(而不是原型)。虽然多重继承变得棘手......
答案 1 :(得分: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();
答案 2 :(得分: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]
答案 3 :(得分: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]
修改一个对象,因为c.foo [0]& a.foo指向它(它们是指向a值的引用/指针)
解决方案here
答案 4 :(得分: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();
};
这个解决方案的一般限制是原型不应该有递归的参考。
也可以在没有这种限制的情况下解决这个问题,但解决方案会更复杂。为了防止无限递归对象创建,您可以在初始化过程中使用某种临时映射存储。此存储将是初始化对象之间的缓存引用。