new Function() with variable parameters - javascript

I need to create a function with variable number of parameters using new Function() constructor. Something like this:
args = ['a', 'b'];
body = 'return(a + b);';
myFunc = new Function(args, body);
Is it possible to do it without eval()?
Thank you very much, guys! Actually, a+b was not my primary concern. I'm working on a code which would process and expand templates and I needed to pass unknown (and variable) number of arguments into the function so that they would be introduced as local variables.
For example, if a template contains:
<span> =a </span>
I need to output the value of parameter a. That is, if user declared expanding function as
var expand = tplCompile('template', a, b, c)
and then calls
expand(4, 2, 1)
I need to substitute =a with 4. And yes, I'm well aware than Function is similar to eval() and runs very slow but I don't have any other choice.

You can do this using apply():
args = ['a', 'b', 'return(a + b);'];
myFunc = Function.apply(null, args);
Without the new operator, Function gives exactly the same result. You can use array functions like push(), unshift() or splice() to modify the array before passing it to apply.
You can also just pass a comma-separated string of arguments to Function:
args = 'a, b';
body = 'return(a + b);';
myFunc = new Function(args, body);
On a side note, are you aware of the arguments object? It allows you to get all the arguments passed into a function using array-style bracket notation:
myFunc = function () {
var total = 0;
for (var i=0; i < arguments.length; i++)
total += arguments[i];
return total;
}
myFunc(a, b);
This would be more efficient than using the Function constructor, and is probably a much more appropriate method of achieving what you need.

#AndyE's answer is correct if the constructor doesn't care whether you use the new keyword or not. Some functions are not as forgiving.
If you find yourself in a scenario where you need to use the new keyword and you need to send a variable number of arguments to the function, you can use this
function Foo() {
this.numbers = [].slice.apply(arguments);
};
var args = [1,2,3,4,5]; // however many you want
var f = Object.create(Foo.prototype);
Foo.apply(f, args);
f.numbers; // [1,2,3,4,5]
f instanceof Foo; // true
f.constructor.name; // "Foo"
ES6 and beyond!
// yup, that easy
function Foo (...numbers) {
this.numbers = numbers
}
// use Reflect.construct to call Foo constructor
const f =
Reflect.construct (Foo, [1, 2, 3, 4, 5])
// everything else works
console.log (f.numbers) // [1,2,3,4,5]
console.log (f instanceof Foo) // true
console.log (f.constructor.name) // "Foo"

You can do this:
let args = '...args'
let body = 'let [a, b] = args;return a + b'
myFunc = new Function(args, body);
console.log(myFunc(1, 2)) //3

If you're just wanting a sum(...) function:
function sum(list) {
var total = 0, nums;
if (arguments.length === 1 && list instanceof Array) {
nums = list;
} else {
nums = arguments;
}
for (var i=0; i < nums.length; i++) {
total += nums[i];
}
return total;
}
Then,
sum() === 0;
sum(1) === 1;
sum([1, 2]) === 3;
sum(1, 2, 3) === 6;
sum([-17, 93, 2, -841]) === -763;
If you want more, could you please provide more detail? It's rather difficult to say how you can do something if you don't know what you're trying to do.

A new feature introduced in ES5 is the reduce method of arrays. You can use it to sum numbers, and it is possible to use the feature in older browsers with some compatibility code.

There's a few different ways you could write that.
// assign normally
var ab = ['a','b'].join('');
alert(ab);
// assign with anonymous self-evaluating function
var cd = (function(c) {return c.join("");})(['c','d']);
alert(cd);
// assign with function declaration
function efFunc(c){return c.join("");}
var efArray = ['e','f'];
var ef = efFunc(efArray);
alert(ef);
// assign with function by name
var doFunc = function(a,b) {return window[b](a);}
var ghArray = ['g','h'];
var ghFunc = function(c){return c.join("");}
var gh = doFunc(ghArray,'ghFunc');
alert(gh);
// assign with Class and lookup table
var Function_ = function(a,b) {
this.val = '';
this.body = b.substr(0,b.indexOf('('));
this.args = b.substr(b.indexOf('(')+1,b.lastIndexOf(')')-b.indexOf('(')-1);
switch (this.body) {
case "return":
switch (this.args) {
case "a + b": this.val = a.join(''); break;
}
break;
}
}
var args = ['i', 'j'];
var body = 'return(a + b);';
var ij = new Function_(args, body);
alert(ij.val);

Maybe you want an annoymous function to call an arbitary function.
// user string function
var userFunction = 'function x(...args) { return args.length}';
Wrap it
var annoyFn = Function('return function x(...args) { return args.length}')()
// now call it
annoyFn(args)

