Why doesn't bind(object) binds this to object inside anoynamous function? - javascript

I am pretty new to javascript, and i am facing some problem understanding why my first code is not working as expected. I have read a few articles on "this", but it didn't help.
var user_obj = {
some_func : function () {
alert("hello");
}}
First code:
Here i am attaching an event listener to some element called elem. The attached function is a property of user_obj, but when i execute it, it gives me error "TypeError: this.some_func is undefined". My doubt is when i am using bind to explicitly bind this to user_obj, why is it giving this error.
document.getElementById("elem").addEventListener("click",(function () {this.some_func.bind(user_obj)})());
Second Code:
This works as expected.
document.getElementById("elem").addEventListener("click",this.some_func.bind(user_obj));
Thanks for the help.

bind() change the value of this inside the function, not outside.
In your first example the anonymous function use this to refer to global object.
If you want the anonymous function uses this to refers to user_obj you need to bind the anonymous function to user_obj, not some_func(), also because some_func() doesn't use this, so there is no need to bind it.
Look at the example: it prints the value of this in the anonymous function, first time binding it to user_obj, second time without binding
var user_obj = {
some_func : function () {alert("hello");}
};
document.getElementById("elem").addEventListener("click",(function () {console.log(this)}.bind(user_obj))());
document.getElementById("elem").addEventListener("click",(function () {console.log(this)})());
<div id="elem"></div>
Your second example works also without the bind(), because again it changes the value inside the function, where this isn't used. But since in your second example the call to the function isn't in an anonymous function, the value of this doesn't change to global object
BTW, it is getElementById(), not getElementByID() (please notice the d of Id)

Related

Uncaught (in promise) TypeError: stringConcat() is not a function : Async String Concatenation doesnt work [duplicate]

I have the following function
function hello() {
alert("hi!");
}
Take this piece of code:
var elem = document.getElementById("btn");
elem.onclick = hello;
My question might be a bit hard to understand, so bear with me:
What EXACTLY differentiates THIS piece of code from a normal call, or what makes this piece of code require a reference to the function variable rather than a regular call? (hello();)
How can I know where I'm supposed to give a reference to the function, and when I'm supposed to actually call it?
Well, the onclick property expects a reference to a function, for it to execute when the element is clicked. Normally it's either:
element.onclick = funcRef;
or
element.onclick = function () {
funcRef();
};
(but of course, it's best to use addEventListener and attachEvent)
Notice how both of them are references to functions, not calling.
When something expects a reference, you don't call it...you assign a reference to it (first example).
When you want to specifically call a function, you call it with () (second example). But notice how in the second example, there's still a reference to a function that's assigned to onclick - it's just an anonymous function.
Probably the more important part:
Some people think you want to do this:
element.onclick = funcRef();
But that immediately executes the function (because of the ()), and assigns its return value to onclick. Unless the return value is a function, this isn't what you want.
I think the moral of the story is that when you want/need something to execute right now, you call the function. If the function is wanted for later use or needs stored, you don't call it.
Do you want it to execute NOW? Then call it.
a=hello() means "Call hello() ASAP, and set its return value to a".
On the other hand, a=hello means "a is an alias for hello. If you call a(), you get the same results as calling hello()"
You use the latter for callbacks, etc, where you want to tell the browser what you want to happen after an event occurs. For example, you may want to say "call hello() when the user clicks" (as in the example). Or, "When the AJAX query returns a result, call the callback() function on the returned data".
How can I know where I'm supposed to give a reference to the function, and when I'm supposed to actually call it?
Do you need the function to run now?
Than add the () to execute it
Do you need to function to be referenced so it is called later?
Do not add the ().
Pretty much all statements in JavaScript have a return value. Unless otherwise specified, functions in JavaScript will return undefined when called. So, the only context in which it would make sense to call your function in this assignment statement if it were to return a function:
function hello() {
return function() {
alert("hi!");
}
}
elem.onclick = hello();
A reference to your function is needed somewhere no matter how it gets called. The difference here is that you are not explicitly calling the hello function. You are assigning a reference to that function to the elem DOM node's onclick event handler so that when onclick is fired for that Node, your function gets called.
hello() means you call that function, which means the function will be executed directly.
while when you have elem.onclick = hello, this is called a callback. Where hello doesn't get executed directly but only when a certain event is fired (in this case when there's a click on the element)

Confused about when to use `bind` in an Event Handler

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.

Javascript object. How to create a button that calls a function inside same object

I have a JS object called 'avatar' with a create function that generates a button on an HTML page and sets a few attributes.
I want to set an onmouseover on the button that calls a function within the avatar object so I have written:
this.create = function(){
this.button = document.createElement("BUTTON");
this.text = document.createTextNode(this.name);
this.button.appendChild(this.text);
document.body.appendChild(this.button);
this.button.style.background=this.color;
this.button.addEventListener("mouseover", this.randomMove());
}
However, the function randomMove is executed immediately and does not wait for the button to be mouseovered. Subsequent mouseovers do nothing.
You need to pass it a function body, not what the function returns. In your case, the code evaluates this.randomMove() and assigns the returned result to the mouseover event handler. This is how it should look:
this.button.addEventListener("mouseover", this.randomMove);
See this simple example to easily understand what's going on: http://jsfiddle.net/f8tm4jq3/
Like Andy mentioned, you need to pass the function reference (without parenthesis), not function expression (with parenthesis) as the argument to addEventListener.
Thanks for your help.
As it turns out what I was really looking for was bind.
this.button.addEventListener("mouseover",this.randomMove.bind(this));
This bound the function call to a method belonging to this object. So even though the HTML generated lives outside the scope of the object instance, the function call remembers which instance it should make the call to.
Thanks again
Ben

Changing a variable reference in JavaScript

I am having a hard time figuring out how to reassign a variable to a new function.
I have two anonymous functions that are assigned to variables and I want "first" to assign itself to "after" after it is called once.
When using .toSource in Firefox it seems like it is definitely reassigning first to after, but the "first in the click handler is still referencing or calling the first that was created at runtime.
Why is this?
JS
var after = function() {
alert("AFTER");
}
var first = function() {
alert("FIRST");
//alert(first.toSource());
first = after;
}
$(".order").click( first );
HTML
<button type="button" class="order">Order</button>
http://jsfiddle.net/E2R2R/2/
Following up on #MichaelGeary s answer, if you want to make the function work by referencing like that, put your function call inside an anonymous function:
$(".order").click( function() {
first();
});
Demo: http://jsfiddle.net/E2R2R/4/
When you make this call:
$(".order").click( first );
you are using the value of first at the time of this call. Its value at that time is what is passed into the click() function.
Changing the value of the first variable later won't affect this.
Another way to look at it: think about the code inside jQuery's click() method. It doesn't know that you used the first variable in your call. The only information it has is the function that first refers to at the time of the call.
See #tymeJV's answer for a way to do what you want. Note what's different about it: the function you're passing into .click() (the anonymous function) doesn't have to change. When that function calls first(), it always uses the current value of that name.

Why does the content of the function load, but not the function when it is called?

I'm having an issue with a function I'm calling (using jQuery).
My issue is that when I attempt call a function within another function, that first one is not being called. However, when I place the contents of the failed function into the one calling said failed function, the code executes fine. I'll show you what I'm talking about:
$('form.register .submit').click(validateRegister);
function submitStart() {
var $this = $(this);
$this.parent().addClass('processing');
var $processing = $('#processing');
$processing.show();
var $endNote = $this.parent().find('#endNote')
$endNote.slideDown();
}
function validateRegister(){
submitStart();
}
See, when the contents of submitStart() are places into the validateRegister(), they code executes. However, when calling the function as it shows in this code, it fails to work. I've considered scope (at least to my knowledge) to no avail.
Any help is extremely appreciated, thank you!
You're losing your expected this value, so the submitStart is likely getting called, but doesn't work properly.
Do this:
function validateRegister(){
submitStart.call( this );
}
Here you're calling the submitStart method via the .call() method, which is available on function instances.
The .call() method lets you manually set the value of this in the function you're calling.
So because you've assigned validateRegister as the handler, jQuery makes sure the value of this is your element. But since validateRegister is calling another function, it carries the responsibility of making sure that function has the proper this value.
Just keep in mind that when you call any function like this:
someFunction();
...the value of this in that function will be window, unless you manually set it to something else.
var $this = $(this); is returning the window.
this will no longer be referencing the clicked element but will represent window instead. You can change submitStart so that it accepts a parameter and pass this to it.
I think you may have an issue with this not being what you think it is.
Try this:
submitStart.call(this)
The problem here is this. When you use validateRegister as an event hander, jQuery automatically makes sure that this refers to the DOM element that dispatched the event. But when you call your separate function, the scope isn't being set correctly, so this probably refers to the window object (that's the default when you refer to this in the global scope). To fix it, the easiest way would be to pass the element along as an argument:
function submitStart(element) {
var $this = $(element);
$this.parent().addClass('processing');
var $processing = $('#processing');
$processing.show();
var $endNote = $this.parent().find('#endNote')
$endNote.slideDown();
}
function validateRegister(){
// "this" is the DOM element
submitStart(this);
}
As #Andrew noted, you can also set the value of this in submitStart by using the .apply() method of the submitStart function.

Categories

Resources