Call function dynamically in Javascript - javascript

I want to dynamically call a function from a string like "User.find". A script would call the function find() in the object User if the function exists. Here's what I tried:
var User = {};
User.find = function(){
return 1;
}
var input = 'User.find';
var some_data_array = {name: 'John Doe'};
var method = input.toString().split('.');
var nameObj = method[0].substring(0,1).toUpperCase() + method[0].substring(1);
var methodToCall = method[1];
nameObj.call(methodToCall, some_data_array);
But it always returns:
nameObj.call(methodToCall, some_data_array);
TypeError: Object User has no method 'call'
Any idea? I can't use window since it is a node.js problem, the script is not executed in the browser.

You're completely misunderstanding call().
call() lets you call a method with a different this.
You want to get as property by name:
object[methodName](arg1, arg, ...);

You actually can achieve this. You first would need to get the scope, where your namespace/function/object is defined.
For example, in your code I would assume its window.
So, a little modification of your code would produce the desired result:
var User = {};
User.find = function(x){
alert(x);
}
var input = 'User.find';
var some_data_array = {name: 'John Doe'};
var method = input.toString().split('.');
var nameObj = global[method[0]];
var methodToCall = method[1];
nameObj[methodToCall](some_data_array.name);
Mark the use of global[]. This is where it starts.
[edited] * Modified code to use global instead of window as being used in nodejs.

What you want to do is access the global object.
This should work:
var User = {};
User.find = function(){
return 1;
}
var input = 'User.find';
var some_data_array = {name: 'John Doe'};
var method = input.toString().split('.');
var nameObj = method[0].substring(0,1).toUpperCase() +method[0].substring(1);
var methodToCall = method[1];
"use strict";
var global = (1,eval)("this");
alert(global[nameObj][methodToCall](some_data_array));

There are a couple of ways to get the desired result. In this case it is very simple to just use an anonymous function, as follows, passing in the string parameter contained in objName.name:
var f = function(oName){
if (oName == 'John Doe') {
return 1;
}
};
var objName = {name: 'John Doe'};
console.log( f( objName.name ) );
If the OP prefers to use an object's call method, the following code also will suffice:
var myObj = {
myFunc: function() {
if (this.name == 'John Doe') {
return 1;
}
}
}
var objName = {
name: 'John Doe'
};
console.log(myObj.myFunc.call(objName));
A couple of things to note. myObj calls its method myFunc() passing in the object parameter objName. The purpose of this in the method is to read the property name, possessing that ability because of being bound to the object objName.

Related

Can't access a object property of an array element, inside a object in javascript

I have an array of objects inside an object.
Why do I get the error 'Cannot read property fname of undefined' below? How do I fix it?
Also, I'm new to javascript and would appreciate any styling or convention suggestions.
https://jsfiddle.net/go1eu9z6/
function myObj() {
this.X = [],
this.haveEntries = function() {
return this.A.length > 0;
},
this.add = function(fname_in, lname_in, city_in) {
var t = new Date().getTime() / 1000;
var obj = {
fname: fname_in,
lname: lname_in,
city: city_in,
t_create: t
};
this.X.push(obj);
return this;
}
}
var AB = {};
AB.X = new myObj();
AB.X.add("mike", 'smith', 'Toronto');
var s = AB.X[0].fname; // Error: Cannot read property fname of undefined
document.getElementById('fname').innerHTML = s
There were a couple problems with your javascript code. Firstly, you were assigning AB.X a value of new Obj() instead of simply AB. Then, you were calling the add method on AB.X, when the correct call would simply be AB.add - see code example below
function myObj() {
this.X = [],
this.haveEntries = function() {
return this.A.length > 0;
},
this.add = function(fname_in, lname_in, city_in) {
var t = new Date().getTime() / 1000;
var obj = {
fname: fname_in,
lname: lname_in,
city: city_in,
t_create: t
};
this.X.push(obj);
return this;
}
}
var AB = {};
AB = new myObj();
AB.add("mike", 'smith', 'Toronto');
var s = AB.X[0].fname;
with you existing code you can also access it by using
var s = AB.X.X[0].fname;
You don't need to make X an instance of myObj. In fact, that's what's breaking your code. X is created when you make a new instance of myObject, like this:
var AB = new myObject();
Then you would be calling your add() method on this AB object, not on X:
AB.add('mike','smith','toronto');
Finally, you would get your results like this:
var firstName = AB.X[0].fname;
This is a little round-about why of constructing this object, though. How do you know which person's name and info occur at which index of X? Maybe look into using an object to store this data with some key that helps you know where stuff is. Might look like:
var people = {
"uniqueID": { // some uniqueID
"fname":"mike",
"lname":"smith",
"city":"toronto"
}
}

Class inside a namespace

I'd like my code to look something like this, but I don't know how to make that happen.
var employee = namespace.employee(2); // return object
var employeeName = namespace.employee(2).name; // return string
var name = employee.name;
I was thinking of creating a namespace like this
var namespace = (function(){
})();
And then putting like classes inside it, I just don't quite know where to start.
This is all you need to implement that behaviour:
var namespace = {};
namespace.employee = function (num) {
return { name: num };
};
The function employee on the object namespace which returns an object containing the key name.
Assuming you want something like a repository of employees and the num tells it which employee to return:
var namespace = {};
var employees = [{ name: 'Joe' }, ...];
namespace.employee = function (num) {
return employees[num];
};
Assuming you want the employees repository to not be globally accessible:
var namespace = {};
namespace.employee = (function () {
var employees = [{ name: 'Joe' }, ...];
return function (num) {
return employees[num];
};
})();

How to create a method call from a string without using eval

Can anyone advise me how to create a method call using a string and without using eval? Please note that methodCall cannot be hard-coded and has to be dynamic. methodCall will be created dynamically.
In the following example this refers to a Backbone view, e.g.
var amount = this.model.get('amount');
var $amount = this.$el.find('.amount');
var methodCall = 'this.parentController.parentController.addBinding';
//then need to call the method with args
methodCall('amount',$amount);
I first thought this would work:
this['controller']['parentController']['view']['addBinding'](amount, $amount);
However I came to realise that this would not be dynamic either. Does anyone have a solution?
As noted in this answer, Multiple level attribute retrieval using array notation from a JSON object, you can traverse the hierarchy of objects with something like this:
function findprop(obj, path) {
var args = path.split('.'), i, l;
for (i=0, l=args.length; i<l; i++) {
if (!obj.hasOwnProperty(args[i]))
return;
obj = obj[args[i]];
}
return obj;
}
You could then give your view/model/collection a method to apply an arbitrary path:
var V = Backbone.View.extend({
dyncall: function(path) {
var f = findprop(this, path);
if (_.isFunction(f))
return f.call(this, 'your args');
}
});
var v = new V();
v.dyncall('parentController.parentController.addBinding');
And a demo http://jsfiddle.net/nikoshr/RweEC/
A little more flexibility on passing the arguments :
var V = Backbone.View.extend({
dyncall: function() {
var f = findprop(this, arguments[0]);
if (_.isFunction(f))
f.apply(this, _.rest(arguments, 1));
}
});
var v = new V();
v.dyncall('parentController.parentController.addBinding', 'your arguments', 'another arg');
http://jsfiddle.net/nikoshr/RweEC/1/

Preserve function property when created with "bind"

I have a function that looks like this:
var tempFun = function() {
return 'something';
}
tempFun.priority = 100;
Now I'm pushing it to an array and binding another object to it in the process like this:
var funArray = [];
var newObj = {};
funArray.push( tempFun.bind(newObj) );
and after this, I would like to acces the function's property like this:
funArray[0].priority
but it returns undefined. Is there some way to preserve the property on the function while binding a new object to it?
No, but you could write a function to do this yourself;
Function.prototype.bindAndCopy = function () {
var ret = this.bind.apply(this, arguments);
for (var x in this) {
if (this.hasOwnProperty(x)) {
ret[x] = this[x];
}
}
return ret;
};
... which you could then use via;
var funArray = [];
var newObj = {};
funArray.push( tempFun.bindAndCopy(newObj) );
No. Bind returns a new function, which "wraps" around the original one. All you can do is copy the properties on this new function:
var boundFun = tempFun.bind(newObj)
boundFun.priority = tempFun.priority;
funArray.push( boundFun );
If you want the properties to be in sync (changes in one visible on the other) you can do:
Object.defineProperty(boundFun, 'priority', {
get : function () { return tempFun.priority; },
set : function (val) { tempFun.priority = val; }
});
From MDN:
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
Hence, .bind() won't be useful for what you're trying to achieve. Besides using jQuery mappers or rewriting your code to use .prototype, a solution that I can think of is:
var obj = {};
for (var i in tempFun) {
if (tempFun.hasOwnProperty(i)) obj[i] = tempFun[i];
}

trying to implement an array within an array of of objects

In Google Apps JS. I would like to implement an array of objects, each with properties and methods. One of the properties needs to be an array of objects and I would like to be able to access this array by using methods in the parent array.
So far my best efforts is:
function myFunction () {
var teamNo = 3;
var allNames =["n1","n2","n3","n4"] ;
var createnames = function () {
var names = [];
for ( var j = 0; j <=3 ; j ++) {
(function (j) {
var localNames = ["local1-names"+j,"local2-names"+j];
names[j] = (function (player){
return {
namArr: localNames,
name: allNames[j],
addName: (function (player){
localNames.push(player);
}) (player),
team: teamNo
};
});
}) (j);
}
return names;
}
var myname = createnames();
var foo = myname[0]().namArr;
var foo1 = myname[1]().namArr;
myname[1]().addName("added");
var foo2 = myname[1]().namArr;
var foo3 = myname[2]().namArr;
var debug = true;
}
As soo as I add the code to implement the sub array I get a runtime error saying that addName does not exist.
You're invoking this immediately:
addName: (function (player) {
localNames.push(player);
})(player)
instead of assigning it:
addName: function (player) {
localNames.push(player);
}
Also, each names[] function takes a player, and so does the addPlayer() function, making the names[] parameter unreachable. If you're not going to pass anything to the names[] functions, then remove the parameter.
And I'd suggest using named functions instead of inlined IIFEs.

Categories