Getting rid of eval - javascript

I have a name of a method as a string in javascript variable and I would like to get a result of its call to variable:
var myMethod = "methodToBeCalled";
var result;
eval("result = "+myMethod+"();")
This works and there are no problems. But this code is inacceptable for Google Closure Compiler. How can I modify it to work with it? Thanks!
EDIT:
It seems the proposed solutions does not work when the name of the method is inside of some object, for instance:
var myFunction = function () { return "foo!" }
var myObject = {
itsMethod: function() { return "foo!" }
};
...
var fstMethodToCall = "myFunction"
var sndMethodToCall = "myObject.itsMethod";
...
window[fstMethodToCall](); // foo!
window[sndMethodToCall](); // undefined

Assuming you are not in a nested scope of some kind, try:
var result = window['methodToBeCalled']();
or
var myMethod = 'methodToBeCalled';
var result = window[myMethod]();
To execute an arbitrary function of arbitrary depth based on a string specification, while not executing eval:
var SomeObject = {
level1: {
level2: {
someFunc: function () {
console.log('hello');
}
}
}
};
var target = 'SomeObject.level1.level2.someFunc';
var obj;
var split = target.split('.');
for (var i = 0; i < split.length; i++) {
obj = (obj || window)[split[i]];
}
obj();

You can use indexer notation:
result = window[myMethod]();

The Closure Compiler doesn't prohibit 'eval', you can continue to use it if you find it convenient but you have to understand that the compiler doesn't try to understand what is going on in your eval statement and assumes your eval is "safe":
function f(x, y) {
alert(eval("y")); // fails: hidden reference to "y"
alert(eval('"'+x+'"')); // might be valid
}
f('me', 'you');
When the compiler optimizes this function it tries to remove "y" and renamed the remain parameter. This will the first eval to fail as "y" no longer exists. The second eval would correct display the alert "me".
So with SIMPLE optimizations, you can use eval to reference global variables and object properties as these are not renamed or removed (but not local ones).
With ADVANCED optimizations, it is a little trickier, as the compiler tries to remove and rename global as well as local variables. So you need to export the values you need to have preserved. This is also true if you use a string to try to reference a name by other means:
var methodName = "myMethod";
(window[methodName])()
or
var methodName = "myMethod";
eval(methodName+"()")
the compiler simply doesn't try to determine if "methodName" is a reference to a function. Here is a simply example of an ADVANCED mode export:
window['myMethod'] = myMethod;
The assignment does two things: it preserves the myMethod function if it would otherwise be removed and it gives it a fixed name by assigning it to a property using a string. If you do need to reference local values, you need to be a little trickier and use a Function constructor. A definition of "f" from my first example, that can eval locals:
var f = new Function("x", "y", "alert(eval('y')); alert(eval('\"' + x + '\"'));");
You may find this page useful:
https://developers.google.com/closure/compiler/docs/limitations

Related

Is there any way to add a new parameter to a function programmatically?

