How to get "this" to reference parent object? [duplicate] - javascript

I read some other answer about this topic but I'm not sure I understand how this keyword works inside addEventListener.
const button = document.querySelector('button');
function foo() { console.log(this) }
button.addEventListener('click', foo);
foo is a regular function inside addEventListener, it's not a method on button object. When foo is called should be executed in the context of the global object, therefore this should be equal to window and not to button.
Looks like a situation similar to this example:
const obj = {
method: function (cb) {
console.log('method', this); // `this` === `obj`
return cb();
}
};
obj.method(function() {
console.log('cb', this); // `this` === `window`
});
Where obj could be considered as button, method could be addEventListener and cb the callback inside addEventListener.
I know I can use bind to change the context of this but I want to understand more in depth why it works like that.
Why this inside addEventListener callback is invoked on the context of the current element instead of the global object?

If we are using functions which have been defined using function keyword as an event handler, then that event handler function executes in the context of the element on which event was binded
button.addEventListener('click', foo);
so, in this case, this value inside foo will be button element.
If we use arrow functions instead of them then this value will be the window object
The reason is this in an arrow function has the same value as the context in which the arrow function was created
button.addEventListener('click', () => { console.log(this) // window } );
More about lexical this
What is lexical 'this'?

While we know that event listeners are executed with 'this' set to the event target, the below lines of code inside the EventTarget.prototype.dispatchEvent method in the EventTarget link that you found will answer your question as to how it is implemented.
for (var i = 0, l = stack.length; i < l; i++) {
stack[i].call(this, event);
}
The 'stack' array has the callback functions and they are invoked using .call by passing in the event target instance (this) and event as arguments.

As event handler is a type of callback, they are passed as a parameter to the function. Let's create a simple function and passed one callback as a parameter to it and see how it actually works.
function testCallBack(fn){
console.log('inside testCallBack');
fn('Hello I am a callBack')
}
testCallBack(foo);
function foo(param){
console.log(param);
}
// Outputs:
inside testCallBack
Hello I am a callBack
Every scope in JavaScript has a this object that represents the calling object for the function.
That's the reason why this inside addEventListener callback is invoked on the context of the current element instead of the global object.
Refer below code for more clear understanding:
function sayNameForAll() {
console.log(this.name);
}
var person1 = {
name: "Rajat",
sayName: sayNameForAll
};
var person2 = {
name: "pldg",
sayName: sayNameForAll
};
var name = "Sidd";
person1.sayName(); // outputs "Rajat" here calling object is person1, so this represents person 1
person2.sayName(); // outputs "pldg"
sayNameForAll(); // outputs "Sidd"
So when you call button.addEventListner('click',foo), your calling object is button.

Event listeners are executed with this set to the object that triggered the event, as one listener can listen to events of many objects.
A regular function invocation however does not set this if the invocation expression does not contain a member access via .. In those cases, without "use strict" active, this will become the global context, which is window in the browser.
If you want this for cb to be obj, you could replace cb() with cb.apply(this), which would set cb's this to that of the enclosing function.
A final warning: these this mechanics only work for functions defined with the function keyword (and similar mechanics). The this inside of an arrow function becomes locked to that of the enclosing scope at the time of definition.

Just like you can use bind or call to set this to whatever you want, the browser APIs can also call your functions with any value set to this. It’s used in a bunch of weird ways and isn’t very consistent. Outside of classes and methods, this is more like a secret extra argument to a function. In this case you could avoid needing it by getting the button element from event.target.

Related

Arrow functions return this as the window object

In my program using the function() syntax is returning the this value of a target element, but using an arrow function returns the window object. How are each of these two functions getting their this?
function editTemplates() {
//sits within for loop
clocksTemplate.gmt[i].addEventListener('keydown', (e) => {
console.log(this); //returns window object
});
clocksTemplate.gmt[i].addEventListener('keydown', function(e) {
console.log(this); //returns element bound to clocksTemplate.gmt
});
According to MDN with arrow functions, this should "retain the original meaning from the enclosing context". Is the enclosing context the event listener? or the function it sits within? According to my test, the enclosing context for the arrow function must be the Window object but I can't see how. With the function() syntax the enclosing function is meant to redefine the this value, which I'm assuming it is doing here within the addEventListener method. This topic has been discussed in depth here and here but I'm a JS newbie and I couldn't understand how this applied to my problem.
That is perfectly normal & expected behaviour of arrow functions.
As the documentation mentions about regular functions:
every new function defined its own this value (a new object in the
case of a constructor, undefined in strict mode function calls, the
context object if the function is called as an "object method", etc.).
If it is required to override the value of this in a regular function, then instead of calling it directly, we can invoke it using call() or apply().
So in your case of regular function, the callback function is getting invoked internally by addEventListener function using a call() or apply() with value of this set to element bound to clocksTemplate.gmt. It's a standard practice to use call() or apply() for invoking callbacks.
In case of the first function (arrow function), the this does not get assigned any new value. They can't be invoked using call() or apply() because arrow functions are anonymous. So it continues to have the value that was defined by the enclosing function editTemplates() and that happens to be window.
See below for code example:
// REGULAR FUNCTION
function editTemplates() {
console.log(this) // window
var myVar = this;
// sits within for loop
clocksTemplate.gmt[i].addEventListener('keydown', function(e) {
// new value assigned for 'this' as element bound to clocksTemplate.gmt
console.log(this); // returns element bound to clocksTemplate.gmt
console.log(myVar); // returns window
});
// ARROW FUNCTION (Anonymous Functions)
function editTemplates() {
console.log(this) // returns window object
var myVar = this;
// sits within for loop
clocksTemplate.gmt[i].addEventListener('keydown', (e) => {
// No new value gets assigned to 'this'
console.log(this); // returns window object
console.log(myVar); // returns window object
// Saves the hassle of saving 'this' to another variable!
});
Hope this answers your question.

this keyword inside an object's function

I didn't understand the var self = this; in the below code. I know that "The value of this, when used in a function, is the object that "owns" the function.".Then this keyword inside the object's function refer to that object,right?However the comments of below codes says the opposite of that.
I'm confused about why we cannot use this keyword inside an object's function, in the below code? What does this refer to in below codes?
var util = require('util');
var EventEmitter = require('events').EventEmitter;
// #station - an object with `freq` and `name` properties
var Radio = function(station) {
// we need to store the reference of `this` to `self`, so that we can use the current context in the setTimeout (or any callback) functions
// !!!! -> using `this` in the setTimeout functions will refer to those funtions, not the Radio class
var self = this;
// emit 'close' event after 5 secs
setTimeout(function() {
self.emit('close', station);
}, 5000);
// EventEmitters inherit a single event listener, see it in action
this.on('newListener', function(listener) {
console.log('Event Listener: ' + listener);
});
};
// extend the EventEmitter class using our Radio class
util.inherits(Radio, EventEmitter);
// we specify that this module is a refrence to the Radio class
module.exports = Radio;
I read similar posts and understood, however i couldn't understand the comments of below codes. Also, nobody mentioned about the this keyword inside a function's function argument inside a constructor. Especially the second sentence which is written bold makes me confused totaly :
We need to store the reference of this to self, so that we can
use the current context in the setTimeout (or any callback) functions.
using this in the setTimeout functions will refer to those funtions,
not the Radio class
Quoted from : http://www.hacksparrow.com/node-js-eventemitter-tutorial.html
The value og the this keyword will change value based on the current context. The complete description of how this works is somewhat complicated. See MDN for more details.
In your case, this will have a different value when the anonymous function inside the setTimeout call is eventually called. However, the self variable will still be available.
The purpose of 'this' in a function is a reference to the object that called the function. Since you're passing an anonymous function to the window with setTimeout, any 'this' call within that function will be referencing the window object.
You could use the Javascript Bind function to preserve the context of 'this' within the callback function regardless of where it's called.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Your setTimeout function would look like this:
setTimeout(function() {
this.emit('close', station);
}.bind(this), 5000);

What is the meaning of 'function(event)' in js

I don't know the meaning of the sentence 'function(event)'
Event.add(apple,'click',function(event) {
Event.stopPropagation(event);
});
Isn't the argument 'event' is the unique keyword of javascript?
Is keyword can be an argument of some function?
I understand the meaning of below code :
function(test) {
alert(test);
}
But I don't understand this one :
function(event)...
Can any one give an explanation about that to me?
The event object is always passed to the handler and contains a lot of useful information what has happened.
Different types of events provide different properties. For example, the onclick event object contains:
event.target - the reference to clicked element. IE uses event.srcElement instead.
event.clientX / event.clientY - coordinates of the pointer at the moment of click.
Information about which button was clicked and other properties.
Please visit this link.
It answers all your questions very simply
Source http://javascript.info/tutorial/obtaining-event-object
Example:
Like if in HTML you have assigned an event like this
<button onclick="alert(event)">See the event</button>
then
function alert(event) {
// event.type contains whether this event was invoked in the result of a click etc
// event.target would contain the reference to the element which invoked this method/event
}
It is an anonymous function, that is a function without name, that sends the event object. That object contains information about the event itself. It is always passed as first object/variable.
It is defining an anonymous function object. This code:
function foo(bar) { ... }
Is functionally similar to:
var foo = function (bar) { ... };
(Except that in the first case the name foo and the creation and assignment of the function object are hoisted to the top of the scope, while in the second case only the name foo is hoisted; foo won't hold the function until the assignment executes.)
Effectively, the code you posted is calling Event.add() and passing a function to it as the third argument, but rather than declaring the function ahead of time it is creating the function object inline.
Another way to write the code block in your question is:
function handler(event) {
Event.stopPropagation(event);
}
Event.add(apple, 'click', handler);
Except that the code in your question does not introduce the handler name.
Note that there is no such method Event.stopPropagation(). However, the event object will have a stopPropagation(), so the capital E was probably a typo. It's likely that the intent was to use function (event) { event.stopPropagation(); }.
event is just a variable that's passed to event listener functions such as Event.add, element.on. It's not reserved (although Event is, which is why you can use Event.add), and you can name it whatever you like.
The event argument is used to pass information about the event that has happened (the click on apple in this case), which can be used to retrieve data about the event or manipulate it.
function(){...} is an anonymous function, which means that you don't need to name it, you can just declare it inline, and the function will be passed as an argument, as if you said
function foo (event) {
...
}
Event.add(apple, "click", foo);
but you don't need to declare it before hand. It does come at the disadvantage of not being duplicable, for instance when clearing an event handler.
Look at the event variable and you will all understand :)
function (event) {
console.log({ event });
}

What state is used by a triggered inner function when dependant on differing value of outer function

1) I have an inner function that is event driven.
2) The inner function depends on variables in the outer function.
3) The variables in the outer functions are parameters to the outer function.
4) The outer function is being runned several times.
q) What values can I excepct the parameter variables from the outer function to have when the inner function gets triggered later on in the code?
scenario 1) When triggering the inner function, the exact state of the outer function that existed when creating the inner function is used.
scenario 2) When triggering the inner function, the latest value from the outer functions is used.
example:
function outerFunction(parameter) {
var object = new Object();
object.on('click', function () {
alert(parameter);
});
return object;
}
Each time outerFunction is called, a new event listener is registered for clicks on object. So, if you call:
outerFunction('foo');
outerFunction('bar');
you should see two alerts, one for 'foo', and one for 'bar'.
In case of jQuery (and, probably, similar libraries) each time you call on(evt, handler) you bind new handler to element for that event. And jQuery stacks handlers.
So if you do:
outerFunction(1);
outerFunction(2);
jQuery will bind two handlers, and when triggering 'click' on your object you will get, in sequence:
Alert for "1".
Alert for "2".
Fiddle for that: http://jsfiddle.net/ajWvk/
If it is your implementation of some abstract event-binding, and your implementation doesn't stack handlers, you will just bind new handler on each invocation of outerFunction, erasing all previous handlers.
In this case, calling:
outerFunction(1);
outerFunction(2);
will only alert "2", when "click" is triggered on object.
It depends on what parameter is. In javascript, strings and numbers are passed by value but objects are passed by reference. So if parameter is a string then it will work like bfavaretto said. However, if parameter is an object and you alert on parameter.text, then you will be alerted with whatever the current value of parameter.text is.
Try defining these in the console:
function outerFunction(parameter) {
setTimeout(function () {
console.log(parameter);
},5000);
}
function otherOuterFunction(parameter) {
setTimeout(function () {
console.log(parameter.text);
},5000);
}
Now, when you run this line it will log "test":
parameter = "text"; outerFunction(parameter); parameter = "hi";
But this will log "hi":
obj = {text: "text"}; otherOuterFunction(obj); obj.text = "hi";

