1

I have problem with create Object instance without reference.

I researched and found many people suggest using jQuery.extend to create object without reference.

Refer: What is the most efficient way to deep clone an object in JavaScript?

But it not success in my case.

Here is my code JSBin

var MyModel = (function() {
  MyModel = function() {};

  var myModelObj = {
    prop1: null,
    prop2: {
      sub1: null,
      sub2: null
    }
  };

  MyModel.prototype = {
    getProp1: function() {
      return myModelObj.prop1;
    },
    getSub1: function() {
      return myModelObj.prop2.sub1;
    },
    getSub2: function() {
      return myModelObj.prop2.sub2;
    },
    setProp1: function(val) {
      myModelObj.prop1 = val;
    },
    setSub1: function(val) {
      myModelObj.prop2.sub1 = val;
    },
    setSub2: function(val) {
      myModelObj.prop2.sub2 = val;
    },
    getObj: function() {
      return $.extend({}, myModelObj);
    },
    setObj: function(json_obj) {
      myModelObj.prop1 = json_obj.prop1;
      myModelObj.prop2.sub1 = json_obj.prop2.sub1;
      myModelObj.prop2.sub2 = json_obj.prop2.sub2;
    },
    setParam: function(prop1, sub1, sub2) {
      myModelObj.prop1 = prop1;
      myModelObj.prop2.sub1 = sub1;
      myModelObj.prop2.sub2 = sub2;
    }
  };
  return MyModel;
}());

var model1 = new MyModel();
model1.setParam('prop1', 'sub1', 'sub2');
var model2 = new MyModel();
model2.setParam('clone-prop1', 'clone-sub1', 'clone-sub2');
console.log("object 1");
console.log(model1.getObj());
console.log("object 2");
console.log(model2.getObj());

My expected result is

model1 = {
   prop1: 'prop1',
   prop2: {
      sub1: 'sub1',
      sub2: 'sub2'
   }
}

model2 = {
   prop1: 'clone-prop1',
   prop2: {
      sub1: 'clone-sub1',
      sub2: 'clone-sub2'
   }
}

But actually, model1 and model2 have same data of model2.

Can someone point me out where i made mistake?

=== Update ===

@arcyqwerty's solution help me solved create object without reference.

var MyModel = function() {
  this.prop1 = null;
  this.prop2 = {
    sub1: null,
    sub2: null
  };
};

MyModel.prototype = {
  getProp1: function() {
    return this.prop1;
  },
  getSub1: function() {
    return this.prop2.sub1;
  },
  getSub2: function() {
    return this.prop2.sub2;
  },
  setProp1: function(val) {
    this.prop1 = val;
  },
  setSub1: function(val) {
    this.prop2.sub1 = val;
  },
  setSub2: function(val) {
    this.prop2.sub2 = val;
  },
  getObj: function() {
    return $.extend({}, this);
  },
  setObj: function(json_obj) {
    this.prop1 = json_obj.prop1;
    this.prop2.sub1 = json_obj.prop2.sub1;
    this.prop2.sub2 = json_obj.prop2.sub2;
  },
  setParam: function(prop1, sub1, sub2) {
    this.prop1 = prop1;
    this.prop2.sub1 = sub1;
    this.prop2.sub2 = sub2;
  }
};

var model1 = new MyModel();
model1.setParam('prop1', 'sub1', 'sub2');
var model2 = new MyModel();
model2.setParam('clone-prop1', 'clone-sub1', 'clone-sub2');
console.log("object 1");
console.log(model1.getObj());
console.log("object 2");
console.log(model2.getObj());

But I also want use encapsulation feature in OOP. It means, we only get value object, property through get function. Is it possible on Javascript? It explain why i have an object inside Model (but it reference on same object)

Thank you very much!

4
  • 2
    Note that the accepted answer you linked to is not an answer to the question. BTW, the way you've wrapped your code in an IIFE does nothing useful, it's just confusing. Oh, your issue is that model1 and model2 both reference the same myModelObj object held in a closure by your IIFE. jQuery has nothing to do with the issue, nor does cloning the object. Commented Mar 3, 2016 at 3:10
  • @RobG: I want use encapsulation feature too. Is it possible on Javascript? Please see my updated question Commented Mar 3, 2016 at 3:29
  • Yes, it's possible. The thing being encapsulated (i.e. held in a closure) is myModelObj, but it seems that that isn't what you want to do. There is a public MyModel constructor in either case, so just use arcyqwerty's answer (which creates the same, single global: MyModel). Commented Mar 3, 2016 at 4:41
  • @RobG: I mean is it possible to keep it encapsulated & not refer to one object? Commented Mar 3, 2016 at 6:52

2 Answers 2

3

Try this

var MyModel = function() {
  this.prop1 = null;
  this.prop2 = {
    sub1: null,
    sub2: null
  };
};

MyModel.prototype = {
  getProp1: function() {
    return this.prop1;
  },
  getSub1: function() {
    return this.prop2.sub1;
  },
  getSub2: function() {
    return this.prop2.sub2;
  },
  setProp1: function(val) {
    this.prop1 = val;
  },
  setSub1: function(val) {
    this.prop2.sub1 = val;
  },
  setSub2: function(val) {
    this.prop2.sub2 = val;
  },
  getObj: function() {
    return $.extend({}, this);
  },
  setObj: function(json_obj) {
    this.prop1 = json_obj.prop1;
    this.prop2.sub1 = json_obj.prop2.sub1;
    this.prop2.sub2 = json_obj.prop2.sub2;
  },
  setParam: function(prop1, sub1, sub2) {
    this.prop1 = prop1;
    this.prop2.sub1 = sub1;
    this.prop2.sub2 = sub2;
  }
};

var model1 = new MyModel();
model1.setParam('prop1', 'sub1', 'sub2');
var model2 = new MyModel();
model2.setParam('clone-prop1', 'clone-sub1', 'clone-sub2');
console.log("object 1");
console.log(model1.getObj());
console.log("object 2");
console.log(model2.getObj());

The problem with your original constructor is that instances of MyModel, although different objects created with the new keyword, all share the same myModelObj (which is only ever created once). Using this solution, new fields are created each time you craete a new MyModel.

This is similar to having MyModel = function() { this.myModelObj = {...}; } and accessing fields using this.myModelObj.prop but at that point, myModelObj is a bit redundant as you can just set the properties on this directly.

Also, using this solution, you can use model1.prop directly without having to say model1.getObj().prop (although that works too)

--

Note: it's also a little strange for me to see

var ClassName = (function() {
  ClassName = function() { ...; };
  ClassName.prototype = { ... };
  return ClassName;
})();

Is there a reason you're doing that instead of

var ClassName = function() { ... };
ClassName.prototype = { ... };

?

I suppose it makes sense in the original code if you didn't want to pollute the namespace with myModelObj, but it seems unnecessary otherwise.

--

Edit: encapsulation

If you require an object's properties to be set through getters/setters, you could try something like this:

var MyModel = function() {
  var privateObject = {
    prop1: null,
    prop2: {
      sub1: null,
      sub2: null
    }
  };
  Object.defineProperty(this, 'prop1', {
    get: function() { 
      console.log('Getting prop1 through getter');
      return privateObject.prop1;
    },
    set: function(value) {
      console.log('Setting prop1 through setter');
      privateObject.prop1 = value;
    }
  });
};

The downside is that you won't be able to share getter/setter functions using the prototype chain, meaning you'll have a lot of function objects hanging around. For a small number of instances, this is probably fine (performance-wise). It will also affect inheritance, if your class has subclasses.

If you're on a platform without defineProperty, you can also replicate this by keeping the var privateObject in the constructor and using this.getProp1 = function() { return privateObject.prop1; } in the constructor instead of on the prototype. The net effect is similar to using defineProperty.

--

Edit: or using getter/setter syntax

Note: the returned object is not an instanceof F.

function F() {
  var fields = { prop: null };
  return {
    get prop() {
      console.log("getter");
      return fields.prop;
    },
    set prop(value) {
      console.log("setter");
      fields.prop = value;
    }
  };
}
f = new F
f.prop = 123
f.prop
Sign up to request clarification or add additional context in comments.

7 Comments

Thank you very much for your help. I tried your solution and it solved my problem. But my expectation is user cannot see property directly, like encapsulation feature in OOP. If they want get value, they MUST get through get... function. That's why i have an object inside Model. Any solution can solve this?
You can have strong encapsulation, with privacy more strict than in Java or C# (no reflection loopholes), and then have each object contain its own copy of its member functions, or you can have shared methods, in which case the data must be publicly available on the object. There aren't good options to achieve both.
@ScottSauyet: of course, the correctness must be highest priority. But I wonder how we protect properties on Javascript? Example, use setter method, we can at least check input value before assign to variable.
Updated with a way to reach your goals, although it breaks prototypical inheritance and results in fields * 2 * n Function objects for n instances (as opposed to fields * 2 Function objects for n instances)
Adding an answer that shows how you can achieve that sort of encapsulation, at the expense of losing the sharing of methods.
|
1

This variant of arcyqwerty's answer demonstrates a much deeper encapsulation of your data. The trade-off is that each instance gets its own copy of the methods, rather than sharing them at a "class" level:

var MyModel = function() {
  var prop1 = null;
  var prop2 = {
    sub1: null,
    sub2: null
  };
  this.getProp1 = function() {
    return prop1;
  };
  this.getSub1 = function() {
    return prop2.sub1;
  };
  this.getSub2 = function() {
    return prop2.sub2;
  };
  this.setProp1 = function(val) {
    prop1 = val;
  };
  this.setSub1 = function(val) {
    prop2.sub1 = val;
  };
  this.setSub2 = function(val) {
    prop2.sub2 = val;
  };
  this.getObj = function() {
    return {
      prop1: prop1,
      prop2: {
        sub1: prop2.sub1,
        sub2: prop2.sub2
      }
    };
  };
  this.setObj = function(json_obj) {
    prop1 = json_obj.prop1;
    prop2.sub1 = json_obj.prop2.sub1;
    prop2.sub2 = json_obj.prop2.sub2;
  };
  this.setParam = function(_prop1, _sub1, _sub2) {
    prop1 = _prop1;
    prop2.sub1 = _sub1;
    prop2.sub2 = _sub2;
  };
};

You said,

But I wonder how we protect properties on Javascript?

I think that's the wrong question. JS is a different language, with different concerns than, say, Java or Ruby. It is at least as much a functional language as an OO one. You probably should not try to act as though you're working in Java when you're doing JS, but learn its folkways instead.

1 Comment

thank you for explain to me! I come to JS with Java background, so they are new to me. And part of my question: Is it possible on Javascript? Thanks again :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.