Since I can determine the number of arguments a function expects to have by calling its Function.length property, is there any way for me to programmatically create the right number of parameters to insert into that function at runtime? Example:
var xyz = function(a,b) {};
var bcd = function(a,b,c,d,e,f) { }; // vararg example
var doc = document, func_length = xyz.length;
doc.xyz = (function() {
return function(a,b,c,d,e) { /* works for one but not the other */ } }).call(doc);
/* would prefer to `return function(a,b)` with only 2 parameters, if it is
used for function `xyz` (though returning 5 works fine in this case), and to
`return function(a,b,c,d,e,f)` with 6 if used for function `bcd` (which does
not work with only five params). */
// thinking about xyz.apply(null,arguments)...but which arguments..? :(
// Returning function(a,b,c,d,e) does not support functions with more than five
// parameters...which would mostly be varargs - hence my question
// I am also well aware that I can use an object or an array instead of
// using many params.
/* This is for incorporating a user-defined function for use in my code, while
* allowing for my function to do 'other stuff' afterward. (And allowing for
* varargs, of course).
* Because coding something like: doc.xyz = xyz is inflexible */
As you can see, I don't know how to do this, or if it is even possible. The search bar hasn't given me any other questions like this one, otherwise I would not have asked...
NOTE: This answer is a product of misunderstanding but
may help the future visitors of this site.
Another way:
Do you really need to add parameters? Writing the function this way would be enough:
function foo(){ //No arguments predefined
a = arguments[0] || ""; //first argument or (if not defined) empty string
b = arguments[1] || ""; //second argument etc.
c = arguments[2] || ""; //third argument etc.
alert(a+b+c);
}
foo("Hello ", "world!");
This alerts "Hello world".
The solution you want:
The simplest way:
This is what you've asked for but it's not as simple as the previous solution.
You can define a meta function with all the parameters and a handler function that changes over the time.
(function(){ //Wrapper
var foo_meta = function(a,b,c,d){ //Local meta of foo
alert(a+b+c+d); //Do the code
};
window.foo = function(a,b){ //Global foo
return foo_meta(a,b,"","");
};
window.redefine_foo = function(){ //Global foo-changer
//Rewrites foo
window.foo = function(a,b,c){
return foo_meta(a,b,c,"");
};
};
})(); //Wrapper
//Do some code
foo("a","b");
redefine_foo(); //Rewrite foo
foo("a","b","c");
//Note that foo_meta is not defined here
foo_meta == undefined; //It's safe in the wrapper :)
This will alert "ab" and then "abc". For the meaning of wrapper function, see the references.
Reference:
Arguments array: http://goo.gl/FaLM1H
Wrapping code: http://goo.gl/uQ5sd0
If you send two parameters 6 and 7 to a function doWork(a,b,c,d,e),a=7 and b=6 will be automatically set and rest of the parameters will be ignored.
Why not just pass one object into the function and use JQuery extend.
e.g.
var parm =
{ x: 1, y : 2};
f(p) {
p = $_.extend({...defaults here}, p);
...
}
This is an example for joining the arguments, regardless of the number of arguments, to show how function arguments can be turned into an array and then processed like any other array.
function foo(){ //No arguments predefined
// convert to real array
var args = Array.prototype.slice.call(arguments);
// or if Array generics are available
var args = Array.slice(arguments);
console.log(args.join(' '));
}
foo('Hello', 'world!');
foo('Hello', 'wonderful', 'world!');
Here is the fiddle
Ref: arguments MDN
Well, I think I've figured it out at last. I've realized that there may be no way to 'truly' add a parameter to a function the way that I was asking, but there is a way to emulate the same result:
var doc = document;
var xyz = function(a,b) {};
var bcd = function(a,b,c,d,e,f) {};
var obj = {};
// Now, here it is (mostly (sort of)):
obj.userFunc = function(args) {
var args_Array = [];
for (var i=0;i < arguments.length; i++ ) {
args_Array.push(arguments[i])
}
xyz.apply(null,args_Array); // or 'this'. or 'undefined'. Whatever you want.
// do other stuff
return this; // we know what to do to make 'this' scope explicit
} // end
var thisFunc = 'xyz'
doc[thisFunc] = obj.userFunc;
doc.xyz('h','i');
doc.xyz('h','i','j');
doc.xyz('h','i','j','k');
doc.xyz('h','i').xyz('j','l').xyz('j','q'); // etc.
The trick was to use the arguments object, which conveniently assimilated all the parameters into a readily available object, push each value into an array then apply the function.
In case you're wondering what the fuss was all about, I wanted to completely incorporate a user-defined function into another function, while still being able to do 'other stuff' afterward. My previous example worked to an extent, but did not have support for varargs. This does.
This approach is greatly more flexible than: doc[thisFunc] = userDefinedFunction
:) 4/26/2014

Make object's properties available inside function

