When using the jQuery "each" function, is there a way to pass arguments to the function that is called ?
something.each(build);
function build(vars) {
}
I know that I can simply do the following, but I was wondering if there is a way in which arguments can be passed directly.
something.each(function() {
build(vars);
);
You can accomplish the above using a closure. The .each function takes as an argument a function with two arguments, index and element.
You can call a function that returns a function that takes those two arguments, and store variables in there that will be referenced when the returned function executes because of JavaScript scoping behavior.
Here's an example:
// closureFn returns a function object that .each will call for every object
someCollection.each(closureFn(someVariable));
function closureFn(s){
var storedVariable = s; // <-- storing the variable here
return function(index, element){ // This function gets called by the jQuery
// .each() function and can still access storedVariable
console.log(storedVariable); // <-- referencing it here
}
}
Because of how JavaScript scoping works, the storedVariable will be reference-able by the returned function. You can use this to store variables and access in any callback.
I have a jsFiddle that also proves the point. Notice how the text output on the page is different than the HTML defined in the HTML pane. Look at how the function references the stored variable and appends that to the text
http://jsfiddle.net/QDHEN/2/
Here's the MDN page for closures for more reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
Related
The following successfully prints 'foo'.
var obj = {
name: 'foo',
printName: function printName() {
console.log(this.name);
}
};
var printButton= document.getElementById('printIt');
printButton.addEventListener('click', function(){
obj.printName();
});
The following doesn't, however:
printButton.addEventListener('click', obj.printName() );
I know the solution... simply use bind so that we're referencing the obj object. i.e:
printButton.addEventListener('click', obj.printName.bind(obj) );
Why then don't we need to use bind in the first example. I don't know why wrapping obj.printName() function call in the anonymous function results in the console.log correctly referencing and printing this properly, but when called directly after click, you needs to use bind
Alright, I commented with some good information on this question so I might as well answer!
Functions are first class
Okay, let's starts with some fundamentals of javascript that is very dissimilar to some other programming languages: in javascript functions are first class citizens--which is just a fancy way of saying that you can save functions into variables and you can pass functions into other functions.
const myFunction = function () { return 'whoa a function'; }
array.map(function () { return x + 1; });
And because of this wonderful feature, there is a big difference between the expressions:
Expression 1
obj.printName
and
Expression 2
obj.printName();
In expression 1: the function isn't being invoked so the value of the expression is of type function
In expression 2: the function is being invoked so the value of the expression is what the function returns. In your case, that's undefined
addEventListener
The method addEventListener takes in two arguments:
a string of the type of event
a function that will be run when the event fires.
Alight, so what does that mean?
When you call
// doesn't work
printButton.addEventListener('click', obj.printName() );
you're not passing a value of type function to the addEventListener method, you're actually passing undefined.
// works
printButton.addEventListener('click', obj.printName.bind(obj) );
then works (for one reason) because the second argument is actually of type function.
What does bind do? Why does it return a function?
Now we need to discuss what bind actually does. It related to the pointer* this.
*by pointer, I mean a reference identifier to some object
bind is a method that exists on every function object that simply binds the this pointer of a desired object to the function
This is best shown by an example:
Say you have a class Fruit that has a method printName. Now that we know that you can save a method into a variable, let's try that. In the example below we're assigning two things:
boundMethod which used bind
unboundMethod that didn't use bind
class Fruit {
constructor() {
this.name = 'apple';
}
printName() {
console.log(this.name);
}
}
const myFruit = new Fruit();
// take the method `printName`
const boundMethod = myFruit.printName.bind(myFruit);
const unboundMethod = myFruit.printName;
boundMethod(); // works
unboundMethod(); // doesn't work
So what happens when you don't call bind? Why doesn't that work?
If you don't call bind in this case, the value of the function that gets stored into the identifier unboundMethod can be thought to be:
// doens't work
const unboundMethod = function() {
console.log(this.name);
}
where the contents of the function is the same contents of the method printName from the Fruit class. Do you see why this is an issue?
Because the this pointer is still there but the object it was intended to refer to is no longer in scope. When you try to invoke the unboundMethod, you'll get an error because it couldn't find name in this.
So what happens when you do use bind?
Loosely bind can be thought of as replacing the this value of function with the object you're passing into bind.
So if I assign: myFruit.printName.bind(myFruit) to boundMethod then you can think of the assignment like this:
// works
const boundMethod = function() {
console.log(myFruit.name);
}
where this is replaced with myFruit
The bottom-line/TL;DR
when to use bind in an Event Handler
You need to use Function.prototype.bind when you want to replace the thises inside the function with another object/pointer. If your function doesn't ever use this, then you don't need to use bind.
Why then don't we need to use bind in the first example?
If you don't need to "take the method" (i.e. taking the value of type of function), then you don't need to use bind either Another way to word that is: if you invoke the method directly from the object, you don't need bind that same object.
In the wrapper function, you're directly invoking the method of the object (as in expression 2). Because you're invoking the method without "taking the method" (we "took" the methods into variables in the Fruit example), you don't need to use bind.
printButton.addEventListener('click', function(){
// directly invoke the function
// no method "taking" here
obj.printName();
});
Hope this helps :D
Note: You need to call printButton.addEventListener('click', obj.printName() ); without parenthesis in obj.printName() since you want to pass the function.
The answer lies in the way this is bound in Javascript. In JS, the way a function is called decides how this is bound. So when you provide the callback function like below:
printButton.addEventListener('click', function(){
obj.printName();
});
Notice, printName is being called via dot notation. This is called implicit binding rule when this is bound to an object before dot, in this case obj. Clearly in this case, you get the expected output.
However, when you call it like this:
printButton.addEventListener('click', obj.printName );
Notice that, all you are passing is the address of the function that is inside obj. So in this case info about obj is lost. In other words, the code that calls back the function doesn't have the info about obj that could have been used to set this. All it has is the address of the function to call.
Hope this helps!
EDIT:
Look at this crude implementation I call bind2 that mimics native bind. This is just to illustrate how native bind function returns a new function.
Function.prototype.bind2 = function (context) {
var callBackFunction = this;//Store the function to call later
return function () { //return a new function
callBackFunction.call(context);//Later when called, apply
//context, this is `obj` passed
//in bind2()
}
};
function hello() {
alert(this.name);
}
obj = {
name:'ABC'
};
var f = hello.bind2(obj);
f();
Notice: How function f() is hard bound here. f() has hard bound this with obj. You cannot change this to other than obj now. This is another thing with bind that probably will help you knowing.
I would like to convert and replace all words in an array using a an object's method which returns an observable. The problem is that since the result is asynchronous, the value of result is overwritten incorrectly at the wrong index. If I had a synchronous function, I could have just simply call the function in a for loop. What should I do in this case?
for(let word in this.words){
var i = Object.keys(this.words).indexOf(word);
this.convertor.get(this.words[i].value).subscribe( (result) => {
this.words[i].value = result; //The value of i is incorrect
});
}
Since ES6 you can easily solve this by replacing var i = with let i =. That way you get a variable that has block scope, and so you get a different variable instance in each iteration of the loop.
The original answer works also when you don't have support for let:
You could solve this with a plain function that you bind an extra argument to:
this.convertor.get(this.words[i].value).subscribe( function (i, result) {
this.words[i].value = result;
}.bind(this, i));
.bind() returns a (new) function that will be like the function you apply bind on, but will pre-determine a few things for when that function is actually called:
this will then be set to what you pass to bind in the first argument. Contrary to arrow functions, old-fashioned functions normally get their this value by how the function is called -- that behaviour is not what you desire, and so it is a welcome feature of bind;
Some of the arguments can be fixed upfront. Whatever other arguments you pass via bind become the values of the first arguments of that function -- they are not determined by the actual call. In this case the value of i is passed, so the function will get that value as its first argument, no matter how it is called later.
Any other argument that is passed to the function at the moment of the call(back) will follow the previously mentioned arguments. In this case, the actual call will pass the value of result, and so the function should deal with two arguments: one provided by bind, the other by the caller.
This solves your issue as you can pass on the correct value of i to each of the functions without losing the value of this.
Another alternative could be forEach so you don't have to track the index and handle cleaning up the subscriptions with first() for example.
this.words.forEach((word, i) => {
this.obs$(word)
.pipe(
tap(v => (this.words[i].value = v)),
first()
)
.subscribe();
});
https://stackblitz.com/edit/static-iterable-value-from-obs?file=src/app/app.component.ts
For example below is an anonymous function that has been placed in parentheses so now the function can be stored as a variable and called on else where as seen in number 2, However how can script number 1 be called to run? How can it be identified if it has no name? And how to change an anonymous function to a defined function?
**Number 1**
(function() {
// Do something
})();
**Number 2**
(var funcName = function() {
// Do something
})();
funcName();
The first function is called immediately because it is followed by () which calls a function.
Then if you were to remove the () from around the var statement (which is an error):
The second function is also called immediately, for the same reason.
The value stored in funcName (which is called as if it were a function, so will cause an error if it is not a function) is the return value of the second function (and is defined by the code you represented as // Do something — the "something" needs to include "return a function").
How can it be identified if it has no name?
Names are only really useful for use in debuggers (where they are very useful in stack traces and the like). For identifying a function to call, you access them like any other object or primitive (via a variable, property, etc). A function declaration just creates a variable with a matching name in the current scope.
Yes, these are anonymous functions, but they are also functions that are being called / invoked immediately, and hence don't need names to be referred to later.
There are many uses for Immediately-Invoked Function Expression (IIFE), but one is to use functions to establish namespaces that do not pollute global
Sorry for my last question. This is the question with better formatting.
I have a method that I am passing a method through :
method("my_passed_method()")
function method(passed_method){
eval(passed_method.replace('()', + '(' + obj + ')' );
}
But this returns :
SyntaxError: missing ] after element list
I'm assuming this is just some simple JSON syntactical error.
I think you probably want this:
method(my_passed_method)
function method(passed_method){
passed_method(obj);
}
Note that when you're calling method, you're passing in a function reference, not a string, and you're not calling my_passed_method (there are no () after it), you're just referring to it.
Within method, you call the function via the variable passed_method, because that variable contains a function reference.
In JavaScript, functions are first class objects. Variables can refer to them, you can pass them around, etc.
Here's a complete, self-contained example:
// The function we'll pass in
function functionToPass(arg) {
display("functionToPass's arg is: " + arg);
}
// The function we pass it into
function functionReceivingIt(func) {
func("foo");
}
// Do it
functionReceivingIt(functionToPass);
Live copy | source
The name of a method, is at the same time, your reference to that method object. The parentheses, optionally with parameters in between them, make javascript call that method. This means your code can be rewritten to:
method(my_passed_method)
function method(passed_method){
passed_method();
}
So, what's going on here?
When you pass in the name my_passed_method, the javascript engine will look what that name maps to, and find that it maps to a functio object.
Than, inside the function call of method, that object is assigned to the parameter name `passed_method. Than, putting parentheses after this name will make javascript try to execute that object, which is indeed possible because this object is a function, and just like any other type, functions are what we call first class citezens. You can treat the just like any other value by assigning them to variables and passing them around.
In Javascript functions are considered objects. You may pass them as parameters to other functions, as demonstrated below.
function something(x){
alert(x);
}
function pass(func){
pass2(func);
}
function pass2(func){
func("hello");
}
pass(something); //alerts hello
Demo:
http://jsfiddle.net/wBvA2/
eval is evil; or so they say. You don't need to pass the function as a string you can just pass the function itself and then use Function.call or Function.apply to pass the argument (or just call it directly):
method(my_passed_method);
function method(passed_method) {
passed_method(obj);
// OR: passed_method.call(window,obj);
}
The circumstances where eval are necessary are very rare.
Note that your code will evaluate obj as javascript, so the outputs may differ.
//This is the function that will run every time a new item is added or the
//list is sorted.
var showNewOrder = function() {
//This function means we get serialize() to tell us the text of each
//element, instead of its ID, which is the default return.
var serializeFunction = function(el) { return el.get('text'); };
//We pass our custom function to serialize();
var orderTxt = sort.serialize(serializeFunction);
//And then we add that text to our page so everyone can see it.
$('data').set('text', orderTxt.join(' '));
};
full code is at http://demos.mootools.net/Dynamic.Sortables
var serializeFunction = function(*el*) { return el.get('text'); };
var orderTxt = sort.serialize(serializeFunction*(el)*);
compare the codes.
Is el being passed or not? what is going on???
I want to learn advanced parameter usage.
If not declaring functions like function name(parameter1, parameter2, parameter3...).
If not calling functions like name(parameter1, parameter2, parameter3...).
If parameters aren't variables.
If declaring functions like function(parameter1, parameter2, parameter3...).
If calling functions like variable(parameter1, parameter2, parameter3...).
If parameters are objects.
I'm interested.
You probably have a bookmark with the lessons in which I'm interested... please, share!!!
The value assigned to "serializeFunction" is actually an anonymous function, you can see it like a pointer or reference to a function, "el" is simply a declared input parameter that will be used then that function will be called.
Looking at the original code of the one that was posted, the call of the sort.serialize function, receives only the function as a parameter, the "serializeFunction" is not being invocated, it's only passed as an argument.
So, the serialize function that receives the reference of the function passed as a parameter it will be in charge of execute it internally.
This is a lambda expression like.
sort.serialize()
accept the function as parameter, not the value.
The first code is probably correct.
In JavaScript, functions are stored in variables just as any other value (as you see with serializeFunction), and sort.serialize only takes a reference to serializeFunction. Then serializeFunction is called from sort.serialize with the current element (el).
The second code would send an undefined value to the serializeFunction (since el has not been defined in that scope) which would throw an error. Even if el was defined, sort.serialize expects a reference to a function, not a value.