I have a lot of my code inside an object literal and there are a couple functions where I want to be able to pass the functions arguments for the parameters but I can't figure out how to do that.
Here is an example of my object..
var test = {
button: $('.button'),
init: function() {
test.button.on('click', this.doSomething);
},
doSomething: function(event, param1, param2) {
console.log(param1);
console.log(param2);
}
};
So when the button is clicked and it calls the function doSomething I want to pass in arguments for param1 and param2.
Something similar to this maybe, but this does not work.
test.button.on('click', this.doSomething('click', 'arg1', 'arg2'));
Any ideas, or am I going about this the wrong way?
The jQuery.proxy() function seems to be exactly what you need. Have a good read at the docs and see if they make sense to you. For your specific example,
var test = {
button: $('.button'),
init: function() {
test.button.on('click', $.proxy(this.doSomething, null, 'arg1', 'arg2');
},
doSomething: function(param1, param2, event) {
console.log(param1);
console.log(param2);
}
};
In this example, the parameters to $.proxy are:
this.doSomething - The the function to call
null - The context in which the function will be called. By supplying null, we are saying to use its 'normal' context.
arg1 - The value of param1 formal parameter of the called function
arg2 - The value of param2 formal parameter of the called function
Since the click callback supplied the final parameter (event), that is already provided and doesn't need to be additionally or explicitly declared. The jQuery.proxy() when passed additional parameters passes those at the front of the formal parameter list and any remaining parameters implicitly supplied are passed at the end. So if we a function that looks like:
var f = function(a, b, c) {
console.log(a, b, c);
};
and invoke it through a proxy:
var p = $.proxy(f, null, 2, 3);
p(1);
The value of a, b and c that are logged will be 2,3,1.
This question is also extremely close to this one.
How can I pass arguments to event handlers in jQuery?
Related
In following example is a typical function which accepts three parameters:
function cook(a, b, c) {
// cooking stuff..
return[results];
};
Or as a property function, like this:
var myApp = {
handler: function(a, b, c) {
// actions
}
};
My question is how to call this function properly, if we want to pass
only two parameters, or even only one:
Like this:
cook(param1, param2); - myApp.handler(param1, param2);
Or we have to pass always the same number of parameters the function
accepts, regardless if they have data or not, like this:
cook(param1, param2, ""); - myApp.handler(param1, param2, "");
Also, what is the proper way if we want to pass the first and the third
parameters? Or only the second or the third parameter. I can't think
something other than this:
cook(param1, "", param3); - myApp.handler(param1, "", param3);
cook("", param2, "");
cook("", "", param3);
Is this correct and the only way to do it?
the best way as you describe your function would be using an object as a parameter
// function definition
function cook(object) {
//do stuff
return object.a + " " + object.b + " " + object.c;
}
parameters1 = {a:"paramA", b:"paramB",c:"paramC"}
cook(parameters1) // returns paramA paramB paramC
if you test those parameters in the function to handle cases where a, b or c might be null or undefined, you can then do
cook({a:"paramAonly"})
or
cook({a:"paramA", b:"paramB"})
"My question is how to call this function properly, if we want to pass
only two parameters, or even only one"
Ok. This question has multiple ways to interpret what you are asking, so I will give you one of those ways.
If you want to have a function that takes a varying number of parameters then you should use es6 spread operator like this
function (...args) { // the ... is the spread operator
return args
}
Now if you want to return always the first item in the parameters you do this:
function (...args) {
return args[0]
}
Lastly if you want to do something that is more flexible then you can offer this method:
function (... args) {
if(args.length === 3) {
return // you get the point?
}
}
Does this answer your question?
Do we have to pass always the same number of parameters the function ?
No, you don't have to pass all the parameters to a function, the arguments you don't set will be undefined
function cook(a, b, c) {
console.log(a, b, c)
};
cook('foo') // will console log 'foo undefined undefined'
What is the proper way if we want to pass the first and the third
parameters?
You were right on this one, although you would generally give undefined or null value as a parameter here rather than an empty string. If you want to ignore param2 but give param3 for example
cook(param1, null, param3)
Then you could test if all the parameters are properly set or not in your function with a more standard null or undefined value
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.
I see a lot of javascript code that passes a function as a parameter that returns an anonymous object.
myFunction(function() {
return {
foo: 'bar'
};
});
What is the advantage or the purpose of using this instead of simply passing directly an anonymous object?
myFunction({
foo: 'bar'
});
The difference is that if you alter the argument passed in your second code snippet, there is no way to get the original argument again.
If you pass an function instead you can call the function more than once and get always the same argument back. (if the function is implemented this way)
Furthermore if you use a function you can do additional stuff like logging how often your function / argument was called or so on. So using a function adds more flexibility for the user of the function.
For the developer of the function on the other hand accepting a function as argument can cause the tiny problem that a function doesn´t have to return the same value every time you call it - myFunc() == myFunc() COULD return false, therefore i would not recommend handing over a function if it is supposed to JUST return an argument.
Backbone uses have a lot of places where they will initialize the function if passed to get the value, eg.
Backbone.Model.extend({
url: function() { return 'myurl.aspx'; }
});
// VS
Backbone.Model.extend({
url: 'myurl.aspx'
});
This is clever if you will have to make some calculation / run some conditions before you'ill know that the url is.
Backbone.Model.extend({
url: function() {
if ( this.get('name') ) {
return 'service1.aspx';
}
else {
return 'service2.aspx';
}
}
});
Your first example sends an anonymous function as the first argument to myFunction while the second example sends an object as the first argument.
myFunction(function() {
return {
foo: 'bar'
};
}); // function() {...}
myFunction({
foo: 'bar'
}); // {foo: 'bar'}
function myFunction(what) {
console.log(what);
}
If you are talking about closures, the main difference is that you can have private variables inside closures:
var setGet = (function() {
var v = null;
return {
get: function() { return v; },
get: function(val) { v=val; },
};
});
// VS:
var setGet = {
v: null,
get: function() { return this.v; },
get: function(val) { this.v; },
};
In the first example you can't access the variable v without using .get/.set on setGet while in the 2. example i can simple change it by setting setGet.v = 'new_val';
In the first example You are passing a callback function.
When we pass a callback function as an argument to another function,
we are only passing the function definition. We are not executing the function
in the parameter.
And since the containing function has the callback function in its parameter as a function definition, it can execute the callback anytime. This allows us to execute the callback functions at any point in the containing function.
A simple example for this is jQuery click binding :
/The anonymous function is not being executed there in the parameter.
//The anonymous function is a callback function
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
But in the second example you are simply passing an object to the called function.
Use this link to get more details about Callback functions. Enjoy :)
I think, it really depends where you saw the code being used.
In this case, myFunction seems to require you to pass a function rather than an Object.
But in general, Consider this
myFunction(function() {
var a = "bar";
return {
foo: a
};
});
and this:
var a = "bar"
myFunction({
foo: a
});
In the second case, anyone outside is able to access a. But in first case, a becomes similar to a private variable which is exposed as public by the function. So you might observe this in places where people wish to follow OOP concepts in the otherwise classless JS.
Another case is where you require a callback function or some function that is to be called later. So if the data is to be preserved until a certain something is over you can make it available as the return value of a function, rather than storing it globally...
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]);
}
}
I'm studying apply and I am trying to understand why the code I am studying only passes one parameter to apply.
I first define Quo:
var Quo = function(string) {
this.status = string;
};
Next I define get_status:
Quo.prototype.get_status = function() {
return this.status;
};
I define statusObject:
var statusObject = {
status: 'yo!'
};
And this is where I am lost:
var status = Quo.prototype.get_status.apply(statusObject);
// status is 'yo!'
According to the documentation "Apply Calls a function with a given this value and arguments provided as an array." You can see in the case, using apply I pass only a single parameter, which I believe is defining "this". Can you clear up what exactly is happening in this method, why apply is necessary, and why in this case I can only pass one param to the method, when it states two are needed. Thank you.
apply sets the context of the function being applied to the object provided in the first parameter.
var o;
function baz(a, b, c) {
//do stuff
}
o = {
foo: 'bar'
};
baz.apply(o);
//this is o
//a is undefined
//b is undefined
//c is undefined
If an array is passed as the second parameter, the parameters will be set based off the values in the array:
baz.apply(o, [1,2,3]);
//this is o
//a is 1
//b is 2
//c is 3
The second parameter in apply is optional, however call is typically used for settings context:
//these do the same thing
baz.call(o);
baz.apply(o);
//this is how they're different
baz.call(o, 1, 2, 3);
baz.apply(o, [1, 2, 3]);
It doesn't state that two are needed:
fun.apply(thisArg[, argsArray])
notice how argsArray is in brackets, it is optional.
What is happening on your call, is that your statusObject is passed as the this argument to your get_status function.
This means that when get_status executes and does return this.status it is in essence returning statusObject.status.
Apply is useful for many reasons, one of which is to invoke methods dynamically. I can pass the string name of a method in the object to be invoked like so:
methods = {
init: function(message) {
alert(message);
}
};
function executeFunc(method) {
methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
//now I can call like this:
executeFunc('init', 'Hey there, this is a message');
An example of this can be found in my jQuery Plugin skeleton on GitHub
apply takes one argument, the object to use as this, followed by the arguments if any.
If the function takes no arguments, e.g. you have function f() { ... }, you don't need to pass any arguments, so you can call f.apply(someObject);.