Finding the parent object of a callback function

I have a function:
myObject.myFunction = function(callback){
callback();
}
and a callback
randomObject.callBack = function(){
console.log(this);
}
if I call randomObject.callBack() directly, I get the parent object in the console. However, if I call myObject.myFunction(randomObject.callBack), it logs a DOM Element.
How can I access the parent object?
Note
I do not know the name of the callbacks parent object ahead of runtime.
The context (i.e. this value) of an object is determined at the time the function is run, not at the time it is defined. Passing randomObject.callBack to another function does not send the context (the randomObject bit); it just sends the function.
Presumably the context is set when myFunction calls it. Since you aren't explicitly providing a context (e.g. with call or apply), the context will be the window object.
You can change this by explicitly saying what the context of the function should be before it is run. You can do this with the bind method:
myObject.myFunction(randomObject.callBack.bind(randomObject))
Now when you call callback inside myFunction, randomObject will be logged.
Note that bind is relatively new; not all browsers support it. The MDN page I linked to above has a bit of code that will make it work in all browsers.
This happens because when you invoke a function without an object, inside the function this will point to Window object.To avoid this we usually do like this
myObject.myFunction = function(callback){
callback();
}
randomObject.callBack = function(){
console.log(this);
}
function proxyCallback(){
randomObject.callBack();
}
myObject.myFunction(proxyCallback);
In javascript, this refers to the object context in which a function is called. This is not necessarily related to any object on which it has been defined.
You can think of it as though functions are not defined as members of objects, but called as members of objects.
There are four things that this might resolve to, depending on context.
A newly created object, if the function call was preceded by the new keyword.
The Object to the left of the dot when the function was called.
The Global Object (typically window), if neither of the above are provided.
The first argument provided to a call or apply function.
In your situation, something like this might be appropriate:
myObject.myFunction(function(){randomObject.callBack()});
This creates a closure so that within myFunction, callback is called as a member of randomObject.

Categories