JavaScript: Get Argument Value and NAME of Passed Variable [duplicate] - javascript

This question already has answers here:
Determine original name of variable after its passed to a function
(9 answers)
Closed 8 years ago.
What I want to do is get the NAME of a variable passed to a function and the VALUE of that variable, and only have to pass in one variable to the function. So:
var x = "anything";
function showName() {
}
showName(x);
or
showName("x");
Which will return: "x = anything".
Right now, I have to specify the variable twice:
showName("x", x);
In order to get the name and value of the variable I am passing in.
Note that I am not interested in the name of argument in the prototype of showName, but the name of the variable in the calling function. Also, the variable passed may be local, so I can't use the window object to find the variable.

The short answer is that you can't.
The longer, evil answer is that you sort of can with some real nastiness. And it only works when called from another function.
there are two interesting attributes available to you that could help
arguments.callee
caller
for fn to do something like this:
(function(){
var showMe = function(s){
alert(arguments.callee.caller.toString().match(/showMe\((\S)\)/)[1] +
' = '+ s)
}
x = 1
showMe(x)
})()
What arguments.callee.caller.toString().match(..)[1] does is look for the showMe being called in the function calling it and prints it and its value.
But this is still pretty limited because it will only hit the first call of showMe(x). So if there is two calls to it, it won't work.
But, it was fun to play with these arcane things.

Strategy 1:
If you can control the data structure during function invocation then you can pass a dictionary which will encode name as a key, paired with its value, notice the stealth curly braces:
var foo = "bar";
yourfunction({foo});
Which passes a javascript dictionary that looks like this:
{foo : "bar"}
When yourfunction( is executed, unpack name and value thustly:
yourfunction = function(dict) {
var name = Object.keys(dict)[0];
var value = dict[name];
console.log(name); //prints foo
console.log(value); //prints bar
}
Strategy 2:
If you can maintain an as-you-go list of name-value pairs in a global scope, then reflection and introspection is always available for set and get, for example:
var my_global_stack = [];
yourfunction = function() {
//Chomp the stack
var dict = my_global_stack.pop();
//The name is the key at index 0
var name = Object.keys(dict)[0];
//Fetch the value by keyname:
var value = dict[name];
console.log(name); //prints foo
console.log(value); //prints bar
}
foo = "bar";
my_global_stack.push({foo});
yourfunction();
Strategy 3:
If user-hostile input isn't an issue, you can use eval( to rediscover value given variablename, for example:
yourfunction = function(somevariable) {
console.log(somevariable); //prints foo
console.log(eval(somevariable)); //prints bar
}
foo = "bar";
yourfunction("foo");
People say eval( is evil here, because if a hostile user is able to overwrite the value of foo in memory at any point, then they can do OS Command Injection and run any command they want.
http://cwe.mitre.org/top25/#Guidance

var x = "anything";
function showName(s) {
alert(s + " = " + eval(s));
}
showName("x");
Not recommended, but there it is.

You could create a hash and pass that in:
var x = {a: 1,b:2}
function showVars(y) {
for (var z in y) { alert(z + " is " + y[z]); }
}
showVars(x);
This doesn't necessarily show the name of the variable, but it does allow for key-value pairs, which may be more to the point of what you need.

This is what I use for debugging. No global variables, no eval, no arguments.callee or arguments.caller:
var Helpers = (function () {
// ECMAScript 5 strict mode
'use strict';
var Module = {};
Module.debug = function () {
var i;
for (i = 0; i < arguments.length; i++) {
console.log(arguments[i] + ':', this[arguments[i]]);
}
};
Module.SomeObject = function SomeObject() {
this.someMember = 1;
this.anotherMember = 'Whatever';
Module.debug.call(this, 'someMember', 'anotherMember');
var privateMember = {
name: 'Rip Steakface',
battleCry: 'Raaaaaaaaahhhhhhhhhrrrrrrrrrg!'
};
Module.debug.call(privateMember, 'name', 'battleCry');
};
return Module;
}());
For those who are wondering why you would want to do this, it's just a way to efficiently log multiple variables along with their names.
If you want to be able to log nested members, as in Module.debug.call(obj, 'hair.fluffiness'), you can modify the function like so:
Module.debug = function () {
var i, j, props, tmp;
for (i = 0; i < arguments.length; i++) {
tmp = this;
props = arguments[i].split('.');
for (j = 0; j < props.length; j++) {
tmp = tmp[props[j]];
}
console.log(arguments[i] + ':', tmp);
}
};
Unfortunately, I can't find any way to efficiently log multiple private variables that aren't members of an object, e.g. var roll = 3, value = 4; Module.debug.call(???);

Not sure you can directly get what you want from JavaScript, since the variable name is not carried around with the value it references (think of variable names as identifiers only the compiler knows about; but which get thrown away at runtime).
You can, however, do something slightly different which allows for passing around named arguments. Create an anonymous object and pass that to your function:
function showNames(o)
{
for( var ix in o )
{
alert( ix + ":" + o[ix] );
}
}
var z = { x : "Anything" }
showNames( z );
// or
showNames( { a : "ay", b : "bee", c: "see" } )
For iterating object properties, I tend to prefer a functional-style, as in:
Array.iteri = function(o, f)
{
for(var i in o) { f(i, o[i]) }
}
function showNames(o)
{
Array.iteri( o, function(i,v)
{
alert( i + ": " + v )
});
}
showNames( { a : "ay", b : "bee", c: "see" } )

The below code is about the best you can do. Unfortunately local variables in a function are properties of the hidden Call Object so they can't be accessed from Javascript like window[a] where a is a property of the window object.
x = "this is x";
var say = function(a) {
document.write(a + " = " + window[a]);
}
say("x");
var wrapper = function () {
var x = "this is x";
document.write(x + " = " + eval("x"))
}
wrapper()

Related

How do I wrap a javascript function with dynamic arguments?

I'd like to wrap some dynamically created javascript functions, similar to Daniel's accepted answer here:
How do I store javascript functions in a queue for them to be executed eventually
// Function wrapping code.
// fn - reference to function.
// context - what you want "this" to be.
// params - array of parameters to pass to function.
var wrapFunction = function(fn, context, params) {
return function() {
fn.apply(context, params);
};
}
The difference is I'd like the argument values to be dynamic at time of execution - is it possible to pass a reference to a variable in the arguments, which could be updated after it is wrapped?
Here's what I'd like to do:
// I have a function to be wrapped
var sayStuff = function(a,b) {
console.log(a);
console.log(b);
}
// Variables I'd like to pass
var randomNumberA = 0;
var randomNumberB = 0;
// Wrap the function
var fun = wrapFunction(sayStuff, this, [*reference randomNumberA*,*reference randomNumberB*]);
// variables get changed
randomNumberA = Math.random()*100;
randomNumberB = Math.random()*100;
// Execute the function using current values of randomNumberA & randomNumberB
fun();
If possible I'd like to do this without changing sayStuff, I have a lot of existing functions like this I'm hoping to wrap, which also get used outside of the wrapping, so ideally I'd like to not replace the arguments with an object.
Hope that makes sense, Thanks!
If the function and the variable will be created in the same scope you can just use that:
var randomNumber = 0;
var fun = function(){ alert(randomNumber); }
randomNumber = 10;
// Now this will alert 10, because when fun is executed
// JS looks in his scope to find what randomNumber is.
fun();
This happens because functions in javascript works as Closures, they carry their environment with them. See: https://en.wikipedia.org/wiki/Closure_(computer_programming)
So if randomNumber will be changed out of the scope where you bind that function, you need to use an object, this is because in javascript we don't have "pointers" or references to pass by. One way is using a object.
function giveMeAFunction(){
var params = { randomNumber: 0 }
var fun = function(){ alert(scope.randomNumber); }
return {fun: fun, scope: scope};
}
var paramsAndFun = giveMeAFunction()
// Now you can change the variables in the scope and call the function
paramsAndFun.params.randomNumber = 10;
paramsAndFun.fun(); // Will alert 10
// Now if you replace the entire params object it will not work
// This is because you will replacing it with a new object while
// The one that is referenced in the scope where fun was created is
// the old one.
paramsAndFun.params = { randomNumber: 15 };
paramsAndFun.fun(); // will still alert 10
Now let's get to binding part of the problem.
There is already Function.prototype.bind function to help you with that.
For example:
var sayStuff = function(opts) {
alert(otions.randomNumber);
}
var options = { randomNumber: 0 };
var fun = sayStuff.bind(this, options);
options.randomNumber = 10;
fun(); // Will print 10
There is a lot going on here. Sorry if I made everything confuse.
If the dynamic arguments are defined in the context argument, a solution can be based passing the name of the variables and then, at execution time, calculate its current value:
var wrapFunction = function(fn, context) {
var xArg = arguments;
return function() {
var argsArray = [];
for (var i = 2; i < xArg.length; i++) {
argsArray.push(context[xArg[i]]);
}
fn.apply(context, argsArray);
};
}
var sayStuff = function() {
for (var i = 0; i < arguments.length; i++) {
console.log('sayStuff func: ' + arguments[i]);
}
}
var randomNumber1 = 0;
var randomNumber2 = 0;
var fun = wrapFunction(sayStuff, this, 'randomNumber1', 'randomNumber2');
randomNumber1 = Math.random()*100;
randomNumber2 = Math.random()*100;
console.log('randomNumber1: ' + randomNumber1);
console.log('randomNumber2: ' + randomNumber2);
fun();

Scope of variables in module pattern

I am trying to understand how I can touch/change/increment my privately scoped variable x in the following script. I'm using the Module pattern here, and thought I could reach into and set the private variables from a public return module declared property or method, but nothing I'm trying is working. Related: when do you declare a new instance of func vs. accessing func as a static delcared variable?
var func = (function() {
var x = 1;
var squareX = function() {
return x * x;
};
var addOne = function() {
x++;
}
return {
X: x,
Xq: squareX,
AddOne: addOne
};
});
func().X = 9; // expecting privately scoped x = 9
func().AddOne(); // expecting privately scoped x = 10
document.write(func().Xq()); // expecting 100 actual = 1
The point of the module pattern is to create a persistent, private scope which is invisible from the outside. Unfortunately, every time you call func, you're creating a new scope (with new return functions and their closures), so all of your operations are discarded afterwards.
Instead of calling func multiple times, just do it once to setup the "module" (you can even do this immediately, with an IIFE), and then perform operations on the result.
var func = function() {
var x = 1; // this is the private variable
var squareX = function() {
return x * x;
};
var addOne = function() {
x++;
};
return {
// Note, you can't just do "X: x,"
// since that will just create a copy;
// you have to use properties
get X() { return x; },
set X(val) { x = val; },
Xq: squareX,
AddOne: addOne
};
};
var funcModule = func();
funcModule.X = 9;
funcModule.AddOne();
document.write(funcModule.Xq());
Note that the reason you need an explicit getter and setter for the X module property is because you need to be able to modify the inner (hidden) variable x. Properties are available in all modern browsers, including IE9+. If you're working in IE8 or below, you'll need to define explicit getX and setX methods, and call them directly (you won't just be able to do funcModule.X = 5).
You need a setter, and you need an IIFE:
var func = (function() {
var x = 1;
var squareX = function() {
return x * x;
};
var addOne = function() {
x++;
}
return {
X: function(value) {
if (value !== undefined) {
x = value;
}
return x; // we can use this as a getter too!
},
Xq: squareX,
AddOne: addOne
};
})(); // <-- this actually runs the function - this makes it an IIFE
document.write("X is " + func.X() + "</br>");
func.X(9); // expecting privately scoped x = 9
document.write("now X is " + func.X() + "</br>");
func.AddOne(); // expecting privately scoped x = 10
document.write("now X is " + func.X() + "</br>");
document.write(func.Xq()); // expecting 100 actual = 100
You're using the Revealing Module Pattern to hide your private instance variable. When using that pattern, you must use a setter of some kind to change your private instance variable. Here is another StackOverflow post where someone else was having the same problem.
I don't recommend that you use the Revealing Module Pattern. In fact, I just gave a talk at NationJS 2014 titled "The Revealing Module is an Anti-Pattern".

Unexpected behavior using getters and setters

Look this code:
<script>
function dbg (object) {
var _string = "";
for (var a in object) {
_string += a + ":\n";
for (var b in object[a])
if (/^get_/.test (b))
_string += "\t" + b + " - " + object[a][b] () + "\n";
}
return _string;
}
function Order () {
var products = [];
this.get_products = function () {return products;}
this.set_products = function (_products) {products = _products;}
}
function Product () {
var id = null;
var name = null;
this.get_id = function () {return id;}
this.get_name = function () {return name;}
this.set_id = function (_id) {id = _id;}
this.set_name = function (_name) {name = _name}
}
var order = new Order ();
var product = new Product ();
product.set_id (1);
product.set_name ("Banana");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Ok
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Duplicated values! What?
</script>
The first time you push the object "Product" into the object "Order", everything looks fine.
When you set new values to the object "Product", the object itself overwrites the previous values of the object "Order". The final result is a array of duplicated values. Is it normal ? Is there a workaround? Just tried everything I knew without success. Thanks.
Crazy Train has already answered it in the comments. The question is listed having 0 answers so I'll add it as an answer.
When adding a variable containing an object to an array you add a reference to the variable, when you re assign the variable the reference is broken.
Adding a variable containing an object to an array then re assigning the variable doesn't change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object=33;
console.log(arr);//=[Object {name="john"}]
Adding a variable containing an object to an array then changing the internal values of the object that the variable contains does change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object.name="Jane";
console.log(arr);//=[Object {name="Jane"}]
So to correct your code you could do the following:
Create a new variable for the product to be added:
var product2=new Product();
product2.set_id (2);
product2.set_name ("Orange");
order.set_products (order.get_products ().concat (product2));
Or break the reference between your product variable and the products array in order:
product=null;//product has no ref to order.products
product=new Product();
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
I would not define members of an object in a constructor function with var as JavaScript doesn't support private members. You can simulate them by creating closures but that has it's own problem when you have instance specific privates (as is your case). You can't use prototype if the functions need to access private instance variables, you can't clone it unless you have public accesssors, inheritance and overriding functions will be a pain.
Here is some more info on using constructor functions.
If you have Chrome or Firefox (with Firebug) then you can press F12 to open the console. You an detach the console window (have it's own window) then copy code in the before mentioned answers and paste them in the commandline of the console. There you can run and re run the code, change and see the output to better understand JS behavior.
You are just overriding the variables in object. I'd do it like this, much simpler:
var products = {
set : function(name,id) {
products.list.push({name:name,id:id});
},
get : function(id) {
var r;
if(typeof id === 'number'){
products.list.forEach(function(e,i){ if(e.id==id) r= products.list[i];});
} else {
products.list.forEach(function(e,i){ if(e.name==id) r = products.list[i];});
}
return r;
},
list : []
};
var order={
set : function(p) {
order.list[p.id]=p;
},
get : function(id) {
return order.list[id];
},
delete : function(id) {
return delete order.list[id];
},
list : {}
};
then you can do this
products.set('apple',34);
products.set('orange',4);
products.set('mango',1);
var x = products.get(1);
var y = products.get('orange');
order.set(x);
order.set(y);
working demo:
http://jsfiddle.net/techsin/tjDVv/2/

How to use a Javascript Function to name Variables?

I'd like to be able to create variables using a function, something like:
function createVar(a,b){
var [a]name[b];
}
So if I call the function createVar(prefix,suffix) I'd end up with a variable named prefixnamesuffix.
You can't create a local variable that way, but you can create properties with strings.
function createVar(obj, a, b)
{
obj[a+'name'+b] = foo;
}
function createVar(a, b, value) {
// declare the variable on current context(default is window),
// and set value, default is undefined
this[a + 'name' + b] = value;
}
use as:
createVar('a', 'b');
// variable anameb now is declare, but it's value is undefined
console.log(anameb); // -> undefined
createVar('prefix', 'suffix', 'optional');
// variable prefixnamesuffix now is exist in window
console.log(prefixnamesuffix); // -> 'optional'
or:
var obj = {};
createVar.call(obj, 'pre', 'suf', 'defValue');
// variable prenamesuf is now exist in obj, but not exist in window
console.log(obj.prenamesuf); // -> 'defValue'
console.log(prenamesuf); // ReferenceError: prenamesuf is not defined
This IS possible - sort of as it IS a bit tricky.
This all begins by understanding the scope of a variable. Let's look at a basic, simple example:
var myvar = 'I see';
This creates a varaible, at a basic level named myvar that you can access such as alert(myvar); which will, in this case alert "I see".
What this really does is create a property on the window object. Thus, var myvar = 'I see'; is the same as window['myvar'] = 'I see';.
Complex objects can also be created in this manner.
Some more examples:
window['fred'] = 'howdy fred';// create fred property
var regularfred = 'howdy fred'; // create regularfred property
window['town'] = {}; // create town object
window['town']['george'] = 'georgetown'; //create town.george property
alert(fred + ":" + regularfred); // alerts "howdy fred:howdy fred"
alert(fred == regularfred);// alerts true
alert(this['town']['george']);// alerts "georgetown"
function createvar(a, b) {
this[a + 'name' + b] = "fredling";
alert(this[a + 'name' + b]);// alerts "fredling"
alert(window[a + 'name' + b]);// alerts "fredling"
}
var hi = 'hi';
var lo = 'loook';
createvar(hi, lo);
alert(this[hi + 'name' + lo]);// alerts "fredling"
Requisit working example on this: http://jsfiddle.net/pGWZN/

Can you dynamically add local variables to a function?

I'm using objects to namespace my JavaScript code. These objects usually contain functions that are called mapping the this-pointer to the object itself using apply. However, I find it inconvenient to use the this-pointer everytime I want to access other functions or properties of the object, especially because in lots of cases I use the new-operator to use function-objects the way you would use classes. I would prefer writing new Line() instead if new this.Line().
It would be great if you could add local variables to a function the way php does it with extract (pseudocode following, it's a little more complicated)
var sample_object = {
"some_function": function() {}
}
test() {
extract(sample_object);
some_function(); // imported from sample_object
}
Is that even possible?
I'm pretty sure eval is your only answer; but you need to be aware that if there's any input outside of your control involved, it isn't safe
function dynamicArgs (varName, varValue) {
eval("var " + varName + "=" + JSON.encode(varValue) );
alert(a);
}
dynamicArgs("a", "value");
You can see the problem with this. How is your function supposed to call the dynamic variable if it doesn't know its name? I hardcoded it to the a variable since I pass it in when calling it, but that's not a good solution. The only solution would be another eval. You really should think about what you need to do and whether this is useful. But it's doable.
Here it is in action: http://jsfiddle.net/mendesjuan/GG3Wu/
function dynamicArgs (varName, varValue) {
eval('var ' + varName + "='" + varValue + "';");
alert(eval(varName));
}
dynamicArgs("f", "Here I am");
Now here's an example like what you're doing, creating a variable from this.MyConstructor
http://jsfiddle.net/mendesjuan/AK3WD/
var ns = {
MyConstructor: function(val) {
this.prop = val;
},
runConstructor: function(val) {
var Ctor = "MyConstructor";
eval('var ' + Ctor + ' = this.' + Ctor);
return new MyConstructor(val);
}
}
alert( ns.runConstructor("Hello").prop );
And here's an example if you wanted to import all the values from an object into the scope;
http://jsfiddle.net/mendesjuan/AK3WD/1/
var ns = {
MyConstructor: function(val) {
this.val= val;
},
anotherProperty: 5,
runConstructor: function(val) {
// Bring all the variables from this into this scope
for (var prop in this) {
eval('var ' + prop + ' = this.' + prop);
}
alert('Testing var anotherProperty: ' + anotherProperty);
var obj = new MyConstructor(val);
alert('Created MyConstructor: its prop is ' + obj.val)
}
}
ns.runConstructor("Hello");
There is controversial with, which has some great applications, but is marginally slow and prone to errors. It throws an error in the strict mode (which you should always opt into) and is going to be deprecated.
var sampleObject = {
someFunction: function() {},
b: 10
}
with (sampleObject) {
typeof someFunction // "function"
var a = 42
var b = 20
}
sampleObject.a // undefined
sampleObject.b // 20
Note, that new variables defined in a with-block won't be added to the object. Nevertheless, if the object already had an eponymous property in it, this property would be modified (thanks, #Rocket).
Just for fun, here's an implementation of extract using eval (which is even more evil than with). You can do unspeakable things with it, for example if your object has properties like sampleObject['x; while (true) { alert("Hi!") }'].
This is how I did it:
function smObject ( object) {
return function () {
function getter(prop) {
return function() {
return this[prop];
}
}
function setter(prop) {
return function(data) {
this[prop]=data;
}
}
for (var o = 0; o < object.length; o++) {
this[object[o]] = {};
this['get' + object[o]] = getter(object[o]);
this['set' + object[o]] = setter(object[o]);
}
}
}
now you can instantiate a function like this:
var fields = ['Name', 'Id', 'Other', '....' ]
var MyFunction = smObject( fields );
var myObject = new MyFunction();
// getter/setters
myObject.setId(5);
myObject.getId(); // will return 5
Regards,
Emanouil

Categories

Resources