run javascript function from string variable and pass parameters - javascript

I need to call a user defined javascript function from within my costom jquery plugin and pass parameters to it, for example:
function test(data)
{
var myfunc="function(data){alert(data);}"; //this is user defined function I retrieved from html tag attribute
var fn=new Function("("+myfunc+")();");
fn.apply(this,arguments);
return fn;
}
test("hello");
The result is undefined, how can I pass data parameter from test function to user defined function? thanks in advance!
question update:
I'm writing a jquery plugin to handle ajax request, much like asp.net mvc unobtrusive ajax, I get the ajax callfack function from html tag attrbute, for example:
<div data-ajax-success="function(data,status,xhr){alert(data);}"....
the value of data-ajax-success attribute is user defined function, it can be following formats:
data-ajax-success="function(data,status,xhr){alert(data);}"
data-ajax-success="function(data){alert(data);}"
data-ajax-success="function(){alert('hello');}"
data-ajax-success="functionName"
I need to parse this attribute value as javascript function and pass jquery ajax callback parameters to this function, where data-ajax-success value is function name, I could call it correctly using following method defined in Micrsoft jquery-unobtrusive-ajax.js:
function getFunction(code, argNames) {
var fn = window, parts = (code || "").split(".");
while (fn && parts.length) {
fn = fn[parts.shift()];
}
if (typeof (fn) === "function") {
return fn;
}
argNames.push(code);
return Function.constructor.apply(null, argNames);
}
but when data-ajax-success is function body, I could not pass parameter to it, here's my sample code that handle ajax callback:
loadData: function (index, options) {
complete: function (xhr,status) {
$(context.loading).hide(context.loadingDuration);
getFunction(context.onComplete, ["xhr", "status"]).apply(this, arguments);
},
success:function (data, status, xhr) {
$(context.updateTarget).html(data);
getFunction(context.onSuccess, ["data", "status", "xhr"]).apply(this, arguments);
},
error: getFunction(context.onFailure, ["xhr", "status", "error"])
});
$.ajax(options);
}
anyone can help me? thank you very much!

MDN describes the syntax of the Function object like this:
new Function ([arg1[, arg2[, ... argN]],] functionBody)
Here is the corresponding example:
// Example can be run directly in your JavaScript console
// Create a function that takes two arguments and returns the sum of those arguments
var adder = new Function("a", "b", "return a + b");
// Call the function
adder(2, 6);
// > 8
Applied to your example code it should read:
var fn=new Function("data",myfunc);
Reference:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function

You are not passing an argument to the fn function.
Change this part:
var fn=new Function("("+myfunc+")();");
to this:
var fn=new Function("("+myfunc+")("+data+");");
But if you are defining the function like that the data variable must contain a json string:
var fn=new Function("("+myfunc+")("+JSON.stringify(data)+");");

I think you are not correctly using the Function constructor. See this link for reference:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function?redirectlocale=en-US&redirectslug=Core_JavaScript_1.5_Reference%2FObjects%2FFunction#Example.3A_Specifying_arguments_with_the_Function_constructor

Please check this
<!DOCTYPE html>
<html>
<body>
<p>Setting a default value to a function parameter.</p>
<p id="demo"></p>
<script>
function test(content)
{
const funString = `(function(content){return content})(content)`
var adder = eval(funString);
// Call the function
return adder;
}
document.getElementById("demo").innerHTML = test(2);
</script>
</body>
</html>

I solved it by modifying this microsoft method:
function getFunction(code, argNames) {
var fn = window, parts = (code || "").split(".");
while (fn && parts.length) { fn = fn[parts.shift()]; }
if (typeof (fn) === "function") { return fn; } //onSuccess="functionName"
if ($.trim(code).toLowerCase().indexOf("function")==0) { return new Function("return (" + code + ").apply(this,arguments);");} //onSuccess="function(data){alert(data);}"
argNames.push(code);
try {return Function.constructor.apply(null, argNames); //onSuccess="alert('hello');return false;"
}catch(e){alert("Error:\r\n"+code + "\r\nis not a valid callback function");}
}

