4

I'm trying to clone some objects using the jquery extend method. However, after cloning my object, I realized that some methods of the cloned object were modifying values from the original object so I thought both objects properties may be pointing at the same variable.

After running some tests, I figured that the properties of my cloned object got copied correctly... including the local self variable (which is used to hold the reference to the original this of the class instance). Since the cloned self is still pointing at the original instance, the methods referring to that variable are targeting the properties of the original instance instead of it's own instance:

var Animal = function() {

  var self = this;

  this.sound = "";

  this.talk = function(){
    alert(self.sound);
  };

};

var dog = new Animal();
dog.sound = "Woof";
dog.talk(); //Woof as expected

var cat = $.extend(true, {}, dog); 
cat.sound = "Meow";
cat.talk(); //Woof... but Meow was expected

The extend method behavior does make sense... but is there a generic way to make sure the self variable refers to the new cloned object? By generic, I mean that the self variable may have a different name (that, _this, etc) and that I would like to avoid adding custom methods to every class just to update the self variable.

You should also keep in mind that the self variable is private (and there's many good reasons to keep it like this) so this mean you can't set it outside the instance.

The_Black_Smurf
  • 5,041
  • 14
  • 50
  • 66
  • 4
    `extend` extends the object, it doesn't create a new instance. I think the only (and right) way to do this, is to create a new Animal, instead of copying the old animal. – adeneo Jan 08 '15 at 01:59
  • 1
    Or, create a method that changes the property with the correct context -> **http://jsfiddle.net/1572gy9o/** – adeneo Jan 08 '15 at 02:03
  • Indeed... It makes more sense to take an animal as prototype and create new instances from there. You can make sublevels as you like. Might seem a bit weird to convert a dog into a cat by extending it. – html_programmer Jan 08 '15 at 02:03
  • @adeneo `extend` is still the reference method when we talk about cloning object even if it's not it main purpose. Creating a new instance would be an easy solution to implement (in my case) but there's always a voice in my head saying "Find a generic solution you lazy programmer!". – The_Black_Smurf Jan 08 '15 at 02:38
  • But the generic and correct solution to create a new Animal **is** to create a new instance of the `Animal` class, not clone the dog, so to speak. `$.extend` is not the reference when it comes to instances of classes, that's something it's generally not used for. – adeneo Jan 08 '15 at 02:42

2 Answers2

3

As a matter of fact, you don't need the var self = this construct for this use case.

Just made a small test here: http://jsfiddle.net/quwdeafd/
Take a look at the scope of this. Isn't that what you were looking for?

var Animal = function() {

  this.sound = "";
  this.talk = function(){
      console.log(this); //Logs Animal the first time, object the second 
      console.log(this.sound); //Logs Woof and meow 
  };

};

var dog = new Animal();
dog.sound = "Woof";
dog.talk(); 

var cat = $.extend(true, {}, dog); 
cat.sound = "Meow";
cat.talk(); 

Small remark: usually it's custom to put this method on the prototype of the object:

Animal.prototype.talk = function(){
      console.log(this); 
      console.log(this.sound);
  };

Because it makes the method immediately available without needing to be initialized on every instantiation.

html_programmer
  • 14,612
  • 12
  • 59
  • 125
  • 1
    `var self = this` is actually a fairly good practice, since `this` changes depending on scope, so if you need to access `this` in deeper scopes, where `this` changes, assigning it to `self`, or any variable will allow it to be accessed in deeper scopes. [Check out this StackOverflow Question](http://stackoverflow.com/questions/337878/var-self-this) – Brandon Anzaldi Jan 08 '15 at 02:33
  • It works fine without the `self` variable, but I think the point was to actually keep it, as the OP seems to need it for something? – adeneo Jan 08 '15 at 02:33
  • @BrandonAnzaldi I agree, I use _.bind instead, relatively often. But for this example, it doesn't seem to serve any purpose. – html_programmer Jan 08 '15 at 02:36
  • @adeneo Possibly, I don't know. But if not, then the solution is pretty simple :-) – html_programmer Jan 08 '15 at 02:37
  • I guess you already figured out my Animal was just an example. As Brandon Anzaldi pointed out, my real class has many scope level and the `self` is really handy (see mandatory) when you enter those scopes. – The_Black_Smurf Jan 08 '15 at 02:47
1

As adeneo pointed out, the problem is at another level. Even if the extend is often referred to as a cloning method, it's wasn't made for this purpose. The problem mentioned in this question is a good example of it's limitations.

For the record, I stopped using the extend method for cloning purpose and I solved my problem by replacing it with a cloning method taken from this SO answer.

Community
  • 1
  • 1
The_Black_Smurf
  • 5,041
  • 14
  • 50
  • 66