new Function(...)
Declaring function in this way causes
the function not to be compiled, and
is potentially slower than the other
ways of declaring functions.
Let is examine it with JSLitmus and run a small test script:
<script src="JSLitmus.js"></script>
<script>
JSLitmus.test("new Function ... ", function() {
return new Function("for(var i=0; i<100; i++) {}");
});
JSLitmus.test("function() ...", function() {
return (function() { for(var i=0; i<100; i++) {} });
});
</script>
What I did above is create a function expression and function constructor performing same operation. The result is as follows:
FireFox Performance Result
IE Performance Result
Based on facts I recommend to use function expression instead of function constructor
var a = function() {
var result = 0;
for(var index=0; index < arguments.length; index++) {
result += arguments[index];
}
return result;
}
alert(a(1,3));

function construct(){
this.subFunction=function(a,b){
...
}
}
var globalVar=new construct();
vs.
var globalVar=new function (){
this.subFunction=function(a,b){
...
}
}
I prefer the second version if there are sub functions.

the b.apply(null, arguments) does not work properly when b inherits a prototype, because 'new' being omitted, the base constructor is not invoked.

In this sample i used lodash:
function _evalExp(exp, scope) {
const k = [null].concat(_.keys(scope));
k.push('return '+exp);
const args = _.map(_.keys(scope), function(a) {return scope[a];});
const func = new (Function.prototype.bind.apply(Function, k));
return func.apply(func, args);
}
_evalExp('a+b+c', {a:10, b:20, c:30});

Related

How do I call a JavaScript method based on a variable value?

I have method a(), method b(), and method c(). I will get a response message from server, which contains a or b or c and so on.
If the response message is a, then I need to call method a().
If the response message is b, then I need to call method b()
And so on...
I don't want to write any if else conditions or switch case to identify the method.
I don't want to do this:
if(res == 'a')
a();
else if(res == 'b')
b();
Instead of that I need something like reflections in Java.
If you have defined the function in Global/window Scope then you can directly use res variable
window[res]();
Otherwise define the function in object and then use it
var obj = {
a : function(){},
b : function(){}
}
obj[res]();
You could use an object and store the function inside, like
var functions = {
a: function () {},
b: function () {},
c: function () {}
default: function () {} // fall back
}
Usage:
functions[res]();
Or with default
(functions[res] || functions.default)();
For this purpose you can define a class that allows you to define and call methods, and determine the calling context:
var MethodsWorker = function () {
this._context = window;
this._methods = {};
}
MethodsWorker.prototype.setContext = function (context) {
this._context = context;
}
MethodsWorker.prototype.defineMethod = function (name, method) {
this._methods[name] = method;
};
MethodsWorker.prototype.invoke = function (methodName, args) {
var method = this._methods[methodName];
if (!method) { throw {}; }
return method.apply(this._context, args);
};
Usage:
var methodsWorker = new MethodsWorker ();
methodsWorker.setContext(Math);
methodsWorker.defineMethod('sqrtOfSum', function() {
var sum = 0;
for (var i = 0, n = arguments.length; i < n; i++) {
sum += arguments[i];
}
return this.sqrt(sum);
});
var result = methodsWorker.invoke('sqrtOfSum', [1, 2, 3]);
alert (result);

Currying with functions that take unlimited arguments

Lets say i have the following add function that takes an unlimited number of arguments.
function add () {
var total = 0;
var args = Array.prototype.slice.call(arguments, 0);
for (var i=0; i<args.length; i++) {
total += arguments[i];
}
return total;
}
and the following curry function.
function curryFunction(orig_func) {
var ap = Array.prototype;
var args = arguments;
function fn() {
if (arguments.length != 0) {
ap.push.apply(fn.args, arguments);
return fn;
} else {
return orig_func.apply(this, fn.args);
}
};
return function() {
fn.args = ap.slice.call( args, 1 );
return fn.apply( this, arguments );
};
}
I then want to do something like:
var f = curryFunction(add);
var a = f(3)(4)(3);
var b = f(10)(3);
var result1 = a(); // returns 10
var result2 = b(); // returns 13
However i always get 13 for both a() and b() i assume is because in line
fn.args = ap.slice.call(args, 1);
the existing array [3,4,3] is overwriting with []. Can someone please provide me with a hint on how to make this work? Thanks
The problem is that fn is scoped to curryFunction and so is shared between a and b.
All you have to do is move the definition of fn into the anonymous return function. It's then created when you call f, and the problematic fn.args = line is only called once.
Proof: jsFiddle.
Currying a function which takes indefinitely many arguments can be implemented as follows;
Lets say we have a function called addAll() which returns the sum of all provided arguments.
var addall = (...a) => a.reduce((p,c) => p + c);
And we have a curry function which takes a function and returns curried version ad infinitum up until the returned function is called with no arguments, only when the result of all previously provided arguments will be returned. OK here is the curry function.
var curry = f => (...a) => a.length ? curry(f.bind(f,...a))
: f();
Lets see it in action;
var addAll = (...a) => a.reduce((p,c) => p + c),
curry = f => (...a) => a.length ? curry(f.bind(f,...a)) : f(),
curried = curry(addAll),
result = curried(10,11)(10)(37)(10,17,42)();
console.log(result);
result = curried("a","b")("c")("d")("e","f","g")();
console.log(result);

