Dynamic Context
When there's a will to fail, obstacles can be found.
function f() {
this.name = 'arfat';
}
f();
function f() {
'use strict'
this.name = 'arfat';
}
f();
function fun() {
return this;
}
fun();
typeof fun.call(2);
fun.apply(null);
fun.call(undefined);
typeof fun.bind(true)();
Functions can act in various ways in JavaScript. Let’s look at them —
Traditional Functions (that is, outside any object)
function add1(num1, num2) { // function declaration
return num1 + num2;
}
const add2 = function(num1, num2) { // function expression
return num1 + num2;
}
Object’s methods (inside an object)
const details = {
name: 'Arfat',
getName: function () {
return this.name;
}
}
details.getName(); // 'Arfat'
Constructor Functions
function Person(name) {
this.name = name;
}
const person1 = new Person('Arfat');
console.log(person1);
// Person { name: 'Arfat' }
Global Context
In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.
console.log(this === global);
console.log(this === window);
Simple Function
When a function is executed normally (without any help of object), the this context is set to global.
function f() {
return this;
}
console.log(f() === global);
console.log(f() === window);
Strict Mode vs Sloppy Mode
'use strict';
foo = 17;
foo = 17;
var undefined = 9
var Infinity = 10;
if (undefined) {
console.log('is true');
}
'use strict';
var undefined = 9;
var Infinity = 10;
if (undefined) {
console.log('is true');
}
Context in Strict mode
In sloppy mode, this is always an object:
function fun() { return this; }
fun() === global;
typeof fun.call(2) === 'object';
fun.apply(null) === global;
fun.call(undefined) === global;
typeof fun.bind(true)() === 'object';
'use strict';
function fun() { return this; }
fun() === undefined;
fun.call(2) === 2;
fun.apply(null) === null;
fun.call(undefined) === undefined;
fun.bind(true)() === true;
const obj = {
name: 'Arfat',
f: function() {
console.log(this.name);
}
};
obj.f();
const obj = {
name: 'Arfat',
f: function() {
console.log(this.name);
}
};
var g = obj.f;
g();
function returnThisStrict() {
'use strict';
return this
}
var obj = { method: returnThisStrict };
console.log(obj.method());
const obj = {
name: 'Arfat',
f: function() {
console.log(this.name);
}
};
setTimeout(obj.f, 2000);
var obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop: function () {
this.friends.forEach(
function(friend) { // (1)
console.log(this.name+' knows '+friend); // (2)
}
);
}
};
obj.loop();
An object is a
collection of properties
A
property is a
named container for a
value
w/ some additional attributes
The name of a property is called a key ;
thus, an object can be considered as
a collection of key-value pairs .
There are similar concepts in other programming languages,
e.g.,
Map, Dictionary, Associative Array, Symbol Table, Hash Table
, ...
all objects in JavaScript are maps (dictionaries) from strings to values.
A (key, value) entry in an object is called a property . The key of a property is always a text string (ES5).
Properties (or named data properties)
Accessors (or named accessor properties)
Internal properties
Exist only in the ECMAScript language specification.
Dot Notation
var jane = {
name: 'Jane',
'desc.func': function () {
return 'Person named ' + this.name;
},
};
$ jane.name
// 'jane'
$ jane['desc.func']
// [Function]
Bracket Notation
this refers to the object on which the method has been invoked
> var obj = { method: returnThisStrict };
> obj.method() === obj
true
Normal functions in sloppy mode
function returnThisSloppy() {
return this
}
> returnThisSloppy() === window
true
Normal functions in strict mode
function returnThisStrict() {
'use strict';
return this
}
> returnThisStrict() === undefined
true
var counter = {
count: 0,
inc: function () {
this.count++;
}
}
We have called the value of counter.inc as a function.
Hence, this is the global object and we have performed window.count++ .
window.count does not exist and is undefined . Applying the ++ operator to it sets it to NaN.
Use strict mode for avoiding this.
> var func = counter.inc;
> func()
> counter.count // didn’t work
0
> var func3 = counter.inc.bind(counter);
> func3()
> counter.count // it worked!
1
function callIt(callback) {
callback();
}
> callIt(counter.inc)
❌
✓
> callIt(counter.inc.bind(counter))
Changing Function context
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
Syntax: function.bind(thisArg[, arg1[, arg2[, ...]]])
The call() method calls a function with a given this value and arguments provided individually.
Syntax: function.call(thisArg, arg1, arg2, ...)
function greet() {
var reply = [
this.animal,
'typically sleep between',
this.sleepDuration
].join(' ');
console.log(reply);
}
var obj = {
animal: 'cats', sleepDuration: '12 and 16 hours'
};
greet.call(obj);
// cats typically sleep between 12 and 16 hours
The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).
Note: While the syntax of this function is almost identical to that of call(), the fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments.
var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
console.log(max);
// expected output: 7
var min = Math.min.apply(null, numbers);
console.log(min);
// expected output: 2
Syntax: function.apply(thisArg, [argsArray])
var obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop: function () {
this.friends.forEach(
function (friend) { // (1)
console.log(this.name+' knows '+friend); // (2)
}
);
}
};
obj.loop()
What to do?
loop: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' knows '+friend);
});
}
loop: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' knows '+friend);
}.bind(this)); // (1)
}
this.friends.forEach(function (friend) {
console.log(this.name+' knows '+friend);
}, this);
() => { ... } // no parameter
x => { ... } // one parameter, an identifier
(x, y) => { ... } // several parameters
x => { return x * x } // block
x => x * x
const arr = [1, 2, 3];
const squares = arr.map(x => x * x);
New Kind of Function
Specifying a body:
The complete list of variables whose values are determined lexically is:
arguments super this new.target
Arrow functions versus normal functions
An arrow function is different from a normal function in only two ways:
var obj = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
loop: function () {
this.friends.forEach(
(friend) => {
console.log(this.name+' knows '+friend);
}
);
}
};
obj.loop()
function showInResult(str) {
console.log(str);
}
const li = document.querySelector('#second-li');
const ul = document.querySelector('ul');
ul.addEventListener('click', function(event) {
showInResult(this.tagName);
showInResult(event.target.tagName);
showInResult(`handler attached to ${event.currentTarget.tagName}`);
})
<button onclick={showInResult(this.tagName)}>
Click
</button>
<ul>
<li>Item 1</li>
<li id="second-li">Item 2</li>
</ul>
In DOM event handler
this is set to the element the event fired from
function bluify(e) {
// Always true
console.log(this === e.currentTarget);
// true when currentTarget and target are the same object
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
target = element that triggered event; currentTarget = element that listens to event.
In an inline event handler
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
When the code is called from an inline on-event handler, its this is set to the DOM element on which the listener is placed: