Javascript - Create object without reference - javascript

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

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.

Related

Angularjs retain local variable

I have a factory like this:
TestFactory= function () {
var objectName=null;
return {
SetName:function(name) {
objectName = name;
},
GetName:function() {
return objectName;
},
Init:function() {
return angular.copy(this);
}
}
}
A controller like:
TestController = function($scope) {
$scope.TestClick = function () {
var tstA = TestFactory.Init();
var tstB = TestFactory.Init();
tstA.SetName('test A')
tstB.SetName('test B')
console.log('A', tstA.GetName());
console.log('B', tstB.GetName());
}
}
In the console I get Test B for both objects.
How can I make a proper instance of this object?
I would like to use the objectName value in other functions of the factory.
Take into account that in Angular, Factories are singletons, so the instance is always the same.
You can do the following:
TestFactory= function () {
var objectName={};
return {
SetName:function(property,name) {
objectName[property] = name;
},
GetName:function(property) {
return objectName[property];
},
Clear:function(property) {
delete objectName[property]
}
}
}
Then in your controller:
TestController = function($scope, TestFactory) {
$scope.TestClick = function () {
TestFactory.SetName('a','test A')
TestFactory.SetName('b','test B')
console.log('A', TestFactory.GetName('a')); // test A
console.log('B', TestFactory.GetName('b')); // test B
}
}
Couple of issues. First your returning an object rather than a function from your factory.
app.factory('TestFactory', function() {
return function() {
var objectName = null;
var setName = function(name) {
objectName = name;
};
var getName = function() {
return objectName;
};
return {
SetName: setName,
GetName: getName
};
};
});
Then you can just instantiate like this:
var tstA = new TestFactory();
var tstB = new TestFactory();
Services and factories are singletons so I think you can achieve what you want with a more appropriate use of the factory by providing an Init function that returns the common code and unique name like so:
angular.module('app')
.factory('ServiceFactory', serviceFactory);
function serviceFactory() {
return {
Init: function (name) {
return {
objectName: name,
setName: function (name) {
this.objectName = name;
},
getName: function () {
return this.objectName;
}
};
}
};
}
This leaves the possibility to use it as a factory that can initialize many types.
You basically need to create a simple getter/setter.
angular.module('app', [])
.controller('TestController', testController)
.service('serviceFactory', serviceFactory);
testController.$inject = ['serviceFactory'];
function testController(serviceFactory) {
serviceFactory.set('A', {
name: 'test A'
});
serviceFactory.set('B', {
name: 'test B'
});
console.log(serviceFactory.getAll());
console.log(serviceFactory.get('A'));
console.log(serviceFactory.get('B'));
}
function serviceFactory() {
var
_model = {
name: ""
},
_data = {};
return {
set: function(key, data) {
_data[key] = angular.extend({}, _model, data);
},
get: function(key) {
return _data[key];
},
getAll: function() {
return _data;
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<body ng-app="app" ng-controller="testController"></body>

Acess to this from subobject in JavaScript

How do I get access to the properties or method of the main object, from sub-obiect level two (sub3). If possible I would like to avoid solutions chaining return this.
Obj = function () {};
Obj.prototype = {
name: 'name',
main: function(){
console.log(this.name);
},
subobject: {
sub2: function () {
console.log(this);
},
sub3: function () {
console.log(this.name); // How access to Obj.name ??
}
}
}
o = new Obj();
o.main(); // return name
o.subobject.sub2(); // return subobject
o.subobject.sub3(); // return undefined
With your current syntax, you can't. Because for sub2 and sub3, the this variable is Obj.prototype.subobject.
You have multiple choice:
The obvious one: don't use a suboject.
Create subobject, sub2 and sub3 in the constructor
Obj = function() {
var self = this;
this.subobject = {
sub1: function() { console.log(self); }
}
}
Use bind at each call:
o.subobject.sub2.bind(o)();

Console logging "this" returns "null"

I am trying to create a flux store for a React app I am building. I am using an object-assign polyfill npm package and Facebook's Flux library.
Initially I was getting the error "Cannot read property '_data' of null' error in the console which was refering to var currIds = this._data.map(function(m){return m.id;});. That method is currently the only one being called directly. I then did console.log(this) which returned "null".
I find this strange. What is going on?
My code:
var Assign = require('object-assign');
var EventEmitterProto = require('events').EventEmitter.prototype;
var CHANGE_EVENT = 'CHANGE';
var StoreMethods = {
init: function() {},
set: function (arr) {
console.log(this);
var currIds = this._data.map(function(m){return m.id;});
arr.filter(function (item){
return currIds.indexOf(item.id) === -1;
}).forEach(this.add.bind(this));
},
add: function(item){
console.log(this);
this._data.push(item);
},
all: function() {
return this._data;
},
get: function(id){
return this._data.filter(function(item){
return item.cid === id;
})[0];
},
addChangeListener: function(fn) {
this.on(CHANGE_EVENT, fn);
},
removeChangeListener: function(fn) {
this.removeListener(CHANGE_EVENT, fn);
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
bind: function(actionType, actionFn) {
if(this.actions[actionType]){
this.actions[actionType].push(actionFn);
} else {
this.actions[actionType] = [actionFn];
}
}
};
exports.extend = function(methods) {
var store = {
_data: [],
actions: {}
};
Assign(store, EventEmitterProto, StoreMethods, methods);
store.init();
require('../dispatcher').register(function(action){
if(store.actions[action.actionType]){
store.actions[action.actionType].forEach(function(fn){
fn.call(null, action.data);
})
}
});
return store;
};
I can't see where set is called, however your this can be null if the function is invoked through call (see here) or apply, and your first argument is null.
This also happens in your require.register callback:
fn.call(null, action.data) //First parameter is your 'this'.

Concise definition of Getter and Setter for nesting properties of an object in ES5 or ES6

I need to create getter and setter for nesting object in JavaScript.
At the moment I am using Object.defineProperty() in the code below.
Targeting ES5 or ES6.
I would like
Do you know a less verbose way (or better alternative) to write this?
Alternative solution to the use of .bind(base) on getter and setter properties.
var base = {
_name: 'default',
_properties: {
_top: 0
},
constructor: function () {
}
};
Object.defineProperty(base, 'name', {
get: function () {
return this._name;
},
set: function (value) {
this._name = value;
}
});
Object.defineProperty(base, 'properties', {
get: function () {
return this._properties;
},
set: function (value) {
this._properties = value;
}
});
// nesting properties
Object.defineProperty(base.properties, 'top', {
get: function () {
return this._properties._top;
}.bind(base),
set: function (value) {
this._properties._top = value;
console.log('WORKS!');
}.bind(base)
});
console.log(base.name);
base.name = 'Foo';
console.log(base.name);
console.log(base.properties.top);
base.properties.top = 1000;
console.log(base.properties.top);
Is this the idea? Tested and working es6 via babel on jsbin.
var base = {
_name: 'default',
get name() {
return this._name;
},
set name(n) {
return this._name = n;
},
_properties: {
_top: 0,
get top() {
return this._top;
},
set top(t) {
return this._top = t;
},
},
get properties() {
return this._properties;
},
};
Or class based version:
class Base {
constructor() {
this._name = 'default';
this._properties = {
_top: 0,
get top() { return this._top; },
set top(val) { return this._top = val; }
};
}
get name() {
return this._name;
}
set name(n) {
return this._name = n;
}
get properties() {
return this._properties;
}
}
var b = new Base();

How do you pull out common methods that depend on local variables in Javascript?

I'm using Node.js and am creating some models for my different objects. This is a simplified version of what they look like at the moment.
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
function model() {}
model.init = function(val) {
_.extend(values, val);
return model;
}
model.store = function(cb) {
db.insert(values.type, values, cb);
}
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}
Then I can do:
var foo = Foo();
foo.init({prop1: 'a', prop2: 'b'}).store(function(err) { ... });
A lot of the functions, like model.init and model.store are going to be identical for every model, but they depend on local variables in the closure like values.
Is there a way to pull these functions into a base class that I can then extend each of models with instead of duplicating all of this code? I would like to end up with something like this, but I'm not sure what the base class should look like or the right way to use it to extend Foo.
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
function model() { this.extend(base); }
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}
Yes you could do something like this;
model.js
/** require your db and underscore varialbles etc.. **/
module.exports = function(values, base) {
var model = typeof base == 'function' ? base : function() {};
model.init = function(val) {
_.extend(values, val);
return model;
}
model.store = function(cb) {
db.insert(values.type, values, cb);
}
return model;
}
then the usage would be similar to;
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' };
var model = require('/path/to/model.js')(values);
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}
If you need to extend the constructor
var Foo = module.exports = function () {
var values = { type: 'foo', prop1: '', prop2: '' },
model = function() { ...something here... };
require('/path/to/model.js')(values, model);
model.prop1 = function(val) {
if(!arguments.length) return values.prop1;
values.prop1 = val;
return model;
}
return model;
}

Categories