javascript中的递归原型继承?

时间:2011-05-27 03:22:24

标签: javascript inheritance prototypal-inheritance

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.fooc.foo显示了更改,因为它继承自a。我现在看到的唯一解决方案是仅修改b的直接属性:

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

我确信我有一个更好的解决方案! 如何从旧对象创建新对象而不冒修改原始文件的风险?

5 个答案:

答案 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();
};

这个解决方案的一般限制是原型不应该有递归的参考。

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