Programmatically create a new object

I am trying to create a function which will take arguments arg1, arg2... then pass them into a constructor for a new object C like so: new C(arg1, arg2...), so to make a new instance of C the user would simply have to call C(arg) instead of new C(arg). Here is my first attempt:
var C = function(a){ this.a = a; }
var Cn = function(){
new C.apply(this, arguments);
}
Cn(0) // Should make a new C with a property a equal to 0
new C(0) // ie the same as this
Edit: Note, I need it to take an arbitrary number of arguments and not use eval. I'm making a library implementing Algebraic Data Types in js.
Edit: The solution was to take Jeremy's Idea and adapt it to take an unbounded number of arguments:
var C = function() {
// A unique object so we can identify when we used the 'newless' constructor
var newlessConstructorObj = {}
// Check to see if C has been called with `new`
if(!(this instanceof C))
// If not pass arguments as single arg back to C
return new C(newlessConstructorObj, arguments);
// Check to see if we got here from the line above, if so the arguments were passed in the second arg
var args = (arguments[0] === newlessConstructorObj) ? arguments[1] : arguments
// Do stuff with args
this.a = args[0];
}
C(0);
new C(0);
If you want to be able to call the function with or without the new keyword, you have to follow this pattern:
C = function(a) {
if (!(this instanceof C)) {
return new C(a);
}
this.a = a;
}
so to create a new instance of "C":
c = new C(a);
and
c = C(a);
will both return a properly formed instance of C
I would have picked Fabrizio Calderan's solution any day, but because you want this specific functionality - here is what predecessors tell us:
you can apply arguments to prototype.constructor (but there are issues when doing it with native types, like Number):
var Cn = function(){
return C.prototype.constructor.apply(C, arguments);
}
Link: Instantiating a JavaScript object by calling prototype.constructor.apply
or.. use eval:
function construct(Constructor)
{
/*
* or Array.prototype.slice.call(arguments, 1).map(function() { ... })
* in JavaScript 1.6+, compatibles, and with augmented Array.prototype
*/
var args = [];
for (var i = 1, len = arguments.length; i < len; i++)
{
args[i - 1] = "arguments[" + i + "]";
}
/* or args.join(", ") if you need it pretty-printed */
return eval("new Constructor(" + args + ")");
}
function Foo()
{
window.alert(Array.prototype.slice.call(arguments, 0).join(", "));
}
var f = construct(Foo, /bar/g, {baz: 42});
Link: http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/ff1a104bdc33d5c8
something like this?
var C = function(obj){
var a;
for (a in obj) {
this[a] = obj[a];
}
}
var Cn = function(obj) { return new C(obj); }
instance = Cn({
a : 1,
b : 2
})
instance.a //1
instance.b //2
instance.c //undefined
Doing it with a fixed number of args is simple:
// assuming one arg
function Cn(arg) {
return new C(arg);
}
// assuming two args
function Cn(arg0, arg1) {
return new C(arg0, arg1);
}
and so on. You can even make a general version for any number of parameters by iterating over arguments to create a string, then eval it. Crass, but effective.
But what's the point? Just to save typing 4 characters?
If you don't care about having a correct instanceof check you can try this:
var C = function(a){ this.a = a; }
var Cn = function(){
return C.apply({}, arguments); // notice an empty object here
}
Cn(0) // Should make a new C with a property a equal to 0
new C(0) // ie the same as this
Updated for ES6, you can use the spread operator:
var C = function(a){ this.a = a; }
var Cn = function(...args){
return new C(...args);
}
assert.deepStrictEqual(Cn(10), new C(10));

Literal object and function constructor

I took this test on http://jsperf.com/literal-obj-vs-function-obj and Literal wins on FF6, Opera 10, IE8, but the Function method faster on Chrome 13.0.782.112, so which one is a better method to use?
var A = {
aa : function(){
var i, j=[];
var arr = ['Literal', 'Function'];
for (i = 0; i < arr.length; i++){
j[i] = arr[i];
}
return j[0];
}
};
var A1 = A;
var A2 = A1;
A1.foo = ' Test';
alert(A1.aa() + A2.foo);
//Function test
function B(){
this.bb = function(){
var i, j=[];
var arr = ['Literal', 'Function'];
for (i = 0; i < arr.length; i++){
j[i] = arr[i];
}
return j[1];
}
}
var B1 = new B();
var B2 = new B();
B.prototype.foo = ' Test';
alert(B1.bb() + B2.foo);
To tell you the truth, the best that I have found is a mix:
function C() {
var i, j = [],
foo;
return {
bb: function() {
var arr = ['Literal', 'Function'];
for (i = 0; i < arr.length; i++) {
j[i] = arr[i];
}
return j[1];
},
setFoo: function(val) {
foo = val;
},
getFoo: function() {
return foo;
}
}
}
var C1 = C();
var C2 = C();
C2.setFoo(' Test');
console.log(C1.bb(), C2.getFoo());
Whichever one you prefer. Speed should never be a concern unless it becomes a real problem in your app. What you're doing looks like premature optimization to me, which is a waste of time. Wait until you need to optimize your code, then optimize the parts that need to be reworked. This is trivial.
Chrome uses an optimisation called hidden classes that allows it to do faster access to object members.
I would bet that this optimisation is enabled only when the object is constructed via new, and as a result an object constructed via new would have faster member access than an object not constructed via new.
well, chrome has around 17% of the market share according to wikipedia. So use literals.
I doubt you're going to do anything intensive enough for the difference to matter.
Function style constructors give you the option of private variables, which is nice.

Set length property of JavaScript object

Let's say I have a JavaScript object:
function a(){
var A = [];
this.length = function(){
return A.length;
};
this.add = function(x){
A.push(x);
};
this.remove = function(){
return A.pop();
};
};
I can use it like so:
var x = new a();
x.add(3);
x.add(4);
alert(x.length()); // 2
alert(x.remove()); // 4
alert(x.length()); // 1
I was trying to make .length not a function, so I could access it like this: x.length, but I've had no luck in getting this to work.
I tried this, but it outputs 0, because that's the length of A at the time:
function a(){
var A = [];
this.length = A.length;
//rest of the function...
};
I also tried this, and it also outputs 0:
function a(){
var A = [];
this.length = function(){
return A.length;
}();
//rest of the function...
};
How do I get x.length to output the correct length of the array inside in the object?
You could use the valueOf hack:
this.length = {
'valueOf': function (){
return A.length;
},
'toString': function (){
return A.length;
}
};
Now you can access the length as x.length. (Although, maybe it's just me, but to me, something about this method feels very roundabout, and it's easy enough to go with a sturdier solution and, for example, update the length property after every modification.)
If you want A to stay 'private', you need to update the public length property on every operation which modifies A's length so that you don't need a method which checks when asked. I would do so via 'private' method.
Code:
var a = function(){
var instance, A, updateLength;
instance = this;
A = [];
this.length = 0;
updateLength = function()
{
instance.length = A.length;
}
this.add = function(x){
A.push(x);
updateLength();
};
this.remove = function(){
var popped = A.pop();
updateLength();
return popped;
};
};
Demo:
http://jsfiddle.net/JAAulde/VT4bb/
Because when you call a.length, you're returning a function. In order to return the output you have to actually invoke the function, i.e.: a.length().
As an aside, if you don't want to have the length property be a function but the actual value, you will need to modify your object to return the property.
function a() {
var A = [];
this.length = 0;
this.add = function(x) {
A.push(x);
this.length = A.length;
};
this.remove = function() {
var removed = A.pop();
this.length = A.length;
return removed;
};
};
While what everyone has said is true about ES3, that length must be a function (otherwise it's value will remain static, unless you hack it to be otherwise), you can have what you want in ES5 (try this in chrome for example):
function a(){
var A = [],
newA = {
get length(){ return A.length;}
};
newA.add = function(x){
A.push(x);
};
newA.remove = function(){
return A.pop();
};
return newA;
}
var x = a();
x.add(3);
x.add(4);
alert(x.length); // 2
alert(x.remove()); // 4
alert(x.length); // 1
You should probably use Object.create instead of the function a, although I've left it as a function to look like your original.
I don't think you can access it as a variable as a variable to my knoledge cannot return the value of a method, unless you will hijack the array object and start hacking in an update of your variable when the push/pop methods are called (ugly!). In order to make your method version work I think you should do the following:
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};
These days you can use defineProperty:
let x = {}
Object.defineProperty(x, 'length', {
get() {
return Object.keys(this).length
},
})
x.length // 0
x.foo = 'bar'
x.length // 1
Or in your specific case:
Object.defineProperty(x, 'length', {
get() {
return A.length
}
})
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};

Categories

Resources