简体   繁体   中英

Javascript - Create object without reference

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!

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 = {...}; } 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; } 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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM