Slice JS function's arguments - javascript

I want to write a common confirm method like
var confirmDelete = function (fun) {
if (confirm("Do you want to delete " + arguments[1])) {
$(arguments[2]).remove();
fun(arguments[3]);
}
return false;
}
It's working fine for fun with one parameter, but I want to suit for two or more parameters, how I can do it?

every javascript Function object has a method named apply. apply will call your function using the given context and the given arguments.
var confirmDelete=function(fun) {
if(confirm("Do you want to delete "+ arguments[1])) {
// remove the first two elements in arguments, and use the resulting array as a new set of
// arguments to fun
fun.apply(this, Array.slice(arguments, 2));
}
}

In JavaScript you can pass as many parameters as you want.
If you don't pass them, they will be undefined.
So... You can do something like this:
var confirmDelete = function(arg1, arg2, arg3) {
if (typeof arg2 === 'undefined') {
arg2 = "default value for arg2";
}
if (typeof arg3 === 'undefined') {
arg3 = "default value for arg3";
}
// do more stuff...
}
You can also read about the magical arguments variable here.

You can rewrite your function something like
var confirmDelete = function fun(arg1, arg2) {
// number of parameter as your need
// TODO some stuff
fun(arg1 -1, arg2 +3); // call it recursively
return false;
}

Related

Use function declaration passed as an argument

I am trying to create a stub definition that the code below could use:
func.calc({
'divide': function(num1, num2) {
// do something
},
'add': function(num1, num2, num3) {
// do something
}
});
So far, I've been having trouble getting the parameters passed to the functions in the second argument. Here's what I've been trying to do:
var func = {
calc: function(operationsArray) {
if (div) {
operationsArray[0](args); /* get args */
}
else (add) {
operationsArray[1](args); /* get args */
}
}
}
Is it possible to get the whole function definition (with parameters and implementation) when it is passed as a parameter like in the first snippet?
You're declaring func.temp as method that accepts an Array as second argument. On the other hand, you call the method with an Object as the second argument.
JavaScript specification tells us that field order is not guaranteed:
4.3.3 Object
An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.
meaning you shouldn't access in_func & in_func2 using location index (i.e. [0]).
You should either call the function with an array like this:
func.temp(mainArg, [
function(arg1, arg2) {
// do something
},
function(arg1, arg2, arg3) {
// do something
}
]);
OR, implement the function so that it uses explicit names:
var func = {
temp: function(arg1, func_obj) {
// do something with arg1
if (trigger1) {
func_obj.in_func(/* get params */);
}
else (trigger2) {
func_obj.in_func2(/* get params */);
}
}
}

ES6 default values not available in function.arguments?

If I have this ES6 function declaration and invocation:
function myFunction (arg1, arg2 = "bob") {
console.log("arguments", arguments);
}
myFunction(1);
...the console.log() statement shows only one argument with a value of "1". "bob" is nowhere to be seen. Is this expected and/or desired behavior? I would expect that default values would be available in the arguments object. If not, is there a way to dynamically get all arguments + defaults in some other manner?
Thanks in advance!
Yes, this is expected and desired. The arguments object is a list of the values that were passed into the function, nothing else.
It is not implicily linked to the parameter variables (that get assigned the default values), like it was in sloppy mode.
Is there a way to dynamically get all arguments + defaults in some other manner?
No. What parameters you have and whether they have default initialisers is static, you don't need to do anything here dynamically. You can do Object.assign([], arguments, [arg1, arg2]) for your example function.
As you know by now, there is no native method to get both "passed arguments AND defaults where arguments are not passed". But there is a workaround:
This function (that I found here) gets all parameters of a given function:
function getArgs(func) {
var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
return args.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
};
So, combining this function with the arguments of your function myFunction, we can get an array that has what you want:
function myFunction (arg1, arg2 = "bob") {
var thisArguments = arguments;
console.log(getArgs(myFunction, thisArguments));
};
function getArgs(func, argums) {
var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
var argsArray = args.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
for(var i = 0; i < argsArray.length; i++){
argsArray[i] += " (default)";
}
var defaults = argsArray.slice(argums.length);
argums = Array.prototype.slice.call(argums);
return argums.concat(defaults);
};
Now, we can see the information in the console calling myFunction:
1. Passing more arguments than parameters
This will return only the arguments.
myFunction("foo", "bar", "baz");
//returns: ["foo", "bar", "baz"]
2. Passing less arguments than parameters
Will return the arguments and the remainder parameters as default, as you want (I added "default" to each string).
myFunction("foo");
//returns ["foo", "arg2 = "bob" (default)"]
3. Passing no arguments
This will return all the parameters.
myFunction();
//returns ["arg1 (default)", "arg2 = "bob" (default)"]
This is the fiddle: https://jsfiddle.net/gerardofurtado/25jxrkm8/1/