Related

How to convert object value to function in javascript?

I want to used objects to store function name and another properties which i will used it to load jS script by Jquery getScript.
I have used conditional by inAarray to check if an object is existing in this array I will call that scrip and execute that function (func) but I still have stack to convert that object value to a function for execution after JS scrip load within status success.
function fetchingData(n_source_id, types, not_id) {
var data = {
'Loan Repayment':{
url:'/js/load/loan/loan_repayment.js',
func:'repayment'// name of function which I will call after JS file successfully loaded
},
'Reject Repayment':{
url:'/js/load/loan/repay_change_till_acc.js',
func:'changeTill'// name of function which I will call after JS file successfully loaded
},
'Issue Till': {
url: '/js/load/issueTill.js',
func: 'issueTill'// name of function which I will call after JS file successfully loaded
}
};
$.each(data, function(inx, vals) {
var _type = inx;
var url = vals;
if ($.inArray(types, [_type]) >= 0) {
ScriptRequire([vals.url], function(status) {
if (status === 'success') {
var fun = new Function(vals.function);
console.log(fun(n_source_id, types));
return fun;
}
});
}
});
}
Since vals.function holds the name of the function, and the function should already be defined by the loaded script, do not use the following to get the function object:
var fun = new Function(vals.function);
Instead, use:
var fun = window[vals.function];

How to call a function on string jQuery

I was reading through fluent api I got a doubt.
I want to take in a string upon which a jQuery function or example is called upon
Function
function compareThis(newString) {
function compare(newString) {
if (this == newString) {
alert("same string");
} else {
alert("differnt string");
}
}
}
Where it is called as
("alerting").compareThis("alerted").compare(); //alert 'different string'
I want to pass the data/string not as parameter but as called upon.
JSFiddle
Note: I would like to call the function in similar cases like finding date interval etc
You can use prototype to add function to String class:
String.prototype.compare = function(newString){
if (this == newString) {
alert("same string");
} else {
alert("differnt string");
}
};
I think you should adapt the code for your function, but it's the idea.
Maybe I missed interpreted however, it looks as it you required a form of method chaining to compare string. To do this you can create a variable and create functions inside it.
var compare = (function(){
var thisString;
var stringToCompare;
var create = function(sVal) {
thisString = sVal;
return this;
};
// Public
var compareThis = function(sVal) {
stringToCompare = sVal;
return this;
};
var compare = function(anotherString) {
return thisString == stringToCompare;
};
return {
create: create,
compareThis: compareThis,
compare: compare
};
}());
var b = compare.create('test').compareThis('test').compare();
alert(b);
Example fiddle

Javascript, possible to pass undeclared method parameters without eval?

