a function (which requires parameters) as arguments of another function - javascript

I have been using functions as parameters. Now I need to pass a function A which requires parameters x generated by function B. I can do that too. by calling A in B with the parameters.
But my problem is, my function B accepts any kind of function, and it is not fixed. It may take function C also which requires parameter y or some function D that does not need any parameter.
Is this possible?
function B(done_function){
//some task generate some value
done_function();
}
function B(done_function){
//some task generate some value including args
done_function(args);
}
How can I make A, C and D functions execute with their arguments.
The top two examples won't work.

The normal way to handle this is to ignore it. Function B should simply not care about how other functions accept arguments. Instead it should only provide a standard and well documented interface to it's callback:
function B (done_function) {
// do some stuff to generate result
done_function(result);
}
Or if function B can possibly generate errors asynchronously then it should do done_function(err, result). Notice that all libraries do this. They don't care how you write your functions.
Now, how to pass various types of functions to B? Just wrap them around another function. For example, say you need to pass the result of B to a logger function and you need to pass a variable specifying the name of the file to log to. Just do this:
B(function(result) {
logToFile(debugLogFile, result);
});
Say for example you need to modify the result because the function you want to pass it to expect it to be in a specific format. Just do something like this:
B(function(result) {
var x = {
some_parameter: something,
result: result
};
doSomethingElse(x);
});
There is no scenario where function B needs to be aware of how you want to process the result it generates. It's you, the programmer, who is responsible to convert the result of function B appropriately before doing further processing.

You can make use of the functions call method:
function B(done_function){
//some task generate some value including args
done_function.call(done_function, args);
}
example jsfiddle

Let B call the callback with a single object as argument, which contains all information:
function B(done_function){
//some task generating some values, including args, for example:
var args = {
status: 3,
code: 'DEC',
location: 'Atlantic',
date: new Date('2017-01-01')
}
done_function(args);
}
Using ES6 destructuring in function parameters, you can filter out the information you need:
Function A could look like this:
function A({status}) {
console.log('status is ' + status);
}
B(A);
In the same way, C could look like this:
function C({code, date}) {
console.log('code is ' + code + ' on ' + date);
}
B(C);
Of course, ES6 destructuring is just a nice shortcut syntax, as you can do it also like this:
function A(response) {
console.log('status is ' + response.status);
}
B(A);
Alternative: use function's length property
If the distinction between different kinds of callbacks can be made on the basis of the number of parameters that are defined for them, then you could use the length property like this:
function B(done) {
var code = 'DEC';
var status = 1;
var location = 'Atlantic';
var date = new Date('2017-01-01');
switch (done.length) {
case 1:
done(status);
break;
case 2:
done(location, date);
break;
default:
done(code, status, location, date);
}
}
function A(status) {
console.log('status = ' + status);
}
function C(location, date) {
console.log('location = ' + location + ' on ' + date.toDateString());
}
B(A);
B(C);
Note the specific rules that apply for the length property's value.

Related

Create a function that takes an object and a key as input - return value for key within the object

Just learning JS, came across this question on coursera. I can't even begin to answer this question; I'm not certain what it's asking. Sorry for the ignorance. Just looking for the basic format. I can write a function that takes input, but not certain how to do this. I've spent a while researching objects and it's not quite sinking in yet. Thanks!
You need to use multiple parameters. You need to separate the parameters with commas like:
function func(param1, param2) {
console.log("Parameter 1: " + param1);
console.log("Parameter 2: " + param2);
}
func(1, 2);
// Console:
// Parameter 1: 1
// Parameter 2: 2
In actual code this would look like the following:
function getValue(object, key) {
if (object.hasOwnProperty(key)) {
// Object.prototype.hasOwnProperty(key) returns if the specified object has the specified key in it
return object[key];
} else {
// The else statement is not really needed as the function already returned
// if the object has the specified key
return null;
// If you need this function for something specific, then you should return a default value
}
}
clean and clear:
function f(obj,keyname){
return obj[keyname];
}
Usage: f(myObj,my_key_field_name)
or simply obj[keyname] if you do not like to write a function