JavaScript Object Reference

I've seen many questions for that context, but I still can't figure out, what exactly my Problem is. (I'm still experimenting with JavaScript, especially with objects)
Code:
function Field(val)
{ var value = val;
this.__defineGetter__("value", function(){ return value; });
this.__defineSetter__("value", function(val){ value = val; if(this.onchange) this.onchange.call(); });
}
function LW_makePlan()
{
/* [...] */
this.Filter1=new Field("");
this.Filter2=new Field("");
this.Filter3=new Field("");
this.init = function()
{
/* [...] */
this.Filter1.onchange=this.getSomething;
}
this.getSomething = function()
{
arg="modus=doWhat";
arg=arg+"&filter1=" + this.Filter1.value;
arg=arg+"&filter2=" + this.Filter2.value;
arg=arg+"&filter3=" + this.Filter3.value;
request_fkt(null, true, arg , this.setSomething);
}
this.setSomething = function(data)
{
alert(data);
}
this.init();
};
What I'm trying:
test = new LW_makePlan();
test.Filter1.value="anything";
test.Filter1 has an "onchange"-property, that is checked in the setter of "Field". if set, the setter will also call the object given within the onchange-property.
this works so far BUT it seems, that this call creates a whole new object-instance ... no not an instance, it is, as if the function "getSomething" is copied as a stand-alone function, because the Code i called, but for example this.Filter1 within the function "getSomething" is undefined ...
Why is this happening and how can I avoid this?
PS: I don't want to use some type of event-handling-Things from 3rd Party codes, I'd like to do it myself with a little help maybe.
EDIT:
Thanks to Steffen Heil, changed to:
var scope=this;
this.Filter1.onchange=function() { scope.getSomething(); };
and it works!
Your call to this.onchange is in Field, so you are calling a function of Field. The assignment this.Filter1.onchanged=this.getSomething kind of copies the method getSomething from LW_makePlan to Field, where it will be called.
So inside of getSomething that is now called onchanged the reference this referes to the Field not the LW_makePlan.
Replace the assignment with this:
var source = this;
this.Filter1.onchange = function() { return source.getSomething(); };
And it will work. Most frameworks have a bindmethod that makes this more readable (hiding the extra variable in a scope).
In reply to the first comment:
You can explicitly call a function like this:
x.call( object, arg1, arg2, ag3 );
x.apply( object, [ arg1, arg2, arg3 ] );
These the are the same and it does not matter what x is. Inside the called function this has the value of object.
x can be:
alert
window.alert
(function(){})
(alert)
(window.alert)
Normal calls to a function are shortcuts:
object.f = g;
object.f( arg1 ) => g.call( object, arg1 );
f( arg1 ) => f.call( window, arg1 );
While window is the global object in a browser; other environments may use another global object.
While the difference between these two shortcuts seems tivial, what about the following?
(object.f)( arg1 )
This is completely valid javascript, as object.f is a function and a function can be invoked using (args1). But:
object.f = g;
(object.f)( arg1 ) => g.call( window, arg1 )
So a.f = b.f; copies a member reference from a to b, but the this context, the code is executon on depends on the way f is called.
a.f(x) == a.f.call(a,x) == (a.f).call(a,x) == b.f.call(a,x) == (b.f).call(a,x)
b.f(x) == b.f.call(b,x) == (b.f).call(b,x) == a.f.call(b,x) == (a.f).call(b,x)
By the way, you can define your own bind very easily:
function bind( object, method ) {
return function() {
return object[ method ].apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, 'getSomething' );
This would match the fix I gave above using "late binding". Most libraries prefer "early binding":
function bind( object, method ) {
return function() {
return method.apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, this.getSomething );
The advantage is better performance, but the main difference is what happens, when getSomething changes after bind was called. The first implementation calls the new value, the second the old value.

Javascript dynamic callback functions

I am trying to design a function that will accept different functions as argument (callbacks). These functions can have different number of arguments. Is there a way I can execute the callback directly without bothering the number of arguments
For example :
function displayDialog(callBack)
{
callBack();
};
This function can be executed as follwing:
displayDialog(myFunction);
Where myFunction is
function myFunction(){}
or function `myFunction(a,b){}
Thank you all, for your answers. I tried using the apply and it worked. Here is what I did:
function caller()
{
var args = ['a', 'b'];
displayDialog(myFunction, args);
}
and in displayDialog function:
function displayDialog(callBack, args)
{
callBack.apply(null, args);
};
Normally, the arguments to a callback are dictated by the code that calls it. That is, your displayDialog function would document what arguments it gives to the callback.
If someone using displayDialog needs information in their callback that displayDialog doesn't provide, that's the caller's problem. They have a couple of different things they can do:
Make their callback close over the information they need
Use Function#bind or similar to curry the arguments into their function
Here's an example of #1:
// Documentation: The callback will be called with the
// number 42
function displayDialog(callback) {
setTimeout(function() {
callback(42); //
}, 35);
}
// A function using displayDialog
function useIt() {
var str = "I'm some information the callback needs";
displayDialog(function(arg) {
snippet.log("arg = " + arg);
snippet.log("str = " + str);
});
}
snippet.log("Calling useIt");
useIt();
snippet.log("useIt completed");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Here's an example of #2:
// Documentation: The callback will be called with the
// number 42
function displayDialog(callback) {
setTimeout(function() {
callback(42); //
}, 35);
}
// A function using displayDialog
function useIt() {
displayDialog(function(arg0, arg1) {
snippet.log("arg0 = " + arg0);
snippet.log("arg1 = " + arg1);
}.bind(null, "I'm a curried argument"));
}
snippet.log("Calling useIt");
useIt();
snippet.log("useIt completed");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
You can call a function, that have args set in the declaration, without supplying any arguments. The parameters will be undefined in the function though, so do a check if they are actually set before using them.
Example:
function callbackFunction(arg1, arg2) {
// First call, arg1 will be "a" and arg2 will be undefined
// Second call, arg1 and arg2 will be undefined
// Third call, arg1 will be "a" and arg2 will be "b"
if(arg1 !== undefined) {
// You can use arg1.
}
if(arg2 !== undefined) {
// You can use arg2
}
// Easiest way to handle this is to set it to a default value if its not set:
// (usually done at the top of the function).
arg1 = arg1 === undefined ? "default" : arg1;
arg2 = arg2 === undefined ? "default" : arg2;
}
callbackFunction("a");
callbackFunction();
callbackFunction("a", "b");
You can also skip using arguments in the function declaration and then use the argument list:
function callbackFunction() {
for(var i=0;i<arguments.length;i++) {
console.log(arguments[i]);
}
}

Modifying callback function's argument(s) before calling them

I have a custom object that implements a function that'll be executed later. Here's how someone would call it:
customObject.onSomething(function(e) {
// do something with e
console.log('foobar');
});
Here's how onSomething is getting created:
var CustomObject = function() {
this.onSomething = function(callback) {
// If the user passes in parameter(s), how can I modify them before calling?
callback.apply(this);
}
}
How can I modify the argument(s) the user passed in before performing apply or call on the function?
apply takes a second parameter which is a list of arguments to pass to the function. call does the same, except it passes its own argument-list (everything after the first parameter which is used as this).
So, if you know which parameters you expect, you can just add them to the invoking function as the second parameter to apply (or as a list of parameters to call):
this.onSomething = function(arg1, arg2) {
// reverse the first and second arguments
callback.apply(this, [arg2, arg1]);
// equivalent:
callback.call(this, arg2, arg1);
};
If you don't know what kind of arguments to expect, but you still want to do something with them, you can do so with the builtin arguments pseudo-array which holds the arguments given to the current function (even when you don't declare them explicitly).
You can use this to invoke the callback with the same arguments given to the invoking function, or some transformation of them; e.g.:
this.onSomething = function() {
// call callback with the same arguments we got
callback.apply(this, arguments);
// or, make some changes
var newArgs = ["extra argument", arguments[1], arguments[0]];
callback.apply(this, newArgs);
};
Sounds like what you're asking for is fairly simple, see below:
var CustomObject = function() {
this.onSomething = function(callback, param1, param2) {
param1 += 4;
param2 = 'Something about ' + param2 + ' is different...';
callback.apply(this, [param1, param2]);
}
}

Categories