Ok, difficult to understand from the title only. Here is an example. I want a function to refer to a variable that is "injected" automagically, ie:
function abc() {
console.log(myVariable);
}
I have tried with:
with({myVariable: "value"}) { abc() }
but this doesn't work unless abc is declared within the with block, ie:
with({myVariable: "value"}) {
function abc() {
console.log(myVariable);
}
abc(); // This will work
}
So the last piece will work, but is it possible to fake the with statement, or do I have to force the developers to declare their function calls in a with statement?
Basically the call I want to do is:
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
Ofcourse, I am aware I could pass this is a one parameter object, but that is not what I am trying to do:
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
Further more, I am trying to avoid using eval:
with({myVariable: "value"}) {
eval(abc.toString())(); // Will also work
}
Is this not supported at at all beyond eval in Javascript?
JavaScript does not provide any straightforward way to achieve the syntax you're looking for. The only way to inject a variable into a Lexical Environment is by using eval (or the very similar Function constructor). Some of the answers to this question suggest this. Some other answers suggest using global variables as a workaround. Each of those solutions have their own caveats, though.
Other than that, your only option is to use a different syntax. The closest you can get to your original syntax is passing a parameter from doSomething to the callback, as Aadit M Shah suggested. Yes, I am aware you said you don't want to do that, but it's either that or an ugly hack...
Original answer (written when I didn't fully understand the question)
Maybe what you're looking for is a closure? Something like this:
var myVariable = "value";
function doSomething() {
console.log(myVariable);
};
doSomething(); // logs "value"
Or maybe this?
function createClosure(myVariable) {
return function() {
console.log(myVariable);
};
}
var closure = createClosure("value");
closure(); // logs "value"
Or even:
var closure = function(myVariable) {
return function() {
console.log(myVariable);
};
}("value");
closure(); // logs "value"
I asked a similar question a long time ago: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
The short answer is no, you can't achieve dynamic scoping without resorting to eval. The long answer is, you don't need to.
JavaScript doesn't support dynamic scoping, but that's not an issue because you can make your free variables parameters of the function that they belong to.
In my humble opinion this is the best solution:
function doSomething(context, callback) {
callback(context);
}
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
However since you don't want to write a formal parameter, the next best thing is to use this instead:
function doSomething(context, callback) {
callback.call(context);
}
doSomething({myVariable: "value"}, function() {
console.log(this.myVariable);
});
Another option would be to manipulate the formal parameter list of the program as follows:
function inject(func, properties) {
var args = [], params = [];
for (var property in properties) {
if (properties.hasOwnProperty(property)) {
args.push(properties[property]);
params.push(property);
}
}
return Function.apply(null, params.concat("return " + func.toString()))
.apply(null, args);
}
Now we can use this inject method to inject properties into a function as follows:
function doSomething(context, callback) {
var func = inject(callback, context);
func();
}
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
See the demo: http://jsfiddle.net/sDKga/1/
Note: The inject function will create an entirely new function which will not have the same lexical scope as the original function. Hence functions with free variables and partially applied functions will not work as expected. Only use inject with normal functions.
The Function constructor is kind of like eval but it's much safer. Of course I would advise you to simply use a formal parameter or this instead. However the design decision is your choice.
Try:
function doSomething(vars, fun) {
for (var key in vars) { // set the variables in vars
window[key] = vars[key];
}
fun.call(); // call function
for (var key in vars) { // remove the variables again. this will allow only the function to use it
delete window[key];
}
}
Set global variables that can then be received inside of fun
The JSFiddle: http://jsfiddle.net/shawn31313/MbAMQ/
Warning: disgusting code ahead
function callWithContext(func, context, args) {
var oldProperties = {};
for(var n in context) {
if(context.hasOwnProperty(n)) {
var oldProperty = Object.getOwnPropertyDescriptor(self, n);
oldProperties[n] = oldProperty;
(function(n) {
Object.defineProperty(self, n, {
get: function() {
if(arguments.callee.caller === func) {
return context[n];
}
if(!oldProperty) {
return;
}
if(oldProperty.get) {
return oldProperty.get.apply(this, arguments);
}
return oldProperty.value;
},
set: function(value) {
if(arguments.callee.caller === func) {
context[n] = value;
}
if(!oldProperty) {
return;
}
if(oldProperty.set) {
return oldProperty.get.apply(this, arguments);
} else if(!oldProperty.writable) {
var fakeObject = {};
Object.defineProperty(fakeObject, n, {value: null, writable: false});
fakeObject[n] = value; // Kind of stupid, but…
return;
}
oldProperty.value = value;
}
});
})(n);
}
}
func.apply(this, args);
for(var n in context) {
if(context.hasOwnProperty(n)) {
if(oldProperties[n]) {
Object.defineProperty(self, n, oldProperties[n]);
} else {
delete self[n];
}
}
}
}
This is vomitously horrendous, by the way; don’t use it. But ew, it actually works.
i don't see why you can't just pass the info in or define a single global, but i think that would be best.
that said, i am working on a Module maker/runner that allows sloppy/dangerous code to execute without interference to the host environment. that provides the opportunity to re-define variables, which can be passed as an object.
this does use eval (Function() technically) but it can run in "use strict", so it's not too crazy/clever.
it doesn't leave behind artifacts.
it also won't let globals get hurt.
it's still a work in progress, and i need to iron out a couple minor details before i vouch for security, so don't use it for fort knox or anything, but it's working and stable enough to perform the operation asked for.
tested in ch28, FF22, IE10:
function Module(strCode, blnPreventExtensions, objWhitelist, objExtend) {
var __proto__=self.__proto__, pbu=self.__proto__, str=strCode, om=[].map, wasFN=false,
params = {Object:1}, fnScrubber, natives= [ Object, Array, RegExp, String, Boolean, Date] ,
nativeSlots = [],
preamble = "'use strict';" ,
inherited="__defineGetter__,__defineSetter__,__proto__,valueOf,constructor,__lookupGetter__,__lookupSetter__",
late = inherited +
Object.getOwnPropertyNames(__proto__||{}) + Object.getOwnPropertyNames(window);
late.split(",").sort().map(function(a) {
this[a] = 1;
}, params);
preamble+=";var "+inherited+";";
//turn functions into strings, but note that a function was passed
if(str.call){wasFN=true; str=String(str); delete params.Object; }
objExtend=objExtend||{};
var vals=Object.keys(objExtend).map(function(k){ return objExtend[k]; })
// build a usable clone of Object for all the new OOP methods it provides:
var fakeOb=Object.bind();
(Object.getOwnPropertyNames(Object)||Object.keys(Object)).map(function(a){
if(Object[a] && Object[a].bind){this[a]=Object[a].bind(Object); } return this;
},fakeOb)[0];
//allow "eval" and "arguments" since strict throws if you formalize them and eval is now presumed safe.
delete params.eval;
delete params.arguments;
params.hasOwnProperty=undefined;
params.toString=undefined;
params['__proto__']={};
__proto__=null;
Object.keys(objWhitelist||{}).map(function ripper(a,b){
b=this[a];
if(typeof b!=='object'){
delete this[a];
}
}, params);
// var ok=Object.keys.bind(Object);
// prevent new prototype methods from being added to native constructors:
if (blnPreventExtensions) {
natives.forEach(function(con, i) {
var proto=con.prototype;
Object.getOwnPropertyNames(proto).map(function(prop){
if(proto[prop] && proto[prop].bind ){ this[prop]=proto[prop];}
}, nativeSlots[i] = {});
delete con.constructor;
delete con.prototype.constructor;
}); //end con map()
} /* end if(blnPreventExtensions) */
//white-list harmless math utils and prevent hijacking:
delete params.Math;
if(blnPreventExtensions){Object.freeze(Math);}
//prevent literal constructors from getting Function ref (eg: [].constructor.constructor, /./.constructor.constructor, etc...):
Function.prototype.constructor = null;
try {
//generate a private wrapper function to evaluate code:
var response = Function(
Object.keys(objExtend) + (vals.length?",":"") +
Object.keys(params).filter(/./.test, /^[\w\$]+$/), // localize most globals
preamble + " return " + str.trim() // cram code into a function body with global-blocking formal parameters
);
// call it with a blank this object and only user-supplied arguments:
if (blnPreventExtensions) { //( user-land code must run inside here to be secure)
response = response.apply({}, vals.concat(fakeOb)).apply({}, [].slice.call(arguments,4) );
}else{
response = response.apply({}, vals.concat(fakeOb));
}
} catch (y) {
response = y + "!!";
} /* end try/catch */
if (blnPreventExtensions) {
om.call(natives, function(con, i) {
var pro=con.prototype;
//remove all proto methods for this con to censor any additions made by unsafe code:
Object.getOwnPropertyNames(pro).map(function(a){ try{delete pro[a];}catch(y){}});
//restore all original props from the backup:
var bu = nativeSlots[i];
om.call(Object.keys(bu), function(prop){ con.prototype[prop]=bu[prop]; }, bu);
}); //end con map()
} /* end if(blnPreventExtensions) */
//restore hidden Function constructor property:
Function.prototype.constructor = Function;
return response;
} /* end Module() */
/////////////////////////////////////////////////////////////
function doSomething(context, fn){
console.log(myVariable);
return myVariable;
}
//use 1:
alert( Module(doSomething, true, {console:1}, {myVariable: "value123"} ) );// immed
//use2:
var fn=Module(doSomething, false, {console:1}, {myVariable: "value123"} );// as function
alert(fn);
alert(fn());
again, i think OP would be best off not doing things later than need be, but for the sake of comprehensiveness and inspiration i'm putting this out there in good faith.
You need to use call() to construct a context, as in:
var f=function(){
console.log(this.foo);
};
f.call({foo:'bar'})
will print "bar"
You can avoid using eval() in calling the function, if you are willing to use it in doSomething():
function abc() {
console.log(myVariable);
}
// Prints "value"
callWith({ myVariable: "value" }, abc);
function callWith(context, func) {
for(var i in context) eval('var ' + i + ' = context[i];');
eval('(' + func.toString() + ')')();
}
Have a look at this post.
Have a look at goog.partial, scroll a little bit up to see the description of what it does:
Here is an implementation of it:
var b = goog.partial(alert, 'Hello world!');
b();//alerts "Hello world!"
In the example it passes the function alert with parameter "Hello world!" but you can pass it your own function with multiple parameters.
This allows you to create a variable that points to a function that is always called with a certain paramater. To use parameters in a function that are not named you can use arguments:
function test(){
console.log(arguments);//["hello","world"]
}
test("hello","world");

JS/ use object which define outside the function

I have the next function in JS:
function status(){
this.functionA = function(){}
//Some others function and fields
}
and I have another function:
function create(root){
var server = libary(function (port) {
//Here some functions
});
var returnValue = {
current:status(),
cur:function(port){
current.functionA();
}}
return returnValue;
}
when I call current.functionA(), It says that current is undefined. How can I call functionA()?
When you have a function-constructor like status(), you'll need to call new for it. I've revised part of your code here.
var returnValue = {
current: new status(),
cur:function(port){
current.functionA();
}}
return returnValue;
}
Just to differentiate; create() doesn't need a new statement because you're actually creating and returning an object to refer to, inside of the function.
function status(){
this.functionA = function(){alert("functionA");}
}
function create(root){
var returnValue = {
current:status.call(returnValue),
cur:function(port){ this.functionA(); }.bind(returnValue)
}
return returnValue;
}
create().cur(999);
I corrected your issue using the JavaScript "call" and "bind" methods which are part of the Function prototype.

