0

I am trying to understand how Object.create copies arrays and objects properties when initiating a new object. It seems to be different then copying a string or number. For example if we have a basic Object with a number and array property. jsfiddle example

var obj = {
  num: 0, arr: []
};

We then initiate 3 new Objects from this base.

var set1 = Object.create(obj);
set1.num = 10;
set1.arr.push(1);
var set2 = Object.create(obj);
var set3 = Object.create(obj, {arr: []});

I was expecting set2.num and set2.arr property to be it's initial state. I found this to be true for the number, but not the array. Of course one work around is to pass {arr: []} when initiating the Object or creating a initiation function that resets the arr property.

// false
console.log(set1.num === set2.num);
// true - why is this true???
console.log(set1.arr === set2.arr);
// false
console.log(set1.arr === set3.arr);

Is this the normal behavior? Is Object.create keeping a reference to all of the Object's array and object properties? It would be very nice to not have to create new arrays and objects when initiating a new Object.

jjbskir
  • 8,474
  • 9
  • 40
  • 53
  • 5
    You've made the assumption these are copies/clones. They are not, they're the same objects being inherited through the prototype – Paul S. Dec 02 '15 at 17:36
  • As others have stated, Object.create doesn't work the way you think it does. What you are looking for is closer to the ES6 `Object.assign` or `.extend` functionality of common js libraries. – Jesse Kernaghan Dec 02 '15 at 17:39
  • @JesseKernaghan the methods you mention will not deep clone an _Object_, OP would still experience the same issues. You can use them to make shallow clones (though some functionality may be lost, e.g. `.length` will not auto-update on produced _array-likes_) – Paul S. Dec 02 '15 at 17:41
  • @PaulS. It wasn't my impression that they were looking to deep clone, perhaps I was wrong. In the example given, will `var set1 = Object.assign({}, obj);` not achieve the intended results? – Jesse Kernaghan Dec 02 '15 at 17:57
  • @JesseKernaghan `var foo = {bar: []}, fizz = Object.assign({}, foo), buzz = Object.assign({}, foo); fizz.bar[0] = 'baz';` What is `buzz.bar`? – Paul S. Dec 02 '15 at 18:11
  • 1
    @PaulS. Hence the deep clone, gotcha. Thank you for taking the time to explain! – Jesse Kernaghan Dec 02 '15 at 18:18
  • Thanks for clarifying, I understand more about how JS works, but sometimes wonder why they made it that way :). I have read about Object.create in various articles (https://davidwalsh.name/javascript-objects-deconstruction) but don't see how those examples could be applied to large scale applications. – jjbskir Dec 02 '15 at 22:58

1 Answers1

1

It would be very nice to not have to create new arrays and objects when initiating a new Object

Write a function in your favourite style

  • Returning a literal

    function makeMyObject() {
        return {num: 0, arr: []};
    }
    // usage
    var obj = MyObject();
    
  • Returning an Object.created object, and assigning to it,

    function makeMyObject() {
        var o = Object.create(null); // or some prototype instead of `null`
        return Object.assign(o, {num: 0, arr: []});
    }
    // usage
    var obj = MyObject();
    
  • Using new

    function MyObject() {
        this.num = 0;
        this.arr = [];
    }
    
    // usage
    var obj = new MyObject();
    

Cloning is a bit more complicated, a basic example might be

function shallowClone(o) {
    var e;
    if (typeof o !== 'object')
        return o;
    e = Object.create(Object.getPrototypeOf(o));
    // copy enumerable references
    Object.assign(e, o);
    // or to keep non-enumerable properties
    // Object.defineProperties(b, Object.getOwnPropertyNames(o).map(Object.getOwnPropertyDescriptor.bind(Object, o)));
    return e;
}

Deep cloning requires looping over properties (e.g. for..in for enumerable only) and type checking instead of simply copying everything over. You usually end up needing to recurse on properties which are Objects themselves.

For known types, you can teach it to use the correct constructor too, e.g.

if (Array.isArray(o)) {
    e = [];
    o.forEach((v, i) => e[i] = recurse(v));
}

Where recurse would be the name of the clone function

Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Thanks for clarifying and providing some solutions! Using the 'new' example probably makes the most sense for my current application. I was hoping to try out Object.create, but now don't see how it can be used in a large scale application with a set up like I described in the question. – jjbskir Dec 02 '15 at 22:55