I would like to do the following.I have a code like this:
var obj = {
method : function() {}
};
var func = function() {
return method(); //method is undefined here
};
func(); // What to do here?
Is it possible to call func in a way that it will see the method inside from obj as it was given for example as a parameter. I want to use obj.method inside func, without writing 'obj.' before and without modifying func itself. Is there any hack possible to achieve this?
In other words, is it possible to force obj as a closure into func?
I tried:
with(obj) {
func();
}
But it doesn't work. Anyone, any ideas? Or is it the only option to get the body of the function as string, put 'with(obj)' inside it and then create a new function out of it?
Clarification:
Because this code will be in a helper class 'eval' is OK. Which I don't want is the modification of the function through .toString(), because browsers implement it differently.
This is a solution, using eval (MDN):
var obj = {
method : function() {console.log("it workes!");}
};
var func = function() {
return method(); //method is undefined here
};
var newfunc = (function (obj, func) {
var method = obj.method;
eval("var f = " + func.toString());
return f;
}(obj, func));
newfunc(); //it workes
Basically you're just creating a new scope with a local variable called method and re-evaluating the function body in this scope. So you're basically creating a new function with the same body. I don't really like this approach and I wouldn't recommend it, but considering your constraints, it might be the only solution.
And yes, it still requires you to write obj.method, but not inside of func. So I figured, it should be ok.
EDIT
So here is a version, in which you don't have to specify the property name manually:
var newfunc = (function (__obj__, __func__) {
for (var __key__ in __obj__) {
if (__obj__.hasOwnProperty(__key__)) {
eval("var " + __key__ + " = " + __obj__[__key__]);
}
}
eval("var __f__ = " + func.toString());
return __f__;
}(obj, func));
This also done by using eval().
Note that I changed all remaining local variables to a names containing underscores, to minimize the probability of name collisions with properties inside obj.
Note also that not all valid property names are valid variable names. You could have an object like this:
var obj = {
"my func": function () {}
}
But if you would use this object you would generate a syntax error with the above method, because it would try to evaluate:
var my func = ...
As apsillers said in the comment section, it gets even worse if you don't have control over the properties of obj. In this case you shouldn't use eval at all, because you would make cross-site scripting attacks very easy (example from apsillers):
var obj = {
"a; alert('xss'); var b": function () {}
}
would evaluate to 3 different statements:
var a;
alert('xss');
var b = function () {};
This is not possible unless you define method separately:
var obj = {
method : function() {}
},
method = obj.method;
// rest of code
This is because the method reference inside func() assumes the window. namespace; thus, without modifying func() itself, it can't be done sanely.
More clarified version based on basilikum's answer, and I've found a simplification with 'with':
var obj = {
method : function() { return "it workes!"; }
};
var func = function() {
return method(); //method is undefined here
};
(function (obj, func) {
with(obj) {
eval("var __res__ = (" + func.toString() + ")()");
}
return __res__;
}(obj, func));
>> "It workes!"

Is it possible for a JavaScript function to return its own function call as a string?