Can event handler defined within JavaScript object literal access itself?

I know I could do this with closures (var self = this) if object was a function:
click here
<script type="text/javascript">
var object = {
y : 1,
handle_click : function (e) {
alert('handling click');
//want to access y here
return false;
},
load : function () {
document.getElementById('x').onclick = this.handle_click;
}
};
object.load();
</script>
The simplest way to bind the call to handle_click to the object it is defined in would be something like this:
var self=this;
document.getElementById('x').onclick =
function(e) { return self.handle_click(e) };
If you need to pass in parameters or want to make the code look cleaner (for instance, if you're setting up a lot of similar event handlers), you could use a currying technique to achieve the same:
bind : function(fn)
{
var self = this;
// copy arguments into local array
var args = Array.prototype.slice.call(arguments, 0);
// returned function replaces first argument with event arg,
// calls fn with composite arguments
return function(e) { args[0] = e; return fn.apply(self, args); };
},
...
document.getElementById('x').onclick = this.bind(this.handle_click,
"this parameter is passed to handle_click()",
"as is this one");
So, the event handler part wires up just fine (I tested it myself) but, as your comment indicates, you have no access to the "y" property of the object you just defined.
This works:
var object = {
y : 1,
handle_click : function (e) {
alert('handling click');
//want to access y here
alert(this.y);
return false;
},
load : function () {
var that = this;
document.getElementById('x').onclick = function(e) {
that.handle_click(e); // pass-through the event object
};
}
};
object.load();
There are other ways of doing this too, but this works.
I see how to do it with Jason's latest one. Any way to do it without the anonymous function?
We can directly pass an object with a handler method thanks to AddEventListener, and you will have access to its attributes:
http://www.thecssninja.com/javascript/handleevent
Hope this will help those who, like me, will look for this topic some years after!

Categories