JavaScript Object Mirroring/One-way Property Syncing - javascript

For security purposes, I have a need for a "mirrored" object. i.e. if I create object A, and shallow-clone a copy of A and call it B, whenever a property of A changes, I hope to have B automatically update itself to reflect the changes, but not the other way around. In other words, one-way property syncing.
My question: is there already a solution out in the wild that I'm not aware of?
Was thinking of implementing a library with observe-js (https://github.com/polymer/observe-js), but thought I should ask around before proceeding. Thanks.

Assuming you don't need to support IE8 and earlier, you can use getters to do that on modern browsers.
function proxy(src) {
var p = {};
Object.keys(src).forEach(function(key) {
Object.defineProperty(p, key, {
get: function() {
return src[key];
}
});
});
return p;
}
var a = {
foo: "Original foo",
bar: "Original bar"
};
var b = proxy(a);
console.log(b.foo); // "Original foo"
a.foo = "Updated foo"; // Note we're writing to a, not b
console.log(b.foo); // "Updated foo"

You can setup the prototype chain for this:
var a = {};
var Syncer = function(){};
Syncer.prototype = a;
var b = new Syncer();
a.foo = 123;
b.foo; // 123
b.bar = 456;
a.bar // undefined
Any property not set on b directly will be looked for on the prototype object, which is a.
You can even wrap this up in a convenience function:
var follow = function(source) {
var Follower = function(){};
Follower.prototype = source;
return new Follower();
}
var a = {};
var b = follow(a);
a.foo = 123;
b.bar = 456;
a.foo; // 123
b.foo; // 123
a.bar; // undefined
b.bar; // 456

Related

Is there any point to use getters/setters for an object?

It seems to me that using getters and setters for an object inside a class has no point to it. As I understand it, get/set is useful because it prevents someone outside the class changing something that shouldn't be changed or changing it to something it shouldn't be. However it seems pointless for objects. For example, I have a person with an address, I want to prevent editing the address, you can only view it:
class Person{
constructor(name, address){
this._name = name;
this._address = address;
}
get address(){
return this._address;
}
}
let bob = new Person("bob", {
street: "123 Main Street",
city: "Los Angelos",
state: "California"
});
But then you can still edit it like this:
let address = bob.address;
address.state = "New York";
To prevent this, I would think that you have to return a copy of the object instead of the reference. However, as far as i know, there is no standard way to deep clone an object. So you either have to shallow clone it, which seems not ideal if you have lots of nested references, or just return the reference to the object, which can be edited.
Am I missing something here?
Consider this class.
class Test {
constructor(val) {
this._foo = val;
}
set foo(val) {
throw new Error("It's not possible to change the foo property!")
}
set boo(val) {
console.log("setting _boo")
this._boo = val;
}
get foo() {
console.log("getting _foo");
return this._foo;
}
}
try {
let test = new Test('foooooo');
test.boo = "booooo";
console.log(`foo: ${test.foo}`);
test.foo = "bar";
} catch (e) {
console.log(e);
}
With "Setter" it's possible to control initializing properties.
You can see that it's possible to change the value of "boo" property but any attempt to change the value of the "foo" will throw an exception.
With "Getter" it's possible to control retrieving the value of properties.
You can see that it's possible to retrieve the value of "foo" property but not "boo" and its value is private.
PS:
Here are some examples to better understand JS behavior with objects and arrays:
//This works fine:
//Object
const obj = {};
obj.foo = 'bar';
console.log(obj); // {foo : 'bar'}
obj.foo = 'bar2';
console.log(obj); // {foo : 'bar2'}
//---------------------------------------
//Array:
const arr = [];
arr.push('foo');
console.log(arr); // ['foo']
arr.unshift("foo2");
console.log(arr); // ['foo2', 'foo']
arr.pop();
console.log(arr); // ['foo2']
//===========================================
//but these won't work:
const obj = {};
obj = {foo: 'bar'}; // error - re-assigning
const arr = ['foo'];
const arr = ['bar']; // error - re-declaring
const foo = 'bar';
foo = 'bar2'; // error - can not re-assign
var foo = 'bar3'; // error - already declared
function foo() {}; // error - already declared
New Example:
class A {
constructor(val) {
this._foo = val;
}
set foo(val) {
throw new Error("It's not possible to change the foo property!")
}
get foo() {
return this._foo;
}
}
class B {
constructor(val) {
this._obj = new A(val);
}
get Obj() {
return this._obj;
}
}
let b = new B('Test');
b.Obj.foo = 'new value';
console.log(b.Obj.foo);
In this manner, it's not possible to change the values ​​of the internal object.

Javascript Proxy set() local property on inherited objects

According to MDN,
handler.set() can trap Inherited property assignment:
Object.create(proxy)[foo] = bar;
In which case, how does one both monitor and allow local assignments on inherited objects?
var base = {
foo: function(){
return "foo";
}
}
var proxy = new Proxy(base, {
set: function(target, property, value, receiver){
console.log("called: " + property + " = " + value, "on", receiver);
//receiver[property] = value; //Infinite loop!?!?!?!?!
//target[property] = value // This is incorrect -> it will set the property on base.
/*
Fill in code here.
*/
return true;
}
})
var inherited = {}
Object.setPrototypeOf(inherited, Object.create(proxy));
inherited.bar = function(){
return "bar";
}
//Test cases
console.log(base.foo); //function foo
console.log(base.bar); //undefined
console.log(inherited.hasOwnProperty("bar")) //true
After some additional thought, i noticed that it intercepts 3 ops:
Property assignment: proxy[foo] = bar and proxy.foo = bar
Inherited property assignment: Object.create(proxy)[foo] = bar
Reflect.set()
but not Object.defineProperty() which appears to be even lower level than the = operator.
Thus the following works:
var base = {
foo: function(){
return "foo";
}
};
var proxy = new Proxy(base, {
set: function(target, property, value, receiver){
var p = Object.getPrototypeOf(receiver);
Object.defineProperty(receiver, property, { value: value }); // ***
return true;
}
});
var inherited = {};
Object.setPrototypeOf(inherited, Object.create(proxy));
inherited.bar = function(){
return "bar";
};
// Test cases
console.log(base.foo); // function foo
console.log(base.bar); // undefined
console.log(inherited.bar); // function bar
console.log(inherited.hasOwnProperty("bar")) // true
I see two options (maybe):
Store the property in a Map, keeping the Maps for various receivers in a WeakMap keyed by the receiver. Satisfy get by checking the Map and returning the mapping there instead of from the object. (Also has.) Slight problem is that you also need to proxy the receiver (not just base) in order to handle ownKeys. So this could be unworkable.
Temporarily get the proxy out of the inheritance chain while setting.
Here's that second one:
var base = {
foo: function(){
return "foo";
}
};
var proxy = new Proxy(base, {
set: function(target, property, value, receiver){
const p = Object.getPrototypeOf(receiver); // ***
Object.setPrototypeOf(receiver, null); // ***
receiver[property] = value; // ***
Object.setPrototypeOf(receiver, p); // ***
return true;
}
});
var inherited = {};
Object.setPrototypeOf(inherited, Object.create(proxy));
inherited.bar = function(){
return "bar";
};
// Test cases
console.log("base.foo:", base.foo); // function foo
console.log("base.bar:", base.bar); // undefined
console.log("inherited.bar:", inherited.bar); // function bar
console.log("inherited has own bar?", inherited.hasOwnProperty("bar")); // true

JS - Create object shorthand

Take the below object:
var value = 'bar';
var obj = { foo: value }
// -> Object { foo="bar" }
Supposing the key was also a variable, one could go:
var key = 'foo', value = 'bar';
var obj = {}
obj[key] = value;
// -> Object { foo="bar" }
Now, I want to do this in one line (shorthand). So I tried:
var obj = {}[key] = value; // or,
var obj = ({})[key] = value; // or,
var obj = new Object()[key] = value;
// -> "bar"
This oddly produces a String instead of an Object.
Is there any way to do this shorthand?
ECMAScript 6 will allow you to do
var obj = {
[key]: value
};
Browser support is not great yet, but transpilers such as 6to5 allow you to use this notation today!
You can, or almost can. If you put the creation of the object, and its assignment to obj in parentheses, you can set a property of the result of that expression:
var obj, key='foo', value = 'bar';
(obj = {})[key] = value;
alert(obj); // [object Object]
alert(obj.foo); // bar
The var keyword cannot be included in that expression within parentheses, so either you will have to declare obj before (like I did), or not declare it at all and accept that it will be in global scope.
Depends on what you call a one-liner, with some code golf you can do
var obj = (function(o) {o[key]=value; return o})({});
it think that's the shortest you'll get it ?
Something like that is coming in ECMAScript 6:
With ECMAScript 6, there is a shorter notation available to achieve
the same:
var a = "foo",
b = 42,
c = {};
// Shorthand property names (ES6)
var o = { a, b, c };
So you could create your object with the following code
var foo = 'bar';
var obj = {foo};
No shorthand in ECMAScript 5 (the current version), but you can make a method
function createObject(key, value /*, key, value, ... */ ) {
var object = {};
for (var i = 0; i < arguments.length; i = i + 2) {
object[arguments[i]] = arguments[i+1];
}
return object;
}
Then you can call it like this
var obj = createObject(key, value, 'anotherKey', 'anotherVal');

Issue when trying to inherit all classes referenced by name in a for loop

Well the title is a mouthful but I couldn't come up with a better one, ideas welcome.
Anyhow, I have a javascript object containing classes as properties. I want to create another object which is in every aspect equal to the first one by subclassing it. I'm gonna try to sum it up:
var L1 = {};
L1.Foo = function() {/*...*/};
L1.Bar = function() {/*...*/};
//...
L1.Baz = function() {/*...*/};
var L2 = {};
L2.Foo = function() { L1.Foo.call(this); /*possibily some other code here*/ };
L2.Foo.prototype = Object.create(L1.Foo.prototype);
L2.Foo.prototype.constructor = L2.Foo;
L2.Bar = function() { L1.Bar.call(this); /*possibily some other code here*/ };
L2.Bar.prototype = Object.create(L1.Bar.prototype);
L2.Bar.prototype.constructor = L2.Bar;
//...
L2.Baz = function() { L1.Baz.call(this); /*possibily some other code here*/ };
L2.Baz.prototype = Object.create(L1.Baz.prototype);
L2.Baz.prototype.constructor = L2.Baz;
var foo = new L2.Foo();
console.log(foo); //L2.Foo
var bar = new L2.Bar();
console.log(bar); //L2.Bar
var baz = new L2.Baz();
console.log(baz); //L2.Baz
First, working version.
I told myself: "huh, looks like there a pattern here" so I went and modified my code as follows:
//first 10 lines unaltered
for(prop in L1) {
L2[prop] = function() { L1[prop].call(this); /*Call super method by default,
unless overriden below*/ };
L2[prop].prototype = Object.create(L1[prop].prototype);
L2[prop].prototype.constructor = L2[prop];
}
//Here I decide that I want to override only the constructor
//for Foo, so naturally:
L2.Foo.prototype.constructor = function() {
L1.Foo.call(this);
this.someOtherProperty = "foo";
};
var foo = new L2.Foo();
console.log(foo); //L2.(anonymous function)?
console.log(foo.someOtherProperty); //undefined?
var bar = new L2.Bar();
console.log(bar); //L2.(anonymous function)?
var baz = new L2.Baz();
console.log(baz); //L2.(anonymous function)?
Second, not-so-working version.
What I am getting wrong?
"huh, looks like there a pattern here" so I went and modified my code
as follows:
for(prop in L1) {
L2[prop] = function() { L1[prop].call(this);
You've hit the common closure in a loop problem - all your L2 functions are actually calling L1.Baz on their new instance as prop will have the value "Baz". See the linked question for how to fix this.
Also, notice that none of your constructors does pass its arguments to the super call, which might bite you as well.
Here I decide that I want to override only the constructor for Foo, so
naturally:
L2.Foo.prototype.constructor = function() {
L1.Foo.call(this);
this.someOtherProperty = "foo";
};
What I am getting wrong?
Overwriting the .constructor property on a prototype object does nothing. Your code is still invoking new L2.Foo, not new L2.Foo.prototype.constructor. You might want to have a look at how the new keyword works.
Instead, you really need to replace L2.Foo. This can be done with this pattern:
L2.Foo = (function (original) {
function Foo() {
original.apply(this, arguments); // apply old L2.Foo constructor
this.someOtherProperty = "foo"; // set property
}
Foo.prototype = original.prototype; // reset prototype
Foo.prototype.constructor = Foo; // fix constructor property
return Foo;
})(L2.Foo);
(or you just put your standard pattern from the first version). If this does get too repetitive, you might also do the .prototype and .constructor setup programmatically:
// whole code
var L2 = {
Foo: function() {
L1.Foo.call(this);
this.someOtherProperty = "foo";
}
// … other overwritten constructors
};
for (var prop in L1) {
if (!L2[prop]) // unless overridden above, create default that only…
(function(parent) {
L2[prop] = function() {
parent.apply(this, arguments); // calls super
};
}(L1[prop]));
L2[prop].prototype = Object.create(L1[prop].prototype);
L2[prop].prototype.constructor = L2[prop];
}
This might just be me, but if the subclass needs to be the same in every aspect as the superclass, why bother making a subclass? I'm not entirely clear an what you are trying to achieve from the code.
But just give the class a property of the class itself
e.g. (in plain java)
public class TestInheritClass {
private TestInheritClass tic;
public TestInheritClass getTic() {
return tic;
}
public void setTic(TestInheritClass tic) {
this.tic = tic;
}
}

JavaScript: How to pass object by value?

When passing objects as parameters, JavaScript passes them by reference and makes it hard to create local copies of the objects.
var o = {};
(function(x){
var obj = x;
obj.foo = 'foo';
obj.bar = 'bar';
})(o)
o will have .foo and .bar.
It's possible to get around this by cloning; simple example:
var o = {};
function Clone(x) {
for(p in x)
this[p] = (typeof(x[p]) == 'object')? new Clone(x[p]) : x[p];
}
(function(x){
var obj = new Clone(x);
obj.foo = 'foo';
obj.bar = 'bar';
})(o)
o will not have .foo or .bar.
Question
Is there a better way to pass objects by value, other than creating a local copy/clone?
Not really.
Depending on what you actually need, one possibility may be to set o as the prototype of a new object.
var o = {};
(function(x){
var obj = Object.create( x );
obj.foo = 'foo';
obj.bar = 'bar';
})(o);
alert( o.foo ); // undefined
So any properties you add to obj will be not be added to o. Any properties added to obj with the same property name as a property in o will shadow the o property.
Of course, any properties added to o will be available from obj if they're not shadowed, and all objects that have o in the prototype chain will see the same updates to o.
Also, if obj has a property that references another object, like an Array, you'll need to be sure to shadow that object before adding members to the object, otherwise, those members will be added to obj, and will be shared among all objects that have obj in the prototype chain.
var o = {
baz: []
};
(function(x){
var obj = Object.create( x );
obj.baz.push( 'new value' );
})(o);
alert( o.baz[0] ); // 'new_value'
Here you can see that because you didn't shadow the Array at baz on o with a baz property on obj, the o.baz Array gets modified.
So instead, you'd need to shadow it first:
var o = {
baz: []
};
(function(x){
var obj = Object.create( x );
obj.baz = [];
obj.baz.push( 'new value' );
})(o);
alert( o.baz[0] ); // undefined
Check out this answer https://stackoverflow.com/a/5344074/746491 .
In short, JSON.parse(JSON.stringify(obj)) is a fast way to copy your objects, if your objects can be serialized to json.
Here is clone function that will perform deep copy of the object:
function clone(obj){
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
Now you can you use like this:
(function(x){
var obj = clone(x);
obj.foo = 'foo';
obj.bar = 'bar';
})(o)
Use Object.assign()
Example:
var a = {some: object};
var b = new Object;
Object.assign(b, a);
// b now equals a, but not by association.
A cleaner example that does the same thing:
var a = {some: object};
var b = Object.assign({}, a);
// Once again, b now equals a.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Use this
x = Object.create(x1);
x and x1 will be two different object,change in x will not change x1
You're a little confused about how objects work in JavaScript. The object's reference is the value of the variable. There is no unserialized value. When you create an object, its structure is stored in memory and the variable it was assigned to holds a reference to that structure.
Even if what you're asking was provided in some sort of easy, native language construct it would still technically be cloning.
JavaScript is really just pass-by-value... it's just that the value passed might be a reference to something.
ES6
Using the spread operator like obj2 = { ...obj1 }
Will have same values but different references
ES5
Use Object.assign obj2 = Object.assign({}, obj1)
Javascript always passes by value. In this case it's passing a copy of the reference o into the anonymous function. The code is using a copy of the reference but it's mutating the single object. There is no way to make javascript pass by anything other than value.
In this case what you want is to pass a copy of the underlying object. Cloning the object is the only recourse. Your clone method needs a bit of an update though
function ShallowCopy(o) {
var copy = Object.create(o);
for (prop in o) {
if (o.hasOwnProperty(prop)) {
copy[prop] = o[prop];
}
}
return copy;
}
As a consideration to jQuery users, there is also a way to do this in a simple way using the framework. Just another way jQuery makes our lives a little easier.
var oShallowCopy = jQuery.extend({}, o);
var oDeepCopy = jQuery.extend(true, {}, o);
references :
http://api.jquery.com/jquery.extend/
https://stackoverflow.com/a/122704/1257652
and to dig into the source.. http://james.padolsey.com/jquery/#v=1.8.3&fn=jQuery.extend
Actually, Javascript is always pass by value. But because object references are values, objects will behave like they are passed by reference.
So in order to walk around this, stringify the object and parse it back, both using JSON. See example of code below:
var person = { Name: 'John', Age: '21', Gender: 'Male' };
var holder = JSON.stringify(person);
// value of holder is "{"Name":"John","Age":"21","Gender":"Male"}"
// note that holder is a new string object
var person_copy = JSON.parse(holder);
// value of person_copy is { Name: 'John', Age: '21', Gender: 'Male' };
// person and person_copy now have the same properties and data
// but are referencing two different objects
I needed to copy an object by value (not reference) and I found this page helpful:
What is the most efficient way to deep clone an object in JavaScript?. In particular, cloning an object with the following code by John Resig:
//Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);
With the ES6 syntax:
let obj = Object.assign({}, o);
When you boil down to it, it's just a fancy overly-complicated proxy, but maybe Catch-All Proxies could do it?
var o = {
a: 'a',
b: 'b',
func: function() { return 'func'; }
};
var proxy = Proxy.create(handlerMaker(o), o);
(function(x){
var obj = x;
console.log(x.a);
console.log(x.b);
obj.foo = 'foo';
obj.bar = 'bar';
})(proxy);
console.log(o.foo);
function handlerMaker(obj) {
return {
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
// a trapping proxy's properties must always be configurable
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
getPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name); // not in ES5
// a trapping proxy's properties must always be configurable
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(obj);
},
getPropertyNames: function() {
return Object.getPropertyNames(obj); // not in ES5
},
defineProperty: function(name, desc) {
},
delete: function(name) { return delete obj[name]; },
fix: function() {}
};
}
If you are using lodash or npm, use lodash's merge function to deep copy all of the object's properties to a new empty object like so:
var objectCopy = lodash.merge({}, originalObject);
https://lodash.com/docs#merge
https://www.npmjs.com/package/lodash.merge

Categories

Resources