最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Recursive prototypal inheritance in javascript? - Stack Overflow

programmeradmin4浏览0评论
Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};

From Prototypal Inheritance in JavaScript

I've been using this code for a while to create new objects inheriting from previous ones. However I ran across a little surprise.

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]

In trying to change c.foo I changed a.foo instead, c.foo showing the change because it's inheriting from a. The only solution I see right now is to only modify direct properties of b:

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

I'm sure there's a better solution I'm missing! How can I create new objects from old objects without risking modifying the original?

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

From Prototypal Inheritance in JavaScript

I've been using this code for a while to create new objects inheriting from previous ones. However I ran across a little surprise.

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]

In trying to change c.foo I changed a.foo instead, c.foo showing the change because it's inheriting from a. The only solution I see right now is to only modify direct properties of b:

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

I'm sure there's a better solution I'm missing! How can I create new objects from old objects without risking modifying the original?

Share Improve this question edited May 27, 2011 at 3:28 AnnanFay asked May 27, 2011 at 3:22 AnnanFayAnnanFay 9,76915 gold badges67 silver badges89 bronze badges
Add a ment  | 

5 Answers 5

Reset to default 1

+1 for referencing Crockford =D

I think arrays are always passed by reference, and the array is never copied. I think Array.slice would copy it, but that's a fringe case...

The create function is mostly used to copy functions and such. I don't ever use that method, as I just create a constructor function:

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

This way, you'll always get a new array for each call. Just do b = new a(); It works for me...

I try to avoid inheritance, because there's always problems with it. I would just use the constructor, then assign new variables to it (not the prototype). Multiple inheritance gets tricky though...

When you create a new object from a prototype no copying is done. Not even the property is copied:

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

If you do

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

Then you are not changing the property in F.protoype, you are adding a new property to f:

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

So this applies to every value you assign a property. If you want to clone a value you have to do it explicitly, simplest way is to just set a new property in the constructor:

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

This problem only arises when the prototype contains mutable values, other values can not be modified (the properties can change values instead).

And if you want to "subclass" F, remember to call it from the new constructor:

function F() { this.a = []; }
function G() { F.call(this); }
G.prototype = new F();

The best inheritance pattern I've ever seen is the Google Closure Library's one. It's based on constructors. Here is example:

//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();

And the result is as you wish:

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]

Well yes:

1)

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

replace the your F.prototype reference of a with "test" so you see no error but actually this is worse then 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]

modifies the a object since c.foo[0] & a.foo point to it (they are references/pointers to the a value)

Solution here

This is solution:


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

General restriction of this solution is that prototypes should not have recursive referencies.

Also it is possible to solve this problem without that restriction, but solution would be more plex. To prevent infinite recursive object creation you may use some kind of temporary map storage during initialization process. This storage would be cache referencies between initialized objects.

发布评论

评论列表(0)

  1. 暂无评论