Is it possible to listen to property changes without the use of Proxy and setInterval?
For common objects you could use the function below but that works for all existing properties but doesn't work for any properties that might get added after the wrapping.
function wrap(obj) {
var target = {};
Object.keys(obj).forEach(function(key) {
target[key] = obj[key];
Object.defineProperty(obj, key, {
get: function() {
console.log("Get");
return target[key];
},
set: function(newValue) {
console.log("Set");
target[key] = newValue;
}
});
});
}
var obj = {
a: 2,
b: 3
};
wrap(obj);
obj.a; // Get
obj.a = 2; // Set
obj.b; // Get
obj.b = 2; // Set
obj.c = 2; // Nothing
obj.c; // Nothing
If the object is an array you could also listen to the length property and reset all the get and set functions when it's changed. This is obviously not very efficient as it changes the properties of each element whenever an element is added or removed.
So I don't think that Object.defineProperty is the answer.
The reason I don't want to use setInterval is because having big intervals will make the wrapping unreliable whereas having small intervals will have a big impact on the efficiency.
Sadly no, that's why Proxies were such a big thing. There is no other way, for now, to trigger code when a property is added to an object than Proxy.
As you say, you can use Object.defineProperty or var a = { get x() {...}, set x(value) {...} } but not detect new properties.
Most frameworks rely on dirty-check: comparing objects on a giving timing. The timing is where the difference mainly is.
AngularJS (Angular 1.x) gave you special functions for asynchronous operations like $timeout and $http and it's own way to listen to DOM events that will wrap your callbacks and run the check after your code.
Angular (Angular 2 to N) uses Zone.js to create a "Running context" for your code, any asynchronous callback is intercepted by Zone.js. It's basically the same solution as for AngularJS but works automagically.
React does something similar but instead of tracking your variables it runs the renderer and compares if the generated DOM (Virtual DOM) is different.
Related
Originally my objective is when any property of the instances of class OnlyOneProp is set,
i.e. obj.what_ever_property = "value",
at last it will only modify obj.the_only_prop.
Behavior is like this:
var obj = new OnlyOneProp();
obj.what_ever_property = "value";
console.log(obj.only_property, obj.what_ever_property);
// expected output:
// >value undefined
Original Question: Is there a way to implement above behaviour?
edit:
With respect to the solution in the answer,
here are some follow up questions:
1) Is there any major flaw to the above code? (Like I had once mixed up receiver and target, which may cause infinite looping on the setter)
2) Would Proxy hinder the performance a lot?
3) Is there any way to bypass the above proxy setter? (Like defineProperty()or so)
4) It can also be an implementation of ReadOnlyObject (after removing the setting line in setter), but would there be a better implementation? (It's a bit out of topic but I also want to know, because I really want to remove the top Proxy which is just overriding the constructor)
If you return an object from the constructor, the new operator returns that object, not the new object it passed to the constructor as this. So a more straight forward version of OnePropertyClass might be
class OnePropertyClass{
constructor( value) {
var self = this;
return new Proxy( this, {
set: function(target, property, value) {
self["only_property"] = value;
return true;
}
}
);
}
}
This can be simplified by using an arrow function instead of the closure:
class OnePropertyClass{
constructor() {
return new Proxy( this, {
set: (target, property, value) => {
this.only_property = value;
return true;
}
}
);
}
}
var obj = new OnePropertyClass();
obj.what_ever_property = "value";
console.log(obj.only_property, obj.what_ever_property);
It doesn't set up any setter loops because the setter stores the value on the actual this object of the constructor, not on the proxy object returned.
Instances of this version of OnePropertyClass inherit per usual - the constructor property returns the OnePropertyClass constructor function, and Object.prototype properties and methods are still inherited.
You may wish to freeze OnePropertyClass.prototype to prevent additions of any other inherited properties. You may also wish to provide trap functions for defineProperty and possibly setPrototype to prevent run time property additions - see MDN handler object methods for details.
Proxy implementation is probably written in C++ and I would expect most of the additional overheads will lie in calling the setter function.
I have not tested this version for extensibility and did not use the target parameter of the set handler Please experiment before use :-)
After digging up from MDN Proxy and inspiration from dynamic setter/getter,
I've come up with the code below:
var OnlyOneProp = new Proxy(
// target
class{// normal class definition
constructor(){
// console.log("anonymous constructor");
}
}, {
construct(target, args, caller){
// if(!new.target){}
// console.log("proxy construct");
return new Proxy(new target(), {
set(target, name, value, receiver){
target.only_property = value;
return true; // must have according to some specification
}
});
},
});
var obj = new OnlyOneProp();
obj.what_ever_property = "value";
console.log(obj.only_property, obj.what_ever_property);
// output: value undefined
It's fully functioning but as you may see there are two new Proxy() instantiation (although the first one only execute once), which I want to remove if possible.
I am currently writing a tool that monitors changes made to an object using a Proxy.
So I have a function watchObject that takes an object as the argument and wraps it inside a Proxy where the handlers corresponding to changes in the object call debugger;. This watchObject is mostly based on the accepted answer of this question.
Setting traps on get, defineProperty and deleteProperty handlers works quite well when the object is modified only.
However when the reference is replaced the handlers are not called and the Proxy wrapping around it is lost.
Lets consider an Object a containing a key foo:
var a = { foo: "bar"};
For example the following will invoke a debugger breakpoint that are inside my traps:
a.foo = "Hello"
delete a.foo
a.baz = "Hi"
ect...
But calling this afterward: a = {keyOne: "one"} will not trigger the breakpoint and will subsequent calls to above examples (that otherwise would trigger the breakpoint) will not invoke a breakpoint anymore.
So I would like to know if there is a way to detect an operation like: a = {keyOne: "one"} is done so as to monitor reference changes for a variable and be able to recreate the proxy object on the new referenced object.
Also, as the whole process of monitoring changes on an object is aimed to make debugging easier, the solution has to be non destructive on the code that is involved.
Using a Proxy is great as it only intercepts and doesn't change overall behaviour of the object wrapped.
I want to give this a shot...
It looks like you want to catch the object itself instead of the method.
Since the object's variable will be set as a property of the window or another object we can use a function to define a getter and setter on the window (or that object) with the desired variable name:
function trackedProxy(name, val, _this){
let handler = {} // place your property traps here
let _privateObject = val
let _privateProxy = new Proxy(_privateObject, handler)
Object.defineProperty(_this, name, {
get: function() {
return _privateProxy;
},
set: function(value) {
console.log("object changed")
// Do something
return _privateObject = value;
}
});
}
//build one with invocation of the function, but do not set as equal to a var or you will have the same issue.
//**bad! - var a = trackedProxy('a',{keyOne: "one"},this)
trackedProxy('a',{ foo: "bar"}, this)
console.log(a)
//Proxy{ foo: "bar"}
a={keyOne: "one"}
//Object changed
console.log(a)
//Proxy{keyOne: "one"}
Keep in mind that you cannot redefine the property on the window after you do this.
I hope this helps :)
My Query:
What is the most efficient way to reference nested JavaScript objects? I have found a couple so far, and am wondering which is the most efficient, and if there are any others which are better than those which I list here. The problem involves usage of a root object, similarly to that which many JavaScript frameworks use. The root object is compulsory. Please do not post answers which do not use a root object.
Note that this is specifically intended for root/sub-root objects which have long names - referencing a.b would be useless, whilst referencing abcdefghijklm.nopqrstuvwxyz would be extremely useful.
EDIT:
Just to clarify a couple of things:
Via efficiency I mean firstly code length (which could in turn affect performance, but that is slightly less important). I am concerned about code length partially because of file size - yes, I would be minifying it afterwards - but also due to readability for anyone viewing my original code (including myself). Performance is also slightly important, I don't want long workarounds which would slow down execution.
The referencing would be mostly for commonly used sub-objects, NOT EVERY SINGLE ONE. I state (in method 1) that many global variables would need to be created - due to the fact that I was assuming that all of the first-level sub-objects would be commonly used.
Objects to be referenced:
The object foo contains the various sub-objects bar, baz and qux, each of which contain various other objects.
var foo = {
bar: {
bar: {},
baz: {},
qux: {}
},
baz: {
bar: {},
baz: {},
qux: {}
},
qux: {
bar: {},
baz: {},
qux: {}
}
};
Method 1 - Reference Variables:
The first method involves creating a variable for each of the sub-objects.
var bar = foo.bar;
var baz = foo.baz;
var qux = foo.quz;
foo.bar.baz could then be referenced simply via bar.baz, and foo.qux.qux via qux.qux.
Advantages:
Reference variables can be very concise - a single character is the minimum possible length.
Disadvantages:
If there are many sub-objects to be referenced, this would increase the global variable count. This is likely to cause conflicts, cross-script and otherwise - especially using single-character variable names.
A new reference variable needs to be created for every nested object. Not only is this inconvenient, but it also requires the programmer to remember to create each variable after amending the object. Conversely, if a sub-object is removed and the programmer forgets to remove the reference variable also, then the code becomes messy with useless global variables cluttering it up.
Method 2 - Reference Function:
The second method involves creating a function which returns a sub-object, depending on a couple of parameters.
function ref(a, b) {
//Ensure that the first parameter is passed:
if (!a) {
return false;
}
return foo.a.b;
};
foo.bar.baz could then be referenced via ref("bar", "baz"), and foo.qux,qux via ref("qux", "qux").
Advantages:
Works for all first-level sub-objects, without repetitive and messy separate variable defining.
Disadvantages:
Only really useful for shortening the root object - if the root object is named a then using the reference function would actually lengthen the code.
Let's say we have a graphic control object, that is an instance of a GraphicObject Class (function).
Let's say one instance looks like :
grControl = {
visual : {
boundingBox : { x : , y : , width: , height : ... },
...
},
action : {
...
}
};
The right way to create shortcuts is either :
• to cache locally the appropriate object in a var in a function that will use it :
function drawBBox (someGraphicControl) {
var bbox = someGraphicControl.visual.boundingBox;
// maybe you want also to cache sub-properties :
var width = bbox.width;
...
}
• to define, on the prototype, a getter and/or a setter that will provide an access to the nested property :
// read-only example
Object.defineProperty(GraphicControl.prototype, 'width',
{ get : function() {
return this.visual.boundingBox.width },
enumerable : true};
// read-write example
Object.defineProperty(GraphicControl.prototype, 'width',
{ get : function() {
return this.visual.boundingBox.width },
set : function(val) {
this.visual.boundingBox.width = val ; },
},
enumerable : true};
this way you can use :
grObject.width
and exactly refer to :
grObject.visual.boundingBox.width
The usual practice in javascript depends upon the circumstances:
1) One-time access to that object in a particular function. If you're just making a one time access to a deeply nested reference, you just spell out the reference with all the intervening names:
var data = foo.baz.quz.prop1;
2) Multiple-accesses to a particular nested object. If there are multiple references to a particular deeply nested object, then you create a temporary local variable that points to the common object and then reference from that within the local function.
function getDeepData() {
var obj = foo.baz.quz;
var prop1 = obj.prop1;
var prop2 = obj.prop2;
var prop2 = obj.prop3;
// code that uses prop1, prop2 and prop3 here
}
3) Iterating properties who's names are not known in advance. Since you don't know the property names in advance, this is done by iterating one level at a time (sometimes recursively) and just keeping the parent object in a local variable.
If you find yourself making lots of deeply nested one-time references all over your code, then you probably want to revisit the structure of your code or data because that can usually be avoided.
I receive a bunch of objects via JSON which ultimately need to have some instance member functions.
Is there a way to do this without copying the data?
For example:
var DataObject = function() {};
DataObject.prototype.add = function() { return this.a + this.b; };
var obj = JSON.parse('{"a":1, "b":2}');
// Do something to obj to make it inherit from DataObject
console.assert( obj.add() === 3 );
I've tried setting obj.prototype = DataObject.prototype but that doesn't seem to work. What am I missing?
Well, in ECMAScript6 (in IE11, and every other non ie browser today), that would be __proto__
obj.__proto__ = Object.create(DataObject.prototype);
[fiddle]
Generally, make sure you only do this at the object creation case, otherwise it can be very risky to do.
Also note, setting the protoype explicitly is not always faster than copying two properties, as you can see here so you have to be sure there is actual gain here.
I'm making a class that will be recreated many times, and in order to save memory I need to thoroughly delete it. Basically I need to access its containing variable if possible.
Here's the example:
function example(){
this.id=0;
this.action=function(){alert('tost');}
this.close=function(){ delete this;}
}
var foo=new example();
My question is:
How can I get access to the foo variable from within the example function so I can remove it?
window.foo will access that global variable.
this.close=function(){ delete window.foo; }
However, I remember there is something fishy with global variables, delete and window, so you might want to do otherwise, and simply use window.foo = null; for example.
If you want to access a variable defined in another function, you'll want to read the answers to this SO question.
Since what you want is to allow the garbage collector to release that object, you need to ensure that there are no references left to the object. This can be quite tricky (i.e. impossible) because the code manipulating the object can make multiple references to it, through global and local variables, and attributes.
You could prevent direct reference to the object by creating a proxy to access it, unfortunately javascript doesn't support dynamic getters and setters (also called catch-alls) very well (on some browseres you might achieve it though, see this SO question), so you can't easily redirect all field and method (which are just fields anyway) accesses to the underlying object, especially if the underlying object has many fields added to it and removed from it dynamically (i.e. this.anewfield = anewvalue).
Here is a smiple proxy (code on jsfiddle.net):
function heavyobject(destroyself, param1, param2) {
this.id=0;
this.action=function(){alert('tost ' + param1 + "," + param2);};
this.close=function(){ destroyself(); }
}
function proxy(param1, param2) {
object = null;
// overwrites object, the only reference to
// the heavyobject, with a null value.
destroyer = function() { object = null; };
object = new heavyobject(destroyer, param1, param2);
return function(fieldname, setvalue) {
if (object != null) {
if (arguments.length == 1)
return object[fieldname];
else
object[fieldname] = setvalue;
}
};
}
var foo = proxy('a', 'b');
alert(foo("action")); // get field action
foo("afield", "avalue"); // set field afield to value avalue.
foo("action")(); // call field action
foo("close")(); // call field close
alert(foo("action")); // get field action (should be 'undefined').
It works by returning a function that when called with a single argument, gets a field on the wrapped object, and when called with two arguments sets a field. It works by making sure that the only reference to the heavyobject is the object local variable in the proxy function.
The code in heavyobject must never leak this (never return it, never return a function holding a reference to var that = this, never store it into a field of another variable), otherwise some external references may be created that would point to the heavyobject, preventing its deletion.
If heavyobject's constructor calls destroyself() from within the constructor (or from a function called by the constructor), it won't have any effect.
Another simpler proxy, that will give you an empty object on which you can add fields, read fields, and call methods. I'm pretty sure that with this one, no external reference can escape.
Code (also on jsfiddle.net):
function uniquelyReferencedObject() {
object = {};
f = function(field, value) {
if (object != null) {
if (arguments.length == 0)
object = null;
else if (arguments.length == 1)
return object[field];
else
object[field] = value;
}
};
f.destroy = function() { f(); }
f.getField = function(field) { return f(field); }
f.setField = function(field, value) { f(field, value); }
return f;
}
// Using function calls
o = uniquelyReferencedObject();
o("afield", "avalue");
alert(o("afield")); // "avalue"
o(); // destroy
alert(o("afield")); // undefined
// Using destroy, getField, setField
other = uniquelyReferencedObject();
other.setField("afield", "avalue");
alert(other.getField("afield")); // "avalue"
other.destroy();
alert(other.getField("afield")); // undefined
The truth is that you can not delete objects in Javascript.
Then you use delete operator, it accepts the property of some object only.
So, when you use delete, in general you must pass to it something like obj.p. Then you pass just a variable name actually this means 'property of global object', and delete p is the same as delete window.p. Not sure what happens internally on delete this but as a result browser just skip it.
Now, what we actually deleting with delete? We deleting a reference to object. It means object itself is still somethere in memory. To eliminate it, you must delete all references to concrete object. Everythere - from other objects, from closures, from event handlers, linked data, all of them. But object itself doest have information about all this references to it, so there is no way to delete object from object itself.
Look at this code:
var obj = <our object>;
var someAnother = {
...
myObjRef: obj
...
}
var someAnotherAnother = {
...
secondRef : obj
...
}
To eliminate obj from memory you must delete someAnother.myObjRef and someAnoterAnother.secondRef. You can do it only from the part of programm which knows about all of them.
And how we delete something at all if we can have any number of references everythere? There are some ways to solve this problem:
Make only one point in program from there this object will be referenced. In fact - there will be only one reference in our program. and Then we delete it - object will be killed by garbage collector. This is the 'proxy' way described above. This has its disadvantages (no support from language itself yet, and necessarity to change cool and nice obj.x=1 to obj.val('x',1). Also, and this is less obvious, in fact you change all references to obj to references to proxy. And proxy will always remain in memory instead of object. Depending on object size, number of objects and implementation this can give you some profit or not. Or even make things worse. For example if size of your object is near size of proxy itself - you will get no worth.
add to every place there you use an object a code which will delete reference to this object. It is more clear and simple to use, because if you call a obj.close() at some place - you already knows everything what you need to delete it. Just instead of obj.close() kill the refernce to it. In general - change this reference to something another:
var x = new obj; //now our object is created and referenced
x = null;// now our object **obj** still im memory
//but doest have a references to it
//and after some milliseconds obj is killed by GC...
//also you can do delete for properties
delete x.y; //where x an object and x.y = obj
but with this approach you must remember that references can be in very hard to understand places. For example:
function func() {
var x= new obj;// our heavy object
...
return function result() {
...some cool stuff..
}
}
the reference is stored in closure for result function and obj will remain in memory while you have a reference to result somethere.
It hard to imagine object that is heavy itself, most realistic scenario - what you have some data inside it. In this case you can add a cleanup function to object which will cleans this data. Let say you have an gigant buffer (array of numbers for example) as a property of the object, and if you want to free memory - you can just clear this buffer still having object in memory as a couple dozens of bytes. And remember to put your functions to prototype to keep instances small.
Here is a link to some very detailed information on the JavaScript delete operator.
http://perfectionkills.com/understanding-delete/