Manuel
Manuel

Reputation: 473

Copy object via closure

I'm trying to get a copy of an object like this:

graphs = (function () {
    var trends = {
        pointSize: 10,
    };

    // Object Oriented JavaScript - pp 109
    var lockVariable = function(x) {
        return function() {
            return x;
        }
    };

    var getTrendsConfig = function() {
        return lockVariable(trends)();
    };
    return {
        getTrendsConfig : getTrendsConfig
    };
}());

c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())

I was expecting to get printed a "{pointSize: 10}"

Because the getTrendsConfig function would pass the trends object to the lockVariable function, which would return the local value object: "{pointSize : 10}", instead I get "{pointSize: 11}".

I'm taking this from an example of the book "Object Oriented JavaScript" pp 108-109:

enter image description here

How do I get my expected result? Is it possible? And why this doesn't work?

Upvotes: 1

Views: 231

Answers (2)

codejockie
codejockie

Reputation: 10864

graphs = (function () {
    var trends = {
        pointSize: 10,
    };

    // Object Oriented JavaScript - pp 109
    var lockVariable = function(x) {
        return function() {
            return x;
        }
    };

    var getTrendsConfig = function() {
        return lockVariable(trends)();
    };
    return {
        getTrendsConfig : getTrendsConfig
    };
}());

c = Object.assign({}, graphs.getTrendsConfig()); // same as ...graphs.getTrendsConfig() in ES6 spread syntax

c.pointSize = 11;

console.log(graphs.getTrendsConfig());
console.log("c =", c);

It's not producing your expected result because you re-assigned c.pointSize to 11 and that's why you're getting 11;

In JavaScript, assigning an object to a variable is done by reference and not by value. This means that you're simply copying over the object's location in memory, causing any modification to affect the original value.

In your example when you assign c = graphs.getTrendsConfig();, c will now point to the same object's location/address.
When you did this c.pointSize = 11, you modified the same (original) object and not a copy.

Solution: In other to make a copy of graphs.getTrendsConfig() you could use Object.assign() or the new ES6 spread syntax .... By making a copy you won't be modifying the original object's pointSize variable.

Upvotes: 1

Quentin
Quentin

Reputation: 943608

Primitive values, such as numbers, in JavaScript, are immutable. You can copy i to x (as per the book), change i and leave x unchanged.

Objects are not immutable and are only ever addressed by reference. When you return the value of trends (c = graphs.getTrendsConfig();), you are returning a reference to the object (so c and trends both contain a reference to the same object). When you modify it, you modify that object. Getting another copy of trends gives you another copy of the reference… which still points to the same object.

The simple way to deal with this is to move the logic that creates the object inside the function that gets called.

graphs = (function() {
  var lockVariable = function(x) {
    return function() {
      return x;
    }
  };

  var getTrendsConfig = function() {
    var trends = {
      pointSize: 10,
    };
    return lockVariable(trends)();
  };
  return {
    getTrendsConfig: getTrendsConfig
  };
}());

c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())

Although you could simplify that to

graphs = (function() {
  var getTrendsConfig = function() {
    return {
      pointSize: 10,
    };
  };
  return {
    getTrendsConfig: getTrendsConfig
  };
}());

c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())

To go with something close to what your original code is trying to acheive, you could return an explicit copy of the object by using Object.assign()

graphs = (function() {
  var trends = {
    pointSize: 10,
  };

  // Object Oriented JavaScript - pp 109
  var lockVariable = function(x) {
    return Object.assign({}, x);
  };

  var getTrendsConfig = function() {
    return lockVariable(trends);
  };
  return {
    getTrendsConfig: getTrendsConfig
  };
}());

c = graphs.getTrendsConfig();
c.pointSize = 11;
console.log(graphs.getTrendsConfig())

Upvotes: 1

Related Questions