Javascript function call like somefunction("Hi")("Hello")("How")

Please have a look at this code. I need to show a alert message "mikä on elämän tarkoitus?" using this code
window["mikä"]("on")("elämän")("tarkoitus")("?");
I need to write a function or piece of code that will show that alert message when I will execute that code.
I have written a function like this:
window["mikä"] = function(str){
alert(str);
}
which works when I call window"mikä" but if I add more like below in console I see a type error.
window["mikä"]("on")("Hello")("How");
My question is would it be valid way to call like below as there is multiple function signs?
window["mikä"]("on")("elämän")("tarkoitus")("?")
To achieve the functionality you are looking for one way is to write a function which returns a function which returns a function as the others mentioned. That works fine if the number of functions is known before hand. Another way is to use a functional programming technique called currying, which is
the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application).
You can write your own curry function like this:
function curry(func, args_) {
var self = this;
self.args = args_ || [];
return function() {
var extended_args = [].concat(self.args).concat(Array.slice(arguments));
if(extended_args.length >= func.length)
return func.apply(this, extended_args);
return new curry(func, extended_args);
};
}
var funcName = "mikä";
window[funcName] = curry(functionstr1, str2, str3, str4) {
alert(funcName + ' ' + str1 + ' ' + str2 + ' ' + str3 + str4);
});
window["mikä"]("on")("elämän")("tarkoitus")("?");
Here are some resources which can help you if you are interested in learning more about currying / functional programming in JS.
http://kukuruku.co/hub/javascript/an-interesting-task-for-an-interview-currying-and-partial-applicationof-a-function
http://tech.pro/tutorial/2011/functional-javascript-part-4-function-currying
Reginald Braithwaite's talk in NDC Oslo
You want the return value to be a function as well, so that additional calls on the return value will call the same function. Just add this
window["mikä"] = function(str){
alert(str);
return window["mikä"];
}
EDIT: Misread your question, this will make multiple alert messages. Sorry.
You probably want to nest the function calls
window["mikä"] = function(s1){
return function(s2) {
return function(s3) {
alert(s1 + ' ' + s2 + ' ' + s3);
}
}
}
window["mikä"]("on")("elämän")("tarkoitus")("?");
As for getting the function name inside the function, there's really no good way to do that, and it should be avoided.

how to add an argument to a method stored in an array that is called later

This is a follow-up to this question (although this is self-contained) trying to `call` three methods but not working correctly with jQuery map.
I am trying to store a set of methods in an array but there is a set that might have arguments like below (the initial methods are in before_methods and the proposed methods are in lm_methods). I'm sure it's pretty self explanatory what I want but I'd like to be able to merge in the arguments into a reasonable call to f (specifically the arc.pLikedByTerm). I currently have the following:
// signature
pLikedByTerm:function(term, ne, sw, m){
....
}
// code before_methods just to show
this.before_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems];
this.lm_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems, arc.pLikedByTerm('surfing'),arc.pLikedByTerm('sailing')];
$.each(this.lm_methods, function(i,f){
f(ne,sw,m);
});
How would I do this or is this bad design? What would be the idiomatic way? My brain is fried.
thx in advance
Update 1
Playing around with answer below, it looks like this works which might the simplest things:
var fns=[logStuff("this is msg"), logMoreArgs("a term","a you msg")];
for (var i=0; i<fns.length; i++) {
fns[i];
}
Having an array of functions is common practice when used often. For example, consider this Callback class.
function Callback(){
this.callbacks = [];
}
Callback.prototype.run = function(cb) {
for (var i=0; i<this.callbacks.length; i++) {
this.callbacks[i]();
}
};
We can then add some callbacks.
function logStuff(msg) {
jsprint(msg || "No message");
}
obj = new Callback();
obj.callbacks.push(logStuff);
obj.callbacks.push(logStuff);
obj.run();
If we run this we see that it's only logging our default value. So if we want to bind some data, we can use the bind function.
Function.prototype.bind
thisArg
The value to be passed as the this parameter to the target
function when the bound function is called. The value is ignored if
the bound function is constructed using the new operator.
arg1, arg2, ...
Arguments to prepend to arguments provided to the bound function
when invoking the target function.
Our new code sets the first parameter to different strings, which we then see. You can bind any number of parameters.
obj = new Callback();
obj.callbacks.push(logStuff.bind(null, "My message"));
obj.callbacks.push(logStuff.bind(null, "My other message"));
obj.run();
end result
The way you are doing would work just ok. Just remove the arguments and parens:
Instead of:
this.lm_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems,
arc.pLikedByTerm('surfing'),arc.pLikedByTerm('sailing')];
Do:
this.lm_methods=[arc.pLocations,arc.pLikedLocations,arc.pLikedItems,
arc.pLikedByTerm,arc.pLikedByTerm];
Example:
function say(txt) {
console.log("say" + txt);
}
function shout(txt) {
console.log("shout" + txt);
}
function whisper(txt) {
console.log("whisper" + txt);
}
var funcArr = [say, shout, whisper];
$.each(funcArr, function(i, f) {
f("hello");
});
would print:
sayhello
shouthello
whisperhello

Extend $.mobile.changePage to accept more options?

I would like to extend $.mobile.changePage to accept more options such as adding a callback function for when the page finishes loading as well as more options for the AJAX call like contentType. Is there a way to do this without changing the source code? If not, I am willing to change the source code for educational purposes, but could not find it in the jQuery Mobile GitHub: https://github.com/jquery/jquery-mobile . Thanks for any helps or guidance.
One of the more exciting parts of JavaScript is that ability to redefine any function using a technique which is commonly referred to as Monkey Patching. (as an aside ES5 provides a new freeze method which allows developers to prevent such modifications.)
Here's an example of a JavaScript MonkeyPatch which allows us to modify the behaviour of a function without editing it's source:
// A namespace object.
var Example = {};
// Sums two values.
Example.sum = function (a, b) {
return a + b;
}
// Usage:
var result = Example.sum(1, 2);
Say we wanted to add logging to the sum method, we could just add a console.log line to the function, but we can also monkey patch it:
// Store a reference to the current 'Example.sum' function.
var originalSum = Example.sum;
// Now redeclare Example.sum...
Example.sum = function (a, b) {
// Call the originalSum function first...
var result = originalSum(a, b);
// Now add some logging...
console.log("Example.sum(" + a + ", " + b + ") yields " + result);
return result;
};
Now when Example.sum is called, not only will we get the result as before, but a console message will also be written. With this in mind, you can monkey patch the $.mobile.changePage method in the same way:
var originalChangePage = $.mobile.changePage;
// Redefine `changePage` so it accepts a 'complete' function in the options
// object which will be invoked when the page change is complete.
$.mobile.changePage = function (to, options) {
if (typeof options.complete === "function") {
$(body).one("pagechange", function (event) {
options.complete(event);
});
}
originalChangePage(to, options);
};

Force missing parameters in JavaScript