In JavaScript, is it possible for a function to return its own function call as a string?
function getOwnFunctionCall(){
//return the function call as a string, based on the parameters that are given to the function.
}
I want this function to simply return its own function call as a string (if it's even possible to do this):
var theString = getOwnFunctionCall(5, "3", /(a|b|c)/);
//This function call should simply return the string "getOwnFunctionCall(5, \"3\", "\/(a|b|c)\/")".
I put this one up on jsFiddle: http://jsfiddle.net/pGXgh/.
function getOwnFunctionCall() {
var result = "getOwnFunctionCall(";
for (var i=0; i < arguments.length; i++) {
var isString = (toString.call(arguments[i]) == '[object String]');
var quote = (isString) ? "\"" : "";
result += ((i > 0) ? ", " : "");
result += (quote + arguments[i] + quote);
}
return result + ")";
}
alert(getOwnFunctionCall(5, "3", /(a|b|c)/));
Note that this should work for your example, but still needs work for arbitrarily complex objects/JSON included as a parameter.
http://jsfiddle.net/WkJE9/4/
function DisplayMyName()
{
//Convert function arguments into a real array then let's convert those arguments to a string.
var args = [].slice.call(arguments).join(',');
// Get Function name
var myName = arguments.callee.toString();
myName = myName.substr('function '.length);
myName = myName.substr(0, myName.indexOf('('));
return(myName + " ("+ args + ")");
}
var functionText = DisplayMyName(5, "3", /(a|b|c)/) //returns DisplayMyName(5, "3", /(a|b|c)/)
alert(functionText);
Using the implicit arguments variable, you can extract both the function arguments and the function name:
function getOwnFunctionCall() {
var args = arguments; // Contains the arguments as an array
var callee = arguments.callee; // The caller function
// Use this to construct your string
}
Edit
Several comments note that callee is not something to be relied on. But if this is something you are going to do inside each of your methods, then just use the function name as you have defined it:
var functionName = "getOwnFunctionCall"; // But you can really just use it inline...
if you NEED to do it, and need to do it in global strict, and you don't want to hard-code the names:
function args(arg){
var me;
try{ badCAll654(); }catch(y){ me=String(y.stack).split("args")[1].split("\n")[1].trim().split("#")[0].replace(/^at /,"").split(" ")[0].trim() }
return me +"("+[].slice.call(arg).join(", ")+")";
}
function getOwnFunctionCall() {
"use strict";
return args(arguments);
}
getOwnFunctionCall(1,true, /dd/);
this can be a good debugging tool, but i would not recommend using it on production sites/apps; it's going to impact performance quite a bit. This pattern only works in chrome and firefox, but works under a global "use strict".
IE9 is less strict, so you can do the following:
function args(arg){
var me=arg.callee+'';
return me.split("(")[0].split("function")[1].trim() +"("+[].slice.call(arg).join(", ")+")";
}
function getOwnFunctionCall() {
"use strict";
return args(arguments);
}
getOwnFunctionCall(1,true, /dd/);
if you poly-fill the trim()s, it should also work in IE8.
if you don't use strict, you can do even more cool stuff like log the function that called the function that's being logged. you CAN even rip that function's source to find calls to the logged function if you want the names of the arguments and not just the values. Complex and worthless, but possible.
again, you should really use this only for debugging!
Based on your comment
I've been trying to find ways to prevent specific functions in eval
statements from being evaluated, and this is one potential solution
for that problem.
What you are asking for might not be what you really need. Why not just override the functions you want to prevent before evaling and restore them aferwards:
var blacklist = [ 'alert', 'setTimeout' ];
var old = {};
// Save the blacklisted functions and overwrite
blacklist.forEach(function(name) {
old[name] = window[name];
window[name] = function() {
console.log(name + ' has been disabled for security reasons');
}
});
eval('alert("Hello world")');
// restore the original functions
blacklist.forEach(function(name) {
window[name] = old[name];
});
is it possible for a function to return its own function call as a string?
No. You cannot extract by what expression you got your arguments into the function - you can only access their values. Of course you could simulate a call string with primitive values, but you never know whether they were passed to the function as a variable, a literal, or a whole expression.
Maybe, Mozilla's toSource method can help you with that.

Javascript: Reference a variable name from the variable itself

I want to create a quick function that will console.log a variable name and the value. I'd like the result of the function to show in the console: foo: bar.
My basic idea for the function looks like this:
function varlog(var_name)
{
console.log(var_name + ": " + eval(var_name));
}
And I'd call is thusly:
function someRandomFunction()
{
var foo = "bar";
// ... some stuff happens
varlog("foo");
}
This works if foo is global, but doesn't work in the example provided. Another option that also only works globally is using window[var_name] instead of the scary eval.
I don't think what I'm asking is possible, but I figured I'd throw it out there.
I'm spending a lot of time attempting to be lazy. My current method is just console.log('foo: ' + bar); which works just fine. But now I just want to know if this is possible.
Some other questions I referenced in searching for this / creating what I have now:
Variable name as a string in Javascript
How to convert variable name to string in JavaScript?
Javascript, refer to a variable using a string containing its name?
How to find JavaScript variable by its name
--
Edit: I'd love to just call varlog(foo), if the name "foo" can be derived from the variable.
Solution - (for your actual use case) - console.log({foo})
In ES6 IdentifierReferences are being accepted as PropertyDefinitions on the ObjectLiteral's PropertyDefinitionList (see compatibility chart):
The variable name is being set to the Object's Property's key
and the variable value is being set to the Object's Property's value.
As console.log shows Objects with their Propertiy/ies' keys and values you can use that to see both your variable's name and value by invoking console.log({foo}).
Note that when you initialize a single anonymous object with several
variables as I did in the second console.log while they appear in
the same order as initialized here in the snippet's output they might
get reordered (alphabetically) elsewhere.
var testint = 3
var teststring = "hi"
var testarr = ["one", 2, (function three(){})]
var testobj = {4:"four", 5:"five", nested:{6:"six",7:"seven"}}
console.log({testint})
console.log({testint, teststring, testarr, testobj})
Answer - (to the question title) - Object.keys({foo})[0]
You can also use this shorthand Object Initializer together with Object.keys() to straightly access the variable name:
var name = "value"
console.log(Object.keys({name})[0])
The reason it doesn't work is because the variable foo is not accessable to the function varlog! foo is declared in someRandomFunction, and is never passed into varlog, so varlog has no idea what the variable foo is! You can solve this problem by passing the variable foo into the function(or using some sort of closure to make foo in the scope of varlog) along with its string representation, but otherwise, I think you are out of luck.
Hope this helps.
While I'm not aware of such a possibility, I'd wanted to share a small idea:
Object.prototype.log = function(with_message) {
console.log(with_message + ":" + this);
}
var x = "string";
x.log("x");
Like I said, a small idea.
Kind of combining a couple of anwers into a small function
Would this work for you?
const log = function() {
const key = Object.keys(this)[0];
const value = this[key];
console.log(`${key}: ${value}`);
}
let someValue = 2;
log.call({someVlaue}); //someValue: 2
Works with function too, even itself.
log.call({log});
// It would return the following
log:function() {
const key = Object.keys(this)[0];
const value = this[key];
console.log(`${key}: ${value}`);
}
I don't believe what you want to do is possible.
The best alternative I can think of is to pass an object to varlog that is basically a key-value hash:
function varlog(obj)
{
for (var varname in obj) {
console.log(varname + ": " + obj[varname]);
}
}
function someRandomFunction()
{
var foo = "bar";
// ... some stuff happens
varlog({foo: foo});
}
I loved #mhitza idea, so I'm making it a little bigger...
The downside is the need to use .valueto reach the variable content.
Object.prototype.log = function(message) {
if (message) console.log(this.name, this.value, message);
else console.log(this.name, this.value);
}
function nar (name, value) {
var o = {name: name, value: value};
this[name] = o;
return o;
}
// var globalVar = 1;
nar('globalVar', 1);
globalVar.log();
// > globalVar 1
globalVar.value += 5;
globalVar.log('equal six');
// > globalVar 6 equal six
var someFunction = function () {
// var localVar = 2;
nar('localVar', 2);
localVar.log('someInfo');
// > localVar 2 someInfo
};
someFunction();
Surprised to see no super simple solution yet.
let varname = "banana"
console.log(`${JSON.stringify({varname}).split('"')[1]}`)
Prints varname in the console

Javascript: constant properties

In javascript, can I declare properties of an object to be constant?
Here is an example object:
var XU = {
Cc: Components.classes
};
or
function aXU()
{
this.Cc = Components.classes;
}
var XU = new aXU();
just putting "const" in front of it, doesn't work.
I know, that i could declare a function with the same name (which would be also kind of constant), but I am looking for a simpler and more readable way.
Browser-compatibility is not important. It just has to work on the Mozilla platform, as it is for a Xulrunner project.
Thank you a lot!
Cheers.
Since you only need it to work on the Mozilla platform, you can define a getter with no corresponding setter. The best way to do it is different for each of your examples.
In an object literal, there is a special syntax for it:
var XU = {
get Cc() { return Components.classes; }
};
In your second exampe, you can use the __defineGetter__ method to add it to either aXU.prototype or to this inside the constructor. Which way is better depends on whether the value is different for each instance of the object.
Edit: To help with the readability problem, you could write a function like defineConstant to hide the uglyness.
function defineConstant(obj, name, value) {
obj.__defineGetter__(name, function() { return value; });
}
Also, if you want to throw an error if you try to assign to it, you can define a setter that just throws an Error object:
function defineConstant(obj, name, value) {
obj.__defineGetter__(name, function() { return value; });
obj.__defineSetter__(name, function() {
throw new Error(name + " is a constant");
});
}
If all the instances have the same value:
function aXU() {
}
defineConstant(aXU.prototype, "Cc", Components.classes);
or, if the value depends on the object:
function aXU() {
// Cc_value could be different for each instance
var Cc_value = return Components.classes;
defineConstant(this, "Cc", Cc_value);
}
For more details, you can read the Mozilla Developer Center documentation.
UPDATE: This works!
const FIXED_VALUE = 37;
FIXED_VALUE = 43;
alert(FIXED_VALUE);//alerts "37"
Technically I think the answer is no (Until const makes it into the wild). You can provide wrappers and such, but when it all boils down to it, you can redefine/reset the variable value at any time.
The closest I think you'll get is defining a "constant" on a "class".
// Create the class
function TheClass(){
}
// Create the class constant
TheClass.THE_CONSTANT = 42;
// Create a function for TheClass to alert the constant
TheClass.prototype.alertConstant = function(){
// You can’t access it using this.THE_CONSTANT;
alert(TheClass.THE_CONSTANT);
}
// Alert the class constant from outside
alert(TheClass.THE_CONSTANT);
// Alert the class constant from inside
var theObject = new TheClass();
theObject.alertConstant();
However, the "class" TheClass itself can be redefined later on
If you are using Javascript 1.5 (in XUL for example), you can use the const keyword instead of var to declare a constant.
The problem is that it cannot be a property of an object. You can try to limit its scope by namespacing it inside a function.
(function(){
const XUL_CC = Components.classes;
// Use the constant here
})()
To define a constant property, you could set the writable attribute to false in the defineProperty method as shown below:
Code snippet:
var XU = {};
Object.defineProperty(XU, 'Cc', {
value: 5,
writable: false
});
XU.Cc = 345;
console.log(XU.Cc);
Result:
5 # The value hasn't changed

Categories

Resources