Javascript : How to detect when the reference of a variable changes - javascript

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 :)

Related

Proxy substitue for ES5

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.

Freeze existing properties of object, but allow object to be extensible

So I have been looking at Object.freeze() and Object.seal().
Object.freeze() - will make all existing properties non-writable, and will not allow any new properties to be added.
Object.seal() - "Sealing an object prevents new properties from being added and marks all existing properties as non-configurable."
I am looking for a way to make all existing properties "frozen" (non-writable), but allow new properties to be added.
Is there shorthand for doing that?
The manually way of doing what I want is:
let freezeExistingProps = obj => {
Object.keys(obj).forEach(k => {
Object.defineProperty(obj, k, {
writable: false
});
});
};
The above function works surprisingly well to freeze existing top-level properties on an object (it doesn't overwrite them, just changes them to non-writable), but I am hoping there might be a more official/quicker way to do the above.
You might do the following:
instance -> frozen static proto -> dynamic proto
Some sample:
function freeze(stat,dyn){
Object.setPrototypeOf(stat,dyn);
Object.freeze(stat);
}
var a={unchangeable:1};
var b={changeable:2}
freeze(a,b);
Now have a look at a and change some b props.
Well, if you want to do it in the manner of freeze, then freezing it immediately, and setting up to a prototype of another object might help, but it will return a copy (pointing to the original object as prototype), exactly in the form how you want. there are obviously some pros and cons, as the properties will not be the immediate properties, but we can find it out by its __proto__ if we need all the keys (assuming you have a dedicated use case)
So, just another try
function freezeExistingProps (obj){
var OBJECT = function(){};
Object.freeze(obj)
OBJECT.prototype = obj;
return new OBJECT();
}
You may want to consider cloning your object into a new one with extra attribute. It's also a very good practice (look for immutability).
An example:
const setAge = (person, age) => ({ ...person, age });
const person = {
firstName: 'Luke',
lastName: 'Skywalker',
};
const personWithAge = setAge(person, 24);

Perform omit on the original object

I am very new to underscore js, I am trying to omit a certain property on an Object. What I did was
myObj = _.omit(myObj,name)
console.log(myObj);
Still the myObj seems to have the property name. Although if I do this it seemes to work
newMyObj= _.omit(myObj,name)
console.log (newMyObj)
it seemed to work fine. What am I doing wrong, can someone help? Ok, so myObj looks like this
Angola: "4.134137685",Brunei: "2.532726835",Countries: "2004",Croatia: "1.717672961", keys: Array[11]
I am trying to omit "keys" which again is an array of objects
Thanks
There are these things called "debuggers". If you don't know what they are, then stop everything you're doing and learn about them now. Search Google for "Chrome devtools", for instance. Stop your code (put a breakpoint) at the point before the call to _.omit. In the console, type in myObj to see exactly what it contains, then also name. Or, you could use the 'Scope Variables" section of devtools to check the value of these variables. Now, make a single step (F10). See if or how the variables have changed, or type myObj again into the console to check its value.
In your particular case, you report that the deletion of the property occurs properly when you do
newMyObj= _.omit(myObj,name)
but not with
myObj= _.omit(myObj,name)
In and of itself, that behavior is completely unexplainable. So there's something else going on that you're not telling us about. My guess is that you are doing something like this:
myObj = { keys: [] };
name = "keys";
delete_property();
console.log(myObj.keys); // []
function delete_property(myObj) {
myObj = _.omit (myObj, name);
}
However, this does not do what you might think. The assignment to myObj within the function does nothing; it just reassigns the value of the function argument. It has no effect on the myObj outside the function.
To be sure, we'd need to see more of your actual code, but this is just a regular old debugging problem of the sort you will encounter thousands of times in your programming career, so you're better off learning to solve it yourself.
I interpreted this question to mean you simply want to remove a property from an object using omit(). Note that omit() returns a copy of the object sans the specified property to remove. The method does not alter the object in place.
Given this premise, the code below, which matches what you have, works just fine:
var copy,
obj = {
Angola: "4.134137685",
Brunei: "2.532726835",
Countries: "2004",
Croatia: "1.717672961",
keys: Array[11]
},
check = function (o) {
_.each(o, function (value, key) {
console.log("key: " + key + " value: " + value);
});
};
copy = _.omit(obj, "keys");
check(copy);
obj = _.omit(obj, "keys");
check(obj);
You will get the same result whether you are using a new variable or the existing one.

Removing a class variable from within the class function

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/

How to delete an object in Javascript crossbrowser

var obj = {
destroy: function(){this = null;}
};
obj.destroy();
This works in Chrome, however firefox is throwing an error referencing this for some reason. Is there a better way to kill this object within a method?
Error:
invalid assignment left-hand side
[Break On This Error] destroy: function(){this = null;}
Not sure why Chrome allows for it but you can't assign a value to this. You can reference this, but you can't assign a value to it.
If you have some array destruction you want to perform you can reference this.myArrayName within your destroy method and free up whatever you're trying to release, but you can't just assign null to this to destroy an instance.
I suppose you could try something like this:
var foo = {
// will nullify all properties/methods of foo on dispose
dispose: function () { for (var key in this) this[key] = null; }
}
foo.dispose();
Pretty much as close as you can get to legally nullifying "this"...
Happy coding.
B
Call me old fashion, but:
foo = null;
I'm not sure why you're making this difficult. Javascript is a garbage collected language. All you have to do to allow something to be freed is to make sure there are no more references to it anywhere.
So, if you start with:
var obj = {
data: "foo";
};
and now you want to get rid or "free" that object, all you have to do is clear the reference to it with:
obj = null;
Since there are no longer any references in your code to that data structure that you originally defined and assigned to obj, the garbage collector will free it.
An object cannot destroy itself (because other things may have references to it). You allow it to be freed by removing all references to it. An object can clear out it's own references to other things, though that is generally not required as removing all references to the object itself will also take care of the references it holds (with the exception of some bugs with circular references between JS and the DOM in certain older browsers - particular IE).
One time when you might explicitly "delete" something is if you have a property on an object that you wish to remove. So, if you have:
var obj = {
data: "foo",
count: 4
};
And you wish to remove the "data" property, you can do that with this:
delete obj.data;
of if the property/key was assigned programmatically via a variable like this:
var key = "xxx";
obj[key] = "foo";
you can remove that key with:
delete obj[key];

Categories

Resources