When you call a function in JavaScript and you miss to pass some parameter, nothing happens.
This makes the code harder to debug, so I would like to change that behavior.
I've seen
How best to determine if an argument is not sent to the JavaScript function
but I want a solution with a constant number of typed lines of code; not typing extra code for each function.
I've thought about automatically prefixing the code of all functions with that code, by modifying the constructor of the ("first-class") Function object.
Inspired by
Changing constructor in JavaScript
I've first tested whether I can change the constructor of the Function object, like this:
function Function2 () {
this.color = "white";
}
Function.prototype = new Function2();
f = new Function();
alert(f.color);
But it alerts "undefined" instead of "white", so it is not working, so I've don't further explored this technique.
Do you know any solution for this problem at any level? Hacking the guts of JavaScript would be OK but any other practical tip on how to find missing arguments would be OK as well.
If a function of yours requires certain arguments to be passed, you should check for those arguments specifically as part of the validation of the function.
Extending the Function object is not the best idea because many libraries rely on the behavior of defaulting arguments that are not passed (such as jQuery not passing anything to it's scoped undefined variable).
Two approaches I tend to use:
1) an argument is required for the function to work
var foo = function (requiredParam) {
if (typeof requiredParam === 'undefined') {
throw new Error('You must pass requiredParam to function Foo!');
}
// solve world hunger here
};
2) an argument not passed but can be defaulted to something (uses jQuery)
var foo = function (argumentObject) {
argumentObject = $.extend({
someArgument1: 'defaultValue1',
someArgument2: 'defaultValue2'
}, argumentObject || {});
// save the world from alien invaders here
};
As others have said, there are many reasons not to do this, but I know of a couple of ways, so I'll tell you how! For science!
This is the first, stolen from Gaby, give him an upvote! Here's a rough overview of how it works:
//example function
function thing(a, b, c) {
}
var functionPool = {} // create a variable to hold the original versions of the functions
for( var func in window ) // scan all items in window scope
{
if (typeof(window[func]) === 'function') // if item is a function
{
functionPool[func] = window[func]; // store the original to our global pool
(function(){ // create an closure to maintain function name
var functionName = func;
window[functionName] = function(){ // overwrite the function with our own version
var args = [].splice.call(arguments,0); // convert arguments to array
// do the logging before callling the method
if(functionPool[functionName].length > args.length)
throw "Not enough arguments for function " + functionName + " expected " + functionPool[functionName].length + " got " + args.length;
// call the original method but in the window scope, and return the results
return functionPool[functionName].apply(window, args );
// additional logging could take place here if we stored the return value ..
}
})();
}
}
thing(1,2 ,3); //fine
thing(1,2); //throws error
The second way:
Now there is another way to do this that I can't remember the details exactly, basically you overrride Function.prototype.call. But as it says in this question, this involves an infinite loop. So you need an untainted Function object to call, this is done by a trick of turning the variables into a string and then using eval to call the function in an untainted context! There's a really great snippet out the showing you how from the early days of the web, but alas I can't find it at the moment. There's a hack that's required to pass the variables properly and I think you may actually lose context, so it's pretty fragile.
Still, as stated, don't try and force javascript to do something against its nature, either trust your fellow programmers or supply defaults, as per all the other answers.
You can imitate something like Python’s decorators. This does require extra typing per function, though not extra lines.
function force(inner) {
return function() {
if (arguments.length === inner.length) {
return inner.apply(this, arguments);
} else {
throw "expected " + inner.length +
" arguments, got " + arguments.length;
}
}
}
var myFunc = force(function(foo, bar, baz) {
// ...
});
In general this sounds like a bad idea, because you’re basically messing with the language. Do you really forget to pass arguments that often?
You could use the decorator pattern. The following decorator allows you to specify minimum and maximum number of arguments that need to be passed and an optional error handler.
/* Wrap the function *f*, so that *error_callback* is called when the number
of passed arguments is not with range *nmin* to *nmax*. *error_callback*
may be ommited to make the wrapper just throw an error message.
The wrapped function is returned. */
function require_arguments(f, nmin, nmax, error_callback) {
if (!error_callback) {
error_callback = function(n, nmin, nmax) {
throw 'Expected arguments from ' + nmin + ' to ' + nmax + ' (' +
n + ' passed).';
}
}
function wrapper() {
var n_args = arguments.length;
console.log(n_args, nmin, nmax);
console.log((nmin <= 0) && (0 <= nmax));
if ((nmin <= n_args) && (n_args <= nmax)) {
return f.apply(this, arguments);
}
return error_callback(n_args, nmin, nmax);
}
for (e in f) {
wrapper[e] = f[e];
}
return wrapper;
}
var foo = require_arguments(function(a, b, c) {
/* .. */
}, 1, 3);
foo(1);
foo(1, 2);
foo(1, 2, 3);
foo(1, 2, 3, 4); // uncaught exception: Expected arguments from 1 to 3 (4 passed).
foo(); // uncaught exception: Expected arguments from 1 to 3 (0 passed).

Categories

Resources