This is front-end only, and not back-end. I also acknowledge that this is a bad idea. At this point I'm just curious.
I have a table of records. I would like the user to be able to enter a JavaScript conditional statement, which is then applied to the table to filter the records.
For example, to filter out records with a name that's less than 6 characters, I might enter:
record.name.length < 6
Without using an external library, the easiest way I've found to do this is with eval. However, in using eval, I of course introduce the risk of the user breaking the code (not a huge concern since this is front-end only, but still a user experience issue).
I would like to sanitize the user input so that it cannot change any values. So far, I believe I only need to do these two things to make eval "safe":
Turn any single equals signs = into double or triple equals signs
Remove or escape parentheses ( )
With these two items taken care of, is there anything else I need to do to prevent the user input from changing values?
One way of doing this which is safer than eval is using the Function constructor. As far as I know, this answer is totally safe, but it's quite possible there's some caveat I don't know or have forgotten, so everyone feel free to reply if I'm wrong.
The Function constructor allows you to construct a function from its string and a list of argument names. For example, the function
function(x, y) {
return x + y;
}
could be written as
new Function('x', 'y', 'return x + y;')
or simply
Function('x', 'y', 'return x + y;')
Note that although the function body has access to variables declared in the function definition, it cannot access variables from the local scope where the Function constructor was called; in this respect it is safer than eval.
The exception is global variables; these are accessible to the function body. Perhaps you want some of them to be accessible; for many of them, you probably don't. However, there is a way round this: declare the names of globals as arguments to the function, then call the function overriding them with fake values. For example, note that this expression returns the global Object:
(function() { return Object; })()
but this one returns 'not Object':
(function(Object) { return Object; })('not Object')
So, to create a function which does not have access to any of the globals, all you have to do is call the Function constructor on the javascript string, with arguments named after all the globals, then call the function with some innocuous value for all the globals.
Of course, there are variables (such as record) which you do want the javascript code to be able to access. The argument-name arguments to Function can be used for this too. I'll assume you have an object called myArguments which contains them, for example:
var myArguments = {
record: record
};
(Incidentally, don't call it arguments because that's a reserved word.) Now we need the list of names of arguments to the function. There are two kinds: arguments from myArguments, and globals we want to overwrite. Conveniently, in client-side javascript, all global variables are properties in a single object, window. I believe it's sufficient to use its own properties, without prototype properties.
var myArgumentNames = Object.keys(myArguments);
var globalNames = Object.keys(window);
var allArgumentNames = myArgumentNames.concat(globalNames);
Next we want the values of the arguments:
var myArgumentValues = myArgumentNames.map(function(key) {
return myArguments[key];
};
We don't need to do the values part for the globals; if we don't they'll just all be set to undefined. (Oh, and don't do Object.keys(myArguments).map(...), because there's a (small) chance that the array will come out in the wrong order, because Object.keys doesn't make any guarantees about the order of its return value. You have to use the same array, myArgumentNames.) Then call the Function constructor. Because of the large number of arguments to Function it's not practical to list them all explicitly, but we can get round this using the apply method on functions:
var myFn = Function.apply(null, allArgumentNames.concat([jsString]))
and now we just call this function with the argument list we've generated, again using the apply method. For this part, bear in mind that the jsString may contain references to this; we want to make sure this doesn't help the user to do something malicious. The value of this inside the script is the first argument to apply. Actually that's not quite true - if jsString doesn't use strict mode, then trying to set this to undefined or null will fail, and this will be the global object. You can get round this by forcing the script into strict mode (using '"use strict";\n' + jsString), or alternatively just set this to an empty object. Like this:
myFn.apply({}, myArgumentValues)
I am sharing my implementation (based on #David's answer).
Some of the keys of the Window object might break the Function.apply. This is why I've filtered the ones that break. Explanations in the code below as a comment.
// Why is windowKeys not inside function scope? No need. It won't
// be changing on each call. Creating array with +270 items for each eval
// might effect performance.
const windowKeys = Object.keys(window).filter((key) => {
// Why is window filtered?
// There are some cases that parameters given here might break the Function.apply.
// Eg. window keys as numbers: '0', (if there is iframe in the page)
// the ones that starts with numbers '0asdf',
// the ones that has dash and special characters etc.
try {
Function.apply(null, [key, "return;"]);
return true;
} catch (e) {
return false;
}
});
/**
* evaluates
* #param {string} code
* #param {object} context
* #returns
*/
const safeEval = (code, context) => {
const keys = Object.keys(context);
const allParams = keys.concat(windowKeys, [`"use strict"; return ${code}`]);
try {
const fn = Function.apply(null, allParams);
const params = keys.map((key) => context[key]);
return fn(...params);
} catch (e) {
console.log(e);
}
};
// simple expression evaluation
const res = safeEval("a + b", { a: 1, b: 2 });
console.log(res);
// try to access window
const res1 = safeEval("{a, b, window, document, this: this}", { a: 1, b: 2 });
console.log(res1);
Idk. if this approach can be exploited, if it does. I think another approach can be running eval on cross-domain iframe and get the result with window messages.
Related
Can someone explain to me the difference of when to use a function by feeding your variables into the parenthesis, and when to tack the function on after the variable with a period, like using the toString() function?
example code
function addMe(a){
a = a+1;
return a;
}
var num = 1;
addMe(num);
num.toString();
I'm not actually sure if my syntax is correct, but I want to know when to feed a variable as a parameter, like how I feed the variable num, to the addMe function. And when to use the function .toString() by putting a period after the variable and typing out the function.
could I have done something like this- provided I built my function correctly?
var num = 1;
num.addMe();
Thanks for the help!
The first is used for simple 'stand alone' functions, while the latter is used for object methods. E.g a number object by default has a toString() method. Some object methods may also require parameters to be passed between the parentheses.
Variables (a function declaration is just a function stored in a variable) are looked up in the scope chain (going up to the next outer scope until a variable with the name is found):
let a = 1; // outer scope
{ // inner scope
console.log(a); // looked up in "inner scope", than "outer scope"
}
Properties of an object are looked up in the objects prototype chain, so if you do
a.b
then a gets looked up in the scopes as explained above, then b is accessed on the resulting object (everything is an object in JavaScript, except for "nothing" (undefined, null)) by looking up the prototype chain. For a simple object, the chain is quite short:
const a = { b: 1 }; // object -> Object.prototype
Here b will be found in the object itself. However all objects inherit from the Object.prototype object, so if you add a property to that (please don't):
Object.prototype.test = 1;
you can then look it up on every object, as the lookup traverses up the prototype chain, and reaches Object.prototype:
console.log({}.test); // 1
Now for numbers (like in your case), they inherit the Number.prototype so you could do:
Number.prototype.addMe = function() {
console.log(this);
};
// two dots are needed to distinguish it from numbers with a fraction (e.g. 1.2)
1..addMe();
That said, now addMe can be called on every number, everywhere in your code. While that might seems useful, it is actually a pain as you don't know where a certain method was added
1..whereDoIComeFrom()
that makes code unreadable and unstructured. Instead if you need a certain functionality multiple times, abstract it into a function, don't touch the native prototypes.
I assume that addMe is just a simplified example, if it isn't, read on:
If you pass an argument to a function in JavaScript, the value will be copied (it is a bit more complicated with non primitives (everything except numbers, booleans etc.)) into the parameter variable of the function called so here:
function addMe(a){
a = a+1;
console.log(a); // 2
return a;
}
var num = 1;
addMe(num);
console.log(num); // 1 ... ?
you actually got two variables (a and num), changing a does not change num. But as you return a you can do:
num = addMe(num);
which copies the value of num into a, then increases a by one and then copues the value of a back to num.
When you did var num = 1 you created a JavaScript object. It looks just like a number but you can think of everything in JavaScript as an object (simplification) and all these objects have different features. So a number has some features, a string has some other features, etc.
You mentioned one feature: toString. Another feature would be toLowerCase.
toString and toLowerCase are functions that come with JavaScript. These functions are then "put on" all of these objects for us to use.
I can have a string variable like
var text = 'MY TEXT'
var lowercaseText = text.toLowerCase()
console.log(lowercaseText) // my text
This code will work because it was decided that the toLowerCase function should work on strings
I can also have an array (list of items)
const list = ['A', 'B', 'C']
const answer = list.toLowerCase()
console.log(answer)
But this code won't work because toLowerCase doesn't work on arrays. So you get the following error message: list.toLowerCase is not a function.
Basically its saying: I don't know what toLowerCase means when used on this list variable (array).
In JavaScript this is called prototypes. Prototype is a way for JavaScript to get some feature from another. Basically: I have all kinds of functions, what object can use what functions. This is called the prototype chain.
In both cases you are using a function. addMe is a function you created and toString is a function in JavaScript that has been placed on objects through this prototype-chain.
Im not actually sure if my syntax is correct
Yes your syntax is correct. Your addMe function is the standard way to create a function in JavaScript.
But i want to know when to feed a variable as a parameter, like how i
feed the variable num, to the addMe function.
Just like you did, you define a function and parameters like you did.
..and when to use the function .toString() by putting a period after
the variable and typing out the function.
When you want to place your function on a object so that all instances of that object can you that object.
In most cases, espcially when you are starting out. You don't have to worry about these prototypes. The way you did.
function addMe(number) {
return number+1
}
const answer = addMe(1) //2
Is a standard way of defining a function and calling it.
I'm looking for a way that one piece of code (A) can generate some kind of token, and then pass it to some other piece of code (B) in such a way that a 3rd piece of code is unable to intercept it (E).
For the purposes of this question assume that A can generate the token securely.
If B exposes some function F to A which accepts, as one of it's arguments, an object which will contain the token, then A can pass that token directly.
But E can find this token by, for example, overwriting console.log and examining the stack.
var x = console.log
console.log = function Eve()
{
x.apply(this, arguments);
console.info(Thing.caller.arguments);
}
(assuming that F invokes console.log of course, but in reality there are plenty such functions that could be overwritten in this way)
I think I have a mitigation against this, which is to delete the secure token from the passed object before calling any potentially compromised functions.
i.e.
var y = function Y(Secure)
{
var Thing = Secure.Arg;
delete Secure.Arg;
console.log('SomeMessage', arguments) ;
}
y({Arg:'SomeArg'})
(in FF at least) manipulating the object argument also changes its value on the stack so the arguments object on the stack now contains an empty object.
Are there any other means that E can gain access to the passed data?
If yes, can those methods be mitigated or should I look into other options with better security?
EDIT for Clarity: It is assumed that E is injected by means outside the control of A or B - e.g. greasemonkey user script
you can use a run-once getter and closure to keep private data off the stack:
function A(secret){
return function(){
var s=secret;
secret=null;
return s;
};
}
function B(fn){
var v=fn();
console.log(fn);
alert("In B(): " + v);
}
var x=A(123); //give secret number to A, getting back a function
alert(x); // no secret here
B(x); // extract the secret by executing a one-time getter
alert(x); // no secret here
alert(x()); // still no secret, it's used-up
since JS uses reference-based security, removing the ref provides security.
Also note that you can set breakpoints to get arguments values, but you can't use try/catch, console.log() or any other user-land trick to recover the 123 in-between A and B in the code above.
You have to run the function to get the value, and running the function destroys the value.
the problem with object properties as OP's code uses is that the secret value is visible on the object if an exception happens, whereas a closure would keep its secret.
It's already a disater if you use a third party library that might overwrite your own code for bad purposes. So the best solution is to use another trustworthy one.
If you're still interested in protecting the token from those libraries, you can define unwritable functions with Object.defineProperty.
var obj = {};
Object.defineProperty(obj,'unwritableMethod',{
value: function(token){
//do stuff with this token
},
writable: false
});
var x = obj.unwritableMethod;
var maliciousMethod = function Eve()
{
x.apply(this, arguments);
console.info(Thing.caller.arguments);
};
// This will be silently ignored.
obj.unwritableMethod = maliciousMethod;
//you can test the result with this
console.log(obj.unwritableMethod === maliciousMethod); //false
I'd like to use new Function(...) to generate a function from very reduced code. I'l like to do this to
avoid parsing the expression on my own and
being as flexible as possible.
I avoid eval() whenever possible. But I'm not sure if it's secure enough to use new Function(...) which is also known as being liable to security holes.
Background
I want to manage the states of menu buttons. So, while defining the buttons, I'd like to write something like
{
..., // More button definition
state: "isInEditmode && (isWidgetSelected || isCursorInWidget),
...
}
While handling the statechange during several events I'll check (summarize) the states of the current overall state object against those in the states attribute.
So I'll generate a Function during rendertime and attaching it as a DOM object attribute, not DOM attribute this way:
...
$el.stateFn = new Function("stateObj", "with (stateObj) {return " + item.state + ";}");
...
Testing state:
visible = $el.stateFn.call(currentStates, currentStates);
The with statement helps me providing the current state object's attributes as variables so that the above expression does not need something like obj.isInEditmode.
Security question
In my opinion this does not introduce security holes as the function attached to the DOM object is generated during render time and read from source. Or am I wrong? Should I avoid this?
Performance hints are appreciated (comment) (I think as long as I evaluating a new Function once during render time, this is acceptable).
Edit 1
I'm using Backbone.js. Using another Framework is out of question.
Some menu items need to be bound to different or even multiple models.
Delegation (or facade / proxy?) models are considerable.
Security-wise both are just as bad if user input is allowed to break out in the code. However, maintenance wise you don't have to worry about hidden bugs when local eval messes with your scope and causes dynamic scoping.
Performance-wise the function generated by new Function is exactly the same as any other function. The generation is slower but inlike eval it doesn't cause the containing scope to be unoptimizable.
In fact, new Function can be used to improve performance in situations like:
//Will behave like function a( obj ) { return obj.something }
function makePropReader( propName ) {
return new Function( "obj", "return obj." + propName );
}
The constructed function will perform better than the function returned here:
function makePropReader( propName ) {
return function( obj ) {
return obj[propName];
}
}
Due to having to dynamically read propName from closure context and do a dynamic read on the object everytime it is called.
Please never ever use eval no matter what. There is a much better alternative. Instead of eval, use the Function constructor. eval is evil, and there's no question about that, but most people skip over the most evil aspect of eval: it gives you access to variables in your local scope. Back in the 90s, back before the concept of JIST compilation, eval sounded like a good idea (and it was): just insert some additional lines dynamically into the code you're already executing line-by-line. This also meant that evals didn't really slow things down all. However, now-a-days with JIST compilation eval statements are very taxing on JIST compilers which internally remove the concept of variable names entirely. For JIST compilers, in order to evaluate an eval statement, it has to figure out where all of its variables are stored, and match them with unknown globals found in the evaled statement. The problem extends even deeper if you get really technical.
But, with Function, the JIST compiler doesn't have to do any expensive variable name lookups: the entire code block is self-contained and in the global scope. For example, take the following terribly inefficient eval snippet. Please note that this is only for the purpose of being an example. In production code, you shouldn't even be using eval or Function to generate a function from a string whose content is already known.
var a = {
prop: -1
};
var k = eval('(function(b){return a.prop + b;})');
alert( k(3) ); // will alert 2
Now, let's take a look at the much better Function alternative.
var a = {
prop: -1
};
var k = (Function('a', 'b', 'return a.prop + b')).bind(undefined, a);
alert( k(3) ); // will alert 2
Notice the difference? There is a major one: the eval is executed inside the local scope while the Function is executed inside the global one.
Now, onto the next problem: security. There is a lot of talk about how security is difficult, and yes, with eval it is pretty much impossible (e.x. if you wrap the whole code in a sandboxing function, then all you have to do is prematurely end the function and start a new one to execute code freely in the current scope). But, with Function, you can easily (but not the most efficiently) sandbox anything. Look at the following code.
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
"use-strict";
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
'"use-strict";' + arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
Then, fiddle around with the whitelist, get it just the way you want it, and then you can use sandboxed_function just like Function. For example:
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
"use-strict";
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
'"use-strict";' + arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
var myfunc = sandboxed_function('return "window = " + window + "\\ndocument = " + document + "\\nBoolean = " + Boolean');
output.textContent = myfunc();
<pre id="output"></pre>
As for writing code to be runned under this strict sandbox, you may be asking, if window is undefined, how do I test for the existence of methods. There are two solutions to this. #1 is just simply to use typeof like so.
output.textContent = 'typeof foobar = ' + typeof foobar;
<div id="output"></div>
As you can see in the above code, using typeof will not throw an error, rather it will only just return undefined. The 2nd primary method to check for a global is to use the try/catch method.
try {
if (foobar)
output.textContent = 'foobar.constructor = ' + foobar.constructor;
else
output.textContent = 'foobar.constructor = undefined';
} catch(e) {
output.textContent = 'foobar = undefined';
}
<div id="output"></div>
So, in conclusion, I hope my code snippets gave you some insight into a much better, nicer, cleaner alternative to eval. And I hope I have aspired you to a greater purpose: snubbing on eval. As for the browser compatibility, while the sandboxed_function will run in IE9, in order for it to actually sandbox anything, IE10+ is required. This is because the "use-strict" statement is very essential to eliminating much of the sneaky sand-box breaking ways like the one below.
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
/*'"use-strict";' +*/ arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
var myfunc = sandboxed_function(`return (function(){
var snatched_window = this; // won't work in strict mode where the this
// variable doesn't need to be an object
return snatched_window;
}).call(undefined)`);
output.textContent = "Successful broke out: " + (myfunc() === window);
<pre id="output"></pre>
One last final comment is that if you are going to allow event API's into your sandboxed environment, then you must be careful: the view property can be a window object, making it so you have to erase that too. There are several other things, but I would recommend researching thoroughly and exploring the objects in Chrome's console.
Lastly, note that Function is a very unique constructor which returns a function instead of an object instance, so there's no need to use new.
Old thread with answers considered dangerous these days.
new Function() still allows access to global variables. So an adversary, when given the chance to effect the function string - which is usually the very reason for considering new Function and hard to guarantee it can't be done maliciously -, can read and modify any global. Good luck from that point on :-)
Which is why new Function falls under the same category as eval from the viewpoint of CSP (Content Security Policy) as mentioned here.
Example:
a = 10
> 10
b = new Function('a = 20; return 42')
> function(...)
a
> 10
b()
> 42
a
> 20
As you have said that you will only be doing this on code you wrote yourself - I'd say that it's fine. new Function() is definitely better than using eval() in any case. You won't be messing with any local variables and you're enforcing your own context by using fn.call.
It seems to me that the problem you are trying to solve would be fairly straight forward if you were using an MVC or MVVM framework that supports 2 way data binding. I.e. changing the UI updates a backing model and updating the model will automatically refresh the UI for you.
For example, knockout.js. In this case the visible databinding would be appropriate.
Scenario
I have an object with a lot of properties and methods, and suppose it's stored in Global.Framework. Now, I have another object called User and I want to provide it access to Global.Framework by cloning the .Framework to User.
However, User also has a property called Name, (stored in User.Name) which needs to be passed to each framework's methods as the first argument, transparently.
Example Code
For example, in the method declarations for Global.Framework, there may be something like
Global.Framework = {
methodOne: function(name, a, b, c) { /* do something */ },
methodTwo: function(name, a) { /* do something */ },
propertyOne: 100,
propertyTwo: "me"
}
However, I want these methods to be exposed to User.Framework like this: (The properties are simply cloned. They do not need any extra processing)
User.Framework = {
methodOne: function(a, b, c) {
return Global.Framework.methodOne(User.Name, a, b, c);
} (...)
The Problem
Obviously, as the amount of methods in Framework will change, and perhaps even their arguments, I cannot declare them one by one manually in the cloning process.
What I've tried so far
I've looked up how to get the arguments dynamically and found this: How to get function parameter names/values dynamically from javascript
But I am not sure how to make this happen, and it should preferably not use too many processing resources. This is what I am thinking of:
Go through every property in the Framework object, cloning it if not a function, or
Get the arguments list for the function
?? Rewrite the calls to return functionBeingLooped(User.Name, [the rest of the arguments])
I'm stuck on step 3 and my limited Javascript knowledge reminds me of nothing except eval (which is out of question). Is there a way to accomplish this?
You could set all User.Framework functons to a closure (not sure how you clone your Global.Framework).
Here is the pseudo code assuming username comes as the first argument, if it doesn't you're going to have to parse the Global.Framework funtction .toString() value:
var org={};
org.t = function(name, a ,b){
console.log(arguments);
};
var cloned={};
cloned.name="cloned";
// your cloning method
for(thing in org){
if(typeof org[thing] === 'function'){
// if name isn't the first argument then you have to use
// org[thing].toString() and parse that
cloned[thing]=function(){
org[thing].apply(cloned,[cloned.name]
.concat(Array.prototype.slice
.call(arguments, 0)));
}
}
}
console.log(cloned.t("a","b"));
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/