this keyword
/* Ref: https://toddmotto.com/understanding-the-this-keyword-in-javascript/
* The this keyword’s value has nothing to do with the function itself,
how the function is called determines the this value
* It can be dynamic, based on how the function is called
* You can change the this context through .call(), .apply() and .bind()
*/
// BY DEFAULT - Window object
/* By default, 'this' should always be the window Object,
which refers to the root-the global scope.
*/
// define a function
var myFunction = function () {
console.log(this); // [object Window]
};
// call it
myFunction();
// Object literals
/*
Inside Object literals, the this value will always refer to its own Object
*/
// create an object
var myObject = {};
// create a method on our object
myObject.someMethod = function () {
console.log(this); // myObject
};
// call our method
myObject.someMethod();
// Here, our window Object didn’t invoke the function - our Object did,
// so 'this' will refer to the Object that called it:
// Prototypes and Constructors
// The same applies with Constructors
var myConstructor = function () {
this.someMethod = function () {
console.log(this);
};
};
var a = new myConstructor();
a.someMethod();
// 'this' value will refer to the Constructor object, which will be myConstructor.
// Events
/*
When we bind events, the same rule applies, the this value points to the owner.
The owner in the following example would be the element.
*/
// let's assume .elem is <div class="elem"></div>
var element = document.querySelector('.elem');
var someMethod = function () {
console.log(this);
};
element.addEventListener('click', someMethod, false);
// Here, this would refer to <div class="elem"></div>.
// Dynamic this
/*
this is dynamic, which means the value could change
// if we just invoke the above function, `this` becomes the window object
someMethod(); // [object Window]
*/
// Changing 'this' context
/*
By using methods like .call(), .apply() and .bind(), you can change the context of a function
which in turn will change the 'this' value
You’ll use it when you want 'this' to refer to something different than the scope it’s in.
*/
// Using .call(), .apply() and .bind()
someMethod.call(anotherScope, arg1, arg1); // commas
someMethod.apply(anotherScope, [arg1, arg1]); // array
// With any of the above, they immediately invoke the function.
var myFunction = function () {
console.log(this);
};
myFunction.call();
// Without any arguments, the function is just invoked and this will remain as the window Object.
var numbers = [{
name: 'Mark'
},{
name: 'Tom'
},{
name: 'Travis'
}];
for (var i = 0; i < numbers.length; i++) {
console.log(this); // window
}
numbers.forEach(function () {
console.log(this); // window
});
/*
We could change each iteration’s scope to the current element’s value inside a regular for loop as well,
and use this to access object properties:
This is especially extensible when passing around other Objects that you might want to run through
the exact same functions.
*/
for (var i = 0; i < numbers.length; i++) {
(function () {
console.log(this.name); // Mark, Tom, Travis
}).call(numbers[i]);
}
numbers.forEach(function (item) {
(function () {
console.log(this.name); // Mark, Tom, Travis
}).call(item);
});
// .bind()
/*
Bind has the same effect as .call(), but instead binds the function’s context prior to being invoked,
this is essential to understand the difference.
Using .bind() will not invoke the function, it just “sets it up”.
*/
var obj = {};
var someMethod = function () {
console.log(this); // this = obj
}.bind(obj);
someMethod();
// Here’s a really quick example of how you’d setup the context for a function,
//I’ve used .bind() to change the context of the function,
// which by default the this value would be the window Object.
// Jumping scope / lexical scope
/*
Lexical scope is where variables and functions are still accessible to us in parent scopes.
*/
var obj = {};
obj.myMethod = function () {
console.log(this); // this = obj
setTimeout(function () {
console.log(this); // window object :O!!!
}, 100);
};
obj.myMethod();
// As we know, functions create scope, and setTimeout will be invoked by itself,
// defaulting to the window Object,
// and thus making the this value a bit strange inside that function.
// how to fix it?
// 1. using bind()
var obj = {};
obj.myMethod = function () {
console.log(this); // this = obj
setTimeout(function () {
console.log(this); // this = obj
}.bind(this), 100); // .bind() #ftw
};
obj.myMethod();
// 2. use jump scope trick (var that = this;)
var obj = {};
obj.myMethod = function () {
var that = this;
console.log(this); // this = obj
setTimeout(function () {
console.log(that); // that (this) = obj
}, 100);
};
obj.myMethod();