How to call method if I have method name as string - javascript

My requirement is on phase change I have to construct method names and call the methods and subsequent methods also,here I am able to construct method name but it is as String and not able to call the method. I have followed some of the suggestion given but I couldn't achieve. Please help.
var previousPhase = $("#currentPhase").val();
var projectPhaseArray = ["requirement", "design", "construction", "testing", "release"];
var i = 0;
$("#currentPhase").change(function() {
alert(previousPhase);
i=projectPhaseArray.indexOf(previousPhase);
for (i; i < projectPhaseArray.length; i++) {
alert(projectPhaseArray[i]);
var phaseTimeLineToCall =
projectPhaseArray[i].concat("PhasePhaseTimeLines");
executeFunctionByName(phaseTimeLineToCall,window);
}
});
function executeFunctionByName(functionName, context /*, args */) {
return context[functionName].apply(context);
}
function requirementPhaseTimeLines(){
alert("In RequirementPhaseTimelines");
}
function designPhaseTimeLines(){
alert("In DesignPhaseTimelines");
}
Thanks.

Strings don't mutate, so you need to save the value back
projectPhaseArray[i] = projectPhaseArray[i].concat("PhasePhaseTimeLines");

You could use a javascript object where you store the function name as key and the function reference as value
var lookup = {
requirementPhaseTimeLines: requirementPhaseTimeLines,
designPhaseTimeLines: designPhaseTimeLines
}
Without Arguments
We have to modify executeFunctionByName slightly
function executeFunctionByName(functionName, lookup) {
return lookup[functionName]()
}
Working Example
function requirementPhaseTimeLines() {
return "In RequirementPhaseTimelines"
}
function designPhaseTimeLines() {
return "In DesignPhaseTimelines"
}
var lookup = {
requirementPhaseTimeLines: requirementPhaseTimeLines,
designPhaseTimeLines: designPhaseTimeLines
}
function executeFunctionByName(functionName, lookup) {
return lookup[functionName]()
}
console.log(
executeFunctionByName("requirementPhaseTimeLines", functions)
)
console.log(
executeFunctionByName("designPhaseTimeLines", functions)
)
With Arguments
If we want to pass in arguments we have to curry the functions we want to let execute.
function greet(word) {
return function(name) {
return word + ', ' + name + '.'
}
}
Second, we have to create a function where we can iterate trough the arguments and set each arguement value to the function we want to execute:
function setArguments(functionRef, args) {
return args.length === 1
? functionRef
: setArguments(functionRef(args[0]), args.slice(1))
}
Working Example
function greet(word) {
return function(name) {
return word + ', ' + name + '.'
}
}
var lookup = {
greet: greet
}
function getFunction(lookup, name) {
return lookup[name] || new Function()
}
function setArguments(functionRef, args) {
return args.length === 1
? functionRef
: setArguments(functionRef(args[0]), args.slice(1))
}
function executeFunctionByName(functionName, lookup, args) {
var functionRef = getFunction(lookup, functionName)
var functionRefWithArgs = setArguments(functionRef, args)
return functionRefWithArgs(args[args.length - 1])
}
console.log(
executeFunctionByName('greet', lookup, ['Hi', 'Jon'])
)

Related

How to convert stringArray to function pointer collection for librarySystem

Currently I'm writing a librarySystem
And there's an stringArray ['greet','name'] as dependencies in the code example below, however I need to use this stringArray as an array of functions passing into
greetingToName() by using apply(), is there any way to convert the stringArray into An array of functions ?
function greet(){
return 'hi!';
}
function name(){
return 'name';
}
function greetingToName (greet, name) {
console.log( greet() + ' ' + name() );
}
var stringArray = ['greet','name'];
greetingToName.apply(null, stringArray); // this is not working properly , because it passes 'greet' and 'name' as string into function, instead of function pointers.
You can create an Object with those 2 functions, and pass the strings as you wanted, and then look inside the object by name.
const posiableFuncs = {
greet: () => {
return 'hi!';
},
name: () => {
return 'name';
}
};
function greetingToName(greet, name) {
console.log(posiableFuncs[greet]() + ' ' + posiableFuncs[name]());
}
const stringArray = ['greet', 'name'];
greetingToName.apply(null, stringArray);
Creating function variables and assigning them in array would do the trick.
something as below.
var g = function(){
return 'hi!';
}
var n = function(){
return 'name';
}
function greetingToName (greet, name) {
console.log( greet() + ' ' + name() );
}
var stringArray = [g,n];
Fiddle
https://jsfiddle.net/dk_dragonknight/yhL188se/
After i've read everybody's solutions here, I got huge inspiration, therefor I came up with this solution without changing the function context , what do you guys think ?
var posiableFuncs = {
greet : function(){
return 'hi! ';
},
name : function(){
return 'Mr. Awesome!';
}
}
function greetingToName (greet, name) {
console.log( greet() + ' ' + name() );
}
var stringArray = ['greet','name'];
// copy the functions and assign into arrayOfFunctions variable based on stringArray
var arrayOfFunctions = stringArray.map(function(functionName){
return posiableFuncs[functionName];
});
greetingToName.apply(window, arrayOfFunctions);

Return functions recursively to form nested functions - Javascript

I'm trying to build a function in JS that has a return composed of different nested functions based on a parameter passed by the user.
function addA(otherFunction)
{
//gets the result from some base function and modifies it
//e.g. +1
}
function addB(otherFunction)
{
//does the same thing as addA, except different values. Consider it a variation of addA.
//eg. -1
}
function constr(input)
{
//based on the chars in input, we will recursively select a new function to be applied.
//the value of this function should be a function
if (...) return addA(constr(shorterInput))
if (*last char) return addA
if (*last char) return addB
if (...) return addB(constr(shorterInput))
}
So far, my script is recognizing addA and and addB as functions. But when it strings two functions together, for example
addB(addA)
The type becomes undefined. Can anybody let me know why it does not register as a function and/or the proper way to return nested functions. Thanks!
Edit: Here is the real code:
function cons(a,b)
{
return function (selector) {
return selector(a,b);
};
}
function a(list)
{
function aHelper(a,b)
{
return a
}
return list(aHelper);
}
function d(list)
{
function dHelper(a,b)
{
return b
}
return list(dHelper);
}
function abc(input)
{
if (input.length==0 || input==null) return null;
var x=input.charAt(input.length-1);
if (x==='a')
{
if (input.length>1)
{
var z=a(abc(input.substr(0,input.length-1)));
return z;
}
return a;
}
if (x==='d')
{
if (input.length>1)
{
var z=d(abc(input.substr(0,input.length-1)));
return z;
}
return d;
}
}
function show(list) {
var sval;
if (list == null) return '()';
else if (typeof list!='string')
{
sval = '(' + show(a(list)) + ' ' + show(d(list)) + ')';
}
else
{
sval=list;
}
return sval;
}
var func=abc('ad');
var func2=abc('a');
var list=cons('a',cons('b','c'));
console.log(typeof func);
console.log(typeof func2);
console.log(typeof list);
console.log(typeof func2(list));
console.log(typeof func(list));
Your function abc is supposed to return a function that can process lists, like a or d. However, you match that signature only in 2 out of 7 cases:
return a, return d are fine
return null - that's not a callable value
z = d(…); return z does return a list
z = a(…); return a does return an element of the list (of whatever type)
d(abc(…)) and a(abc(…)) use abc as if it would return a list
A correct implementation would look like this:
function abc(directions) {
if (directions.length == 0) {
return function id(list) { return list; }; // a function that does nothing
}
var f = directions[0] == 'a' ? car : cdr; // ignoring other values, you might also throw an error
var processRest = abc(input.slice(1));
return function(list) { // make a function to process a list
var z = f(list); // do the current operation
return processRest(z); // do the rest of operations
};
}
Or even better/shorter with the help of higher-order function composition:
function id(x) { return x; }
function compose(f, g) {
if (f == id) return g;
if (g == id) return f;
return function(x) { return f(g(x)); };
}
function abc(dirs) {
return !dirs.length ? id : compose(abc(dirs.slice(1)), dirs[0]=='a'?car:cdr);
}

What does jQuery's $.each with three arguments do, and how can I translate it to pure JS?

I'm trying to remove JQuery from a project I inherited and I have stumbled upon this line of code which doesn't really make sense.
$.each(options.reservationOptions,this._addToSelect, [select]);
What does $.each() do when there are 3 things passed to it.
The first is an object, the second is a function, and the third is a var.
Here is the [select] init:
var select = L.DomUtil.create('select', 'booking-select ' + options.RoomName, reservationContainer);
Here is the function:
_addToSelect: function (select) {
try {
var value = this.value;
var text = this.text;
if (text) {
var option = $("<option>").addClass('booking-option').text(text);
//var option = L.DomUtil.create('option', 'booking-option');
//option.innerText = text;
if ( value )
option.val(value);
//option.value = value;
option.appendTo(select);
//select.appendChild(option.get());
//var optionsList = select.options || select;
//optionsList.add(option.get());
}
} catch (ex) {
console.log('could not add option to select ' + ex.message);
}
It iterates, the first argument is the array or object, the second is the callback, and the third is the arguments passed in to the callback. In a loop you'd do the same thing with (assuming array)
options.reservationOptions.forEach(function(item) {
this._addToSelect.apply(item, [select]);
}.bind(this));
Here's a short version of what jQuery does
$.each = function (obj, callback, args) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike(obj);
if (args) {
if (isArray) {
for (; i < length; i++) {
value = callback.apply(obj[i], args);
if (value === false) {
break;
}
}
} else {
for (i in obj) {
value = callback.apply(obj[i], args);
if (value === false) {
break;
}
}
}
}
}
forEach takes 2 arguments, callback and context – https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Obiekty/Array/forEach
options.reservationOptions.forEach(function(option) {
this.options.reservationOptions(option);
}, this);

Get object caller name by function call JavaScript

I'm writing a piece of code to easily save error logs in an object for debugging.
What I'm trying to achieve is to get the Object name from the function it was called from like so:
var MainObject = {
test : function() {
return MainObject.test.caller;
// When called from MainObject.testcaller,
// it should return MainObject.testcaller.
},
testcaller : function() {
return MainObject.test(); // Should return MainObject.testcaller, Returns own function code.
},
anothercaller : function() {
return MainObject.test(); // Should return MainObject.anothercaller, Returns own function code.
}
}
However when I run this code it returns the function code from MainObject.testcaller.
JSFiddle example
Is there any way this is possible?
Update
After looking at Rhumborl's answer, I discovered that assigning the value through another function would lead it to point back at the function name without the object itself.
Code:
(function (name, func) {
MainObject[name] = func;
})('invalid', function() {
return MainObject.test("blah");
});
// This now points at invalid() rather than MainObject.invalid()
Updated fiddle
There is a non–standard caller property of functions that returns the caller function, however that is a pointer to a function object and doesn't tell you the object it was called as a method of, or the object's name. You can get a reference to the function through arguments.callee.
There is also the obsolete arguments.caller, but don't use that. It also provides a reference to the calling function (where supported).
Once you have a reference to the calling function (if there is one), you then have the issue of resolving its name. Given that Functions are Objects, and objects can be referenced by multiple properties and variables, the concept of a function having a particular name is alluvial.
However, if you know that the function is a property of some object, you can iterate over the object's own enumerable properties to find out which one it is.
But that seems to be a rather odd thing to do. What are you actually trying to do? You may be trying to solve a problem that can be worked around in a much more robust and simpler way.
Edit
You can do what you want in a very limited way using the method described above for the case in the OP, however it is not robust or a general solution:
var mainObject = {
test : function() {
var obj = this;
var caller = arguments.callee.caller;
var global = (function(){return this}());
var fnName, objName;
for (var p in global) {
if (global[p] === obj) {
objName = p;
}
}
for (var f in obj) {
if (obj[f] === caller) {
fnName = f;
}
}
return objName + '.' + fnName;
},
testcaller : function() {
return mainObject.test();
},
anothercaller : function() {
return mainObject.test();
}
}
console.log(mainObject.testcaller()); // mainObject.testcaller
console.log(mainObject.anothercaller()); // mainObject.anothercaller
but it's brittle:
var a = mainObject.anothercaller;
console.log(a()); // mainObject.anothercaller
var b = {
foo : mainObject.anothercaller
}
console.log(b.foo()); // mainObject.anothercaller
Oops.
You can use this trick at http://www.eriwen.com/javascript/js-stack-trace/ which throws an error, then parses the stack trace.
I have updated it for the latest versions of Firefox, Chrome and IE. Unfortunately it doesn't work well on my IE9 (and I haven't tested it on Opera).
function getStackTrace() {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox/Chrome/IE11
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
var line = lines[i].trim();
if (line.match(/^at [A-Za-z0-9\.\-_\$]+\s*\(/)) {
// Chrome/IE: " at Object.MainObject.testcaller (url:line:char)"
var entry = line.substring(3, line.indexOf('(') - 1);
// Chrome appends "Object." to the front of the object functions, so strip it off
if (entry.indexOf("Object.") == 0) {
entry = entry.substr(7, entry.length);
}
callstack.push(entry);
} else if (line.match(/^[A-Za-z0-9\.\-_\$]+\s*#/)) {
// Firefox: "MainObject.testcaller#url:line:char"
callstack.push(line.substring(0, lines[i].indexOf('#')));
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
} else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to getStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE9 and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf('')) || 'anonymous';
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
return callstack;
}
var MainObject = {
test: function (x) {
// first entry is the current function (test), second entry is the caller
var stackTrace = getStackTrace();
var caller = stackTrace[1];
return caller + "()";
},
testcaller: function () {
return MainObject.test(1, null);
}
}
function SomeFunction() {
return MainObject.test("blah");
}
document.body.innerHTML += '<b style="color: red">' + MainObject.testcaller() + '</b>';
document.body.innerHTML += '<div>Calling SomeFunction() returns: <b style="color: red">' + SomeFunction() + '</b></div>';
MainObject.test() should return: <b style="color: blue">MainObject.testcaller()</b>
<hr />
MainObject.test() returns:
Updated fiddle here

Assigning a variable name from within a string with a function

I'm keeping a record of each time various functions are called. I have a function called
record_activity( function_name );
I really don't want to have to write this at the top of every function I want to track. Currently there are lots of functions in the format:
Object.Key.Func = function() { ... }
I've written this, which seems to work but I'm really not sure about it's implications:
function sub ( variable, func ) {
var temp_func = function ( args ) {
record_activity( variable );
return func.apply(this,arguments);
}
eval( variable + ' = ' + temp_func );
}
sub( 'Object.Key.Func', function (name) { alert('hi ' + name) } );
Object.Key.Func('test');
If there is a way of doing this without an eval I'd be much happier.
Thanks
I think you can create a wrapper func for each func that you want to track. Hope the following code will help you:
var counter = {};
// assume this is what record_activity has to do.
function record_activity (f, func_name, args) {
counter[func_name] = counter[func_name]===undefined ? 1 : counter[func_name]+1;
}
// assume that you want to keep track of functional call obj.foo and obj.bar
var obj = {
a: 3,
foo: function () {
console.log("foo");
},
bar: function (b) {
console.log("bar", this.a, b);
}
};
function create_func (f, func_name) {
return function () {
record_activity(f, func_name, arguments);
f.apply(obj, arguments);
};
}
for(var prop in obj) {
if (typeof obj[prop] === 'function') {
// create the wrapper func
obj[prop] = create_func(obj[prop], prop);
}
};
// test
for (var i = 0; i < 3; i++) {
obj.foo();
};
for (var i = 0; i < 10; i++) {
obj.bar(i);
};
console.log(counter);

Categories