Skip to content

Instantly share code, notes, and snippets.

@connorrose
Last active September 10, 2020 07:00
Show Gist options
  • Save connorrose/22252565f4daf087150134e0a08c6b8d to your computer and use it in GitHub Desktop.
Save connorrose/22252565f4daf087150134e0a08c6b8d to your computer and use it in GitHub Desktop.
This Binding of Arrow Functions vs normal Function Expressions in various contexts
// GLOBAL SCOPE
const foo = () => this;
const bar = function() {return this};
const baz = globalThis;
const thud = this;
console.log('globalThis : this in global scope => ', baz === thud); // false
console.log('Arrow this : this in global => ', foo() === thud); // true
console.log('Function this : globalThis => ', bar() === baz); // true
--------------------------
// DEFINED IN GLOBAL SCOPE
function normalGlobal() {
// this => Global object (defaults to globalThis when no invoking object)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
}
const arrowGlobal = () => {
// this => {} [empty object] (this-value on Global object)
}
----------------------------
// DEFINED IN FUNCTION SCOPE
function wrapperInGlobal() {
function normalWrapped() {};
const arrowWrapped = () => {};
normalWrapped(); // this => Global object (default behavior)
arrowWrapped(); // this => Global object (inherits from wrappedInGlobal, which defaulted to globalThis)
}
wrapperInGlobal();
-----------------------------------------------
// DEFINED AS OBJECT METHODS (OBJECT IN GLOBAL)
const demoObject = {
demoProperty: true,
normalMethod: function() {},
arrowMethod: () => {}
}
demoObject.normalMethod(); // this => demoObject (bound at call time to invoking object)
demoObject.arrowMethod(); // this => {} [empty object] (bound at definition, which was in Global scope, so empty object)
--------------------------------------------
// DEFINED AS OBJECT METHODS WITHIN FUNCTION
function wrapperInGlobal() {
const demoObject = {
demoProperty: true,
normalMethod: function() {},
arrowMethod: () => {}
}
demoObject.normalMethod(); // this => demoObject (still bound at call time to invoking object
demoObject.arrowMethod(); // this => Global object, inherited from wrapperInGlobal
}
-------------------
// NESTED FUNCTIONS
function wrapperInGlobal() {
const demoObject = {
normalMethod: function() {
console.log('LOG 1: ' + this);
function nestedNormal() {
console.log('LOG 2: ' + this);
}
const nestedArrow = () => {
console.log('LOG 3: ' + this);
}
nestedNormal();
nestedArrow();
}
}
demoObject.normalMethod();
}
wrapperInGlobal();
// LOG 1: demoObject => normalMethod is bound to the object that invoked it
// LOG 2: Global object => default this value for a normal function call without an invoking object, regardless of enclosing scope
// LOG 3: demoObject => nestedArrow inherits it's this value from the surrounding scope, which is normalMethod
-------------------------------------------------
// DEFINED GLOBALLY / CALLED IN FUNCTION / METHOD
const arrowGlobal = () => {
console.log('Arrow: ' + this)
}
const normalGlobal = function() {
console.log('Normal: ' + this)
}
function wrapperInGlobal() {
const wrappedArrow = () => {
console.log(`Wrapped: ` + this)
}
const demoObject = {
demoProp: true,
normalMethod: function() {
console.log('Method: ' + this)
arrowGlobal();
normalGlobal();
}
}
demoObject.normalMethod();
}
wrapperInGlobal();
// Arrow: {} => empty object retained from definition in global
// Wrapped: Global object => inherited from wrapper function
// Normal: Global Object => default w/ no invoking object
// Method: demoObject => invoking object
// NOTE: Arrow / Wrapped do NOT inherit demoObject as this-value despite being invoked within normalMethod
----------------------
// *** SET TIMEOUT ***
// Global
function normalGlobal() {
console.log(`NORMAL GLOBAL: ${JSON.stringify(this)}`);
}
setTimeout(normalGlobal, 0); // this => some weird timer object
setTimeout(function () {
console.log(`NORMAL ANON: ${JSON.stringify(this)}`);
}, 0); // this => same weird timer object
const arrowGlobal = () => {
console.log(`ARROW GLOBAL: ${JSON.stringify(this)}`);
};
setTimeout(arrowGlobal, 0); // this => {} (empty object)
setTimeout(() => {
console.log(`ARROW ANON: ${JSON.stringify(this)}`);
}, 0); // this => {} (empty object)
// As Object Method
const demoObject = {
demoProp: true,
normalMethod() {
console.log(`normalMethod: ${JSON.stringify(this)}`);
},
arrowMethod: () => {
console.log(`normalMethod: ${JSON.stringify(this)}`);
},
};
setTimeout(demoObject.normalMethod, 0); // this => timer object [NOT demoObject as would be without setTimeout]
setTimeout(demoObject.arrowMethod, 0); // this => {} [empty object from Global]
// Within Object Method
const demoObject = {
demoProp: true,
normalMethod() {
function normalGlobal() {
console.log(`NORMAL GLOBAL: ${JSON.stringify(this)}`);
}
setTimeout(normalGlobal, 0);
setTimeout(function () {
console.log(`NORMAL ANON: ${JSON.stringify(this)}`);
}, 0);
const arrowGlobal = () => {
console.log(`ARROW GLOBAL: ${JSON.stringify(this)}`);
};
setTimeout(arrowGlobal, 0);
setTimeout(() => {
console.log(`ARROW ANON: ${JSON.stringify(this)}`);
}, 0);
},
};
demoObject.normalMethod();
// both "normal" functions still have a "this" bound to the timer object
// both arrow functions now have a "this" bound to the demoObject, which they inherited from the normalMethod
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment