A walk in JavaScript
DAY 7
this
Keyword- Introduction
- Resolving
this
- Explicitly binding
this
through prototype methodsFunction.prototype.bind()
Function.prototype.apply()
Function.prototype.call()
- Strict mode
- What happens on strict mode?
- Semantic Differences
- Arrow Functions
- Generators
- Exercises
this
Keyword
Over and over again I see engineers struggling with this
topic; is so weird!! Long ago I found myself in the same situation, like, being writing code for many years and still … never took the time to really understand this
when this
is one of the most important and powerful features in JavaScript!
Engineers we feel so frustrated about this
that there’s even a joke for this
!
JavaScript makes me want to flip the table and say “F* this shit”, but I can never be sure what **
this
refers to.
The good things came when I took the responsibility of this
and accepted the guilt was entirely mine.
Why this intro? because there are tons of articles regarding this
but everything about this
was written by Kyle Simpson who dedicated a whole book for this
topic , so we’re gonna read and study it until we breath this
.
Resolving this
Let’s take a look at the following chapters of You Don’t Know JS: this & Object Prototypes - 1st Edition
- Chapter 1: this Or That?
- Chapter 2: this All Makes Sense Now!
- Chapter 5: Prototypes
- Chapter 6: Behavior Delegation
Now let’s see how ECMAScript specifies the mechanism to resolve this
.
In the other hand, MDN describes this
on the Operators section
Explicitly binding this
through prototype methods
Now we’ve learned that this
has specific rules and it’s resolved at run-time, and we saw that the function
prototype has 3 methods to explicitly define where to point when this
needs to be resolved during it’s execution.
Now, there’s a catch!!! it seems that depending on a thing called mode, that depending on it’s strictness or non-strictness (a.k.a. Sloppy) it’ll alter the semantics and behavior of many things including this
.
Strict Mode
- MDN - Strict Mode
- MDN - Transitioning to Strict Mode
- ECMAScript 5.1 - Strict Mode ECMAScript 2015 - Strict Mode
- The ECMAScript 2016 change you probably don’t know
- Speaking JavaScript - Chp.7 Strict Mode” - by Dr. Axel Rauschmayer
What happens on strict mode?
TL;DR
- Eliminates some JavaScript
silent errors
by changing themto throw errors
. - Fixes mistakes that make it difficult for JavaScript engines to perform optimizations: strict mode code can sometimes be made to run faster than identical code that’s not strict mode.
- Prohibits some syntax likely to be defined in future versions of ECMAScript.
Semantic differences
this
resolution won’t propagate to the global scope, thus for a strict mode function, the specified this is not boxed into an object, and if unspecified, this will beundefined
- arguments doesn’t alias named function arguments
eval
doesn’t create a new variable in the scope from which it was called,eval
of strict mode code does not introduce new variables into the surrounding scope.
When adding 'use strict';
the following cases will throw an Error:
- SyntaxError
- Octal syntax
var n = 023;
with
statement- Using delete on a variable name
delete myVariable
; - Using
eval
orarguments
as variable or function argument name - Using one of the newly reserved keywords (in prevision for ECMAScript 2015):
implements
,interface
,let
,package
,private
,protected
,public
,static
,- and
yield
- Escape characters are not allowed
var y = \010;
- Declaring function in blocks
if (a < b) { function f() {} }
- Obvious errors
- Declaring twice the same name for a property name in an object literal
{a: 1, b: 3, a: 7}
This is no longer the case in ECMAScript 2015 (bug 1041128). - Declaring two function parameters with the same name function
f(a, b, b) {}
- Declaring twice the same name for a property name in an object literal
- Octal syntax
- TypeError
- Writing to a get-only property is not allowed
- Writing to a read-only property is not allowed
- Deleting an undeletable property is not allowed
delete Object.prototype
- Setting properties on primitive values
false.true = '';
,(14).sailing = 'home';
,'with'.you = 'far away';
- Runtime errors
- Setting a value to an undeclared variable
- Trying to delete a non-configurable property
- Poisoned arguments and function properties, e.g. accessing
arguments.callee
,arguments.caller
,anyFunction.caller
, oranyFunction.arguments
- ReferenceError
- Using a variable, without declaring it
Arrow Functions
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
Source: MDN - Arrow Functions
Syntax
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }
// The parameter list for a function with no parameters should be written with a pair of parentheses.
() => { statements }
// Parenthesize the body of a function to return an object literal expression:
params => ({foo: bar})
// Rest parameters and default parameters are supported
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements }
// Destructuring within the parameter list is also supported
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
One of the most expected and misused features of ES6 is the Arrow Function. Undoubtedly powerful it might also derive in a headache if you don’t really know how they work and which are the differences between the full body notation and the arrow notation.
Let’s take a look at YDKJS - ES6 & Beyond - chapter 2
Generators
So far we’ve seen (except for the iterators) only run-to-completion examples of code. It means, “the execution won’t stop until it’s done or fails”. What if I tell you there’s a feature that let you define a function capable of being paused midway and resumed later?
Together with iterators
ES6 introduced something called generators
.
The Generator object is returned by a generator function and it conforms to both the iterable protocol and the iterator protocol.
There are 2 ways to create a generator object
-
function* name([param[, param[, ... param]]]) { statements }
-
let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor let myGenerator = new GeneratorFunction ([arg1[, arg2[, ...argN]],] functionBody)
Note that GeneratorFunction is not a global object.
generator
function objects created with theGeneratorFunction
constructor are parsed when the function is created. This is less efficient than declaring a generator function with afunction* expression
and calling it within your code, because such functions are parsed with the rest of the code.Source: MDN GeneratorFunction
Since this is a particularly complex topic, with several nuances, let’s try to understand them through examples:
Example of execution sequence
/**
*
* @param {number} initialValue
* @returns {Object} Generator
*/
function* bottlesOfBeer (initialValue) {
let bob = initialValue;
let lastMessage = `No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, ${bob} bottles of beer on the wall.`;
while (true) {
console.log(`${bob} bottles of beer on the wall, ${bob} bottles of beer.`);
yield bob--;
console.log(`Take one down and pass it around, ${bob} bottles of beer on the wall.`);
if (bob < 1) {
bob = initialValue;
console.log(lastMessage);
}
}
}
let bob = bottlesOfBeer(100);
bob.next();
// log -> 5 bottles of beer on the wall, 5 bottles of beer.
// statement completion value -> {value: 5, done: false}
bob.next();
// log -> Take one down and pass it around, 4 bottles of beer on the wall.
// 4 bottles of beer on the wall, 4 bottles of beer.
// statement completion value -> {value: 4, done: false}
// ... 3 more calls
bob.next();
// log -> Take one down and pass it around, 0 bottles of beer on the wall.
// No more bottles of beer on the wall, no more bottles of beer.
// Go to the store and buy some more, 5 bottles of beer on the wall.
// 5 bottles of beer on the wall, 5 bottles of beer.
// statement completion value -> {value: 5, done: false}
// guess what happens now?
Passing values through next
/**
*
* @returns {Object} Generator
*/
function* passingValToNext () {
let val = 10;
while (true) {
console.log(`UP val=${val}`);
val = yield val + 10;
console.log(`DOWN val=${val}`);
}
}
let pvtn = passingValToNext();
// statement completion value -> passingValToNext {<suspended>}
pvtn.next(2);
// log -> UP val=10
// statement completion value -> {value: 20, done: false}
pvtn.next(7);
// log -> DOWN val=7
// log -> UP val=7
// statement completion value -> {value: 17, done: false}
// WAIT! WHAT??!!!!
// how does it work?
Sample combining initial value and passing value to next
/**
*
* @param {Number} expectedTotal
* @returns {Object} Generator
*/
function* calculateDownloadProgress (expectedTotal) {
let totalDownloaded = 0;
let newItems = 0;
while (true) {
totalDownloaded += newItems || 0; // lazy verification for the value passed by `next`
let percent = ((totalDownloaded / expectedTotal) * 100).toFixed(2);
newItems = yield `${percent}%`;
}
}
let progress = calculateDownloadProgress(1024);
// statement completion value -> undefined
progress.next()
// statement completion value -> {value: "0.00%", done: false}
progress.next(15)
// statement completion value -> {value: "1.46%", done: false}
progress.next(500)
// statement completion value -> {value: "50.29%", done: false}
DIY
/**
*
* @returns {Object} Generator
*/
function* spinGen() {
while(true){
yield* ['\\', '|', '/', '--'];
}
}
// now you add the code to see the output
Let’s take some time to read and discuss:
- ECMAScript Generator Function
- YDKJS - ES6 & Beyond - CH3 - Generators - by Kyle Simpson
- The Basics Of ES6 Generators - By Kyle Simpson
- 2ality - ES6 generators in depth - by Dr. Axel Rauschmayer
Exercises
Let’s open our test files:
Now open your terminal.
- Make sure you’re at the project location
- If you didn’t install all the packages yet then run
npm i
for a fresh dependency install, ornpm ci
for an installation based on the lock file. - Type
npm run test:watch
, this will start running your tests every time you make a change.
Our task is to make ALL our DAY 7 tests pass ;)