Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lionyxml/javascript-notes

Some nots on JS using a .org file.
https://github.com/lionyxml/javascript-notes

Last synced: about 1 month ago
JSON representation

Some nots on JS using a .org file.

Awesome Lists containing this project

README

        





JavaScript Notes











JavaScript Notes



Table of Contents







1. Await inside loops



What will be the output of this snippet?



const arr = [1 ,2 , 3 , 4];
let resValue = 1;

const incAfter1Sec = () => new Promise((res, rej) => {
setTimeout(() => { res(resValue++) }, 1000);
});

const testing = async () => {
console.log('🚀 Start')

arr.forEach(async (each) => {
const val = await incAfter1Sec()
console.log("🚀 each", val)

})

console.log('🚀 End');

}

testing();


What about instead of a forEach, mapping?



const arr = [1 ,2 , 3 , 4];
let resValue = 1;

const incAfter1Sec = () => new Promise((res, rej) => {
setTimeout(() => { res(resValue++) }, 1000);
});

const testing = async () => {
console.log('🚀 Start')

arr.map(async (each) => {
const val = await incAfter1Sec()
console.log("🚀 each", val)

})

console.log('🚀 End');

}

testing();



In simple terms, each (from the code) is called on separate function
that is syncrhonous. forEach (and map) expects a synchronous
function and does not wait for promises. This can be explained by
the concept of `generators`.


`each` iteration end calls the `next()` method, `yield` the value
and exit, then, call `next()` again, and since `next()` accepts only
synchronous stuff, promises don`t work as intended when we use
await.

The `solution`, simply use a for loop:




const arr = [1 ,2 , 3 , 4];
let resValue = 1;

const incAfter1Sec = () => new Promise((res, rej) => {
setTimeout(() => { res(resValue++) }, 1000);
});

const testing = async () => {
console.log('🚀 Start')

for (let i = 0; i < arr.length ; i++) {
console.log("🚀 each", i);
await incAfter1Sec()

}

console.log('🚀 End');
}

testing();


It could be also written in this fashion:




const arr = [1 ,2 , 3 , 4];
let resValue = 1;

const incAfter1Sec = () => new Promise((res, rej) => {
setTimeout(() => { res(resValue++) }, 1000);
});

const testing = async () => {
console.log('🚀 Start')

for (const i in arr) {
console.log("🚀 each", i);
await incAfter1Sec()

}

console.log('🚀 End');
}

testing();






3. Curry or Partial Application?





3.1. Definitions





3.1.1. Application




The process of applying a function to its arguments in order to
produce a return value.






3.1.2. Partial Application




The process of applying a function to some of its arguments. The
partially applied function gets returned for later use. In other
words, a function that takes a function with multiple parameters
and returns a function with fewer parameters. Partial
application fixes (partially applies the function to) one or
more arguments inside the returned function, and the returned
function takes the remaining parameters as arguments in order to
complete the function application.






3.1.3. Curry




A function that takes a function with multiple parameters as
input and returns a function with exactly one parameter.









4. What is: Function Composition





4.1. Function composition




Is the process of combining two or more functions to produce a new
function. Composing funcitons together is like snapping together a
series of pipes for our data to flow throug.


The composition of functions `f` and `g` can be defined as
`f(g(x))`, which evaluates from the inside out, right to left. In
other words:



  1. `x`

  2. `g`

  3. `f`





4.2. Example of what is



You want to do the following:


  1. split the name into an array on spaces

  2. map the name to lower case

  3. join with dashes

  4. encode the URI component



const toSlug = input => encodeURIComponent(
input.split(' ')
.map(str => str.toLowerCase())
.join('-')
);
console.log(toSlug('JS Cheerleader')); // 'js-cheerleader'

Not bad, but what about do it more readable?



const toSlug = input => encodeURIComponent(
join('-')(
map(toLowercase)(
split(' ')(
input
)
)
)
)

console.log(toSlug('JS Cheerleader')); // 'js-cheerleader'


It looks even harder to read, but wait a bit…

If we use composable forms of common utilities, like.



const curry = fn => (...args) => fn.bind(null, ...args);
const map = curry((fn, arr) => arr.map(fn));
const join = curry((str, arr) => arr.join(str));
const toLowerCase = str => str.toLowerCase();

const split = curry((splitOn, str) => str.split(splitOn));



Or even production-tested versions available from Loadash/fp or
ramda. You can import like this:



import { curry, map, join, split } from 'lodash/fp';
// ...or like this...
const curry = require('lodash/fp/curry');
const map = require('lodash/fp/map');


To be honest, this implementation of curry is a little bit lazy, a
real curry would always produce a unary function. Instead, this is
a simple partial application. For the porposes of this demo, it
will work interchangeable.


Going back to our `toSlug()` implementation, it looks like a lot
of nesting, and a bit confusing to read. We can flatten the
nesting with a function that will compose these functions
automatically, meaning that it will take the output from one
function and automatically patch it to input of the next function
until it spits out the final value.


Come to think of it we have an extras utility that sounds like
something like that. It takes a list of values and applies a
function to each of those values, accumulating a single result.
The values themselves can be functions. The functions is called
`reduce()`, but to match the compose behavior above, we need it to
reduce right to left, instead of left to right.


Good thing there's a `reduceRight()` that does exactly what we're
looking for:




const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);


Like `.reduce()`, the array `.reduceRight()` method takes a
reducer function and an initial value (`x`). We iterate over the
array functions (from right to left), applying each in turn to the
accumulated value (`v`).


With compose, we can rewrite our composition above without the
nesting:




const toSlug = compose(
encodeURIComponent,
join('-'),
map(toLowerCase),
split(' ')
);

console.log(toSlug('JS Cheerleader')); // 'js-cheerleader'



Of course, `compose()` comes with a loadash/fp or ramda as well:



import { compose } from 'lodash/fp';
// or
const compose = require('lodash/fp/compose');


Compose is great when you're thinking in terms of the mathematical
form of composition, inside out… but what if you want to think in
terms of the sequence from left to right?


There’s another form commonly called `pipe()`. Lodash calls it
`flow()`:



const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);

const fn1 = s => s.toLowerCase();
const fn2 = s => s.split('').reverse().join('');
const fn3 = s => s + '!'

const newFunc = pipe(fn1, fn2, fn3);
const result = newFunc('Time'); // emit!



Notice the implementation is exactly the same as `compose()`,
except that we're using `.reduce()` instead of `.reduceRight()`,
which reduces left to right instead of right to left.


Let's look at our `toSlug()` function implemented with `pipe()`:



const toSlug = pipe(
split(' '),
map(toLowerCase),
join('-'),
encodeURIComponent
);

console.log(toSlug('JS Cheerleader')); // 'js-cheerleader'


For me, this is much easier to read.


Hardcore functional programmers define their entire application in
terms of function compositions. I use it frequently to eliminate
the need for temporary variables. Look at the `pipe()` version of
`toSlug()` carefully and you might notice something special.


In imperative programming, when you're performing transformations
on some variable, you’ll find references to the variable in each
step of the transformation. The `pipe()` implementation above is
written in a points-free style, which means that it does not
identify the arguments on which it operates at all.


I frequently use pipes in things like unit tests and Redux state
reducers to eliminate the need for intermediary variables which
exist only to hold transient values between one operation and the
next.


That may sound weird at first, but as you get practice with it,
you'll find that in functional programming, you're working with
very abstract, generalized functions in which the names of things
don't matter so much. Names just get in the way. You may start to
think of variables as unnecessary boilerplate.


That said, I'm of the opinion that points-free style can be taken
too far. It can become too dense, and harder to understand, but if
you get confused, here's a little tip… you can tap into the flow
to trace what's going on:



const trace = curry((label, x) => {
console.log(`== ${ label }: ${ x }`);
return x;
});

Here's how you use it:



const toSlug = pipe(
trace('input'),
split(' '),
map(toLowerCase),
trace('after map'),
join('-'),
encodeURIComponent
);

console.log(toSlug('JS Cheerleader'));
// '== input: JS Cheerleader'
// '== after map: js,cheerleader'
// 'js-cheerleader'



`trace()` is just a special form of the more general `tap()`,
which lets you perform some action for each value that flows
through the pipe. Get it? Pipe? Tap? You can write `tap()` like
this:



const tap = curry((fn, x) => {
fn(x);
return x;
});


Now you can see how `trace()` is just a special-cased `tap()`:



const trace = label => {
return tap(x => console.log(`== ${ label }: ${ x }`));
};


You should be starting to get a sense of what functional
programming is like, and how partial application & currying
collaborate with function composition to help you write programs
which are more readable with less boilerplate.







5. What is: Difference Between
Class & Prototypal Inheritance





5.1. Class Inheritance




A Class is like a blueprint - a description of the object to be
created. Classes inherit from classes and create subclass
relationships: hierarchical class taxonomies.


Instances are typically instantiated via constructor functions
with the new keyword. Class inheritance may or may
not use the class keyword from ES6. Classes as you
may know them from languages like Java don't technically exist in
Javascript. Constructor functions are used, instead. The ES6
class keyword desugars to a constructor function:



class Foo {};
console.log(typeof Foo);


function
undefined


In JS, class inheritance is implemented on top of prototypal
inheritance, but that does not mean that it does the same thing:


JS class inheritance uses the prototype chain o wire the child
`Constructor.prototype` to the parent `Constructor.prototype` for
delegation. Usually, the `super()` constructor is also called.
Those steps form
single-ancestor parent/child hierarchies and
create the tightest coupling available in OO design.





5.2. Prototypal Inheritance




A prototype is a working object instance. Objects inherit directly
from other objects.


Instances may be composed from many different source objects,
allowing for easy selective inheritance and flat
Prototype delegation hierarchy. In other words,
class taxonomies are not an automatic side-effect of prototypal
OO: a critical distinction.


Instances are typically instantiated via factory functions, object
literals, or `Object.create()`.





5.3. Why does this Matter?




Inheritance is fundamentally a code reuse mechanism: A way for
different kinds of objects to share code. The way that you share
code matters because if you get it wrong, it can create a lot of
problems, specifically:


Class inheritance creates parent/child objects taxonomies as
side-effect


Those are virtually impossible to get right for all new use cases,
and widespread use of a base class leads to the
fragile base class problem, which makes them difficult to fix when you get them wrong. In
fact, class inheritance causes many well known problems in OO
design:



  • The tight coupling problem (class inheritance is the tightest
    coupling avaiable in oo design), which leads to…

  • The fragile base class problem


  • Inflexible hierarchy problem (eventually, all evolving
    hierarchies are wrong for new uses)


  • The duplication by necessity problem (due to inflexible
    hierarchies, new use cases are often shoe-horned in by
    duplicating, rather than adapting existing code)


  • The Gorilla/banana problem (What you wanted was a banana, but
    what you got was a gorilla holding the banana, and the entire
    jungle)

The solution to all of these problems:


* Favor object composition over class inheritance. *


~ The Gang of Four, "Design Patterns: Elements of Reusable Object
Oriented Software"





5.4. Three Different Kinds of
Prototypal Inheritance





5.4.1. Before the three




Before we dive into the other kinds of inheritance, let's take a
closer look at what I mean by class inheritance:




// Class Inheritance Example
// NOT RECOMMENDED. Use object composition, instead.
class GuitarAmp {
constructor ({ cabinet = "spruce", distortion = "1", volume = "0" } = {}) {
Object.assign(this, {
cabinet, distortion, volume
});
}
}

class BassAmp extends GuitarAmp {
constructor (options = {}) {
super(options);
this.lowCut = options.lowCut;
}
}

class ChannelStrip extends BassAmp {
constructor (options = {}) {
super(options);
this.inputLevel = options.inputLevel;
}
}

const myAmp = new BassAmp();
const actualAmp = Object.keys(myAmp);
const expectedAmp = ["cabinet", "distortion", "volume", "lowCut"];

console.log("1.", actualAmp, expectedAmp, " instance should inherit props from GuitarAmp and BassAmp");

const myStrip = new ChannelStrip();
const actualStrip = Object.keys(myStrip);
const expectedStrip = ["cabinet", "distortion", "volume", "lowCut", "inputLevel"];

console.log("2.", actualStrip, expectedStrip, " instance should inherit props from GuitarAmp, BassAmp and ChannelStrip");



1. [ 'cabinet', 'distortion', 'volume', 'lowCut' ] [ 'cabinet', 'distortion', 'volume', 'lowCut' ] instance should inherit props from GuitarAmp and BassAmp
2. [ 'cabinet', 'distortion', 'volume', 'lowCut', 'inputLevel' ] [ 'cabinet', 'distortion', 'volume', 'lowCut', 'inputLevel' ] instance should inherit props from GuitarAmp, BassAmp and ChannelStrip
undefined


`BassAmp` inherits from `GuitarAmp`, and `ChannelStrip` inherits
from `BassAmp` & `GuitarAmp`. This is an example of how OO
design goes wrong. A channel strip isn’t actually a type of
guitar amp, and doesn’t actually need a cabinet at all. A better
option would be to create a new base class that both the amps
and the channel strip inherits from, but even that has
limitations.


Eventually, the new shared base class strategy breaks down, too.


There’s a better way. You can inherit just the stuff you really
need using object composition:



// Composition Example

const distortion = { distortion: 1 };
const volume = { volume: 1 };
const cabinet = { cabinet: "maple" };
const lowCut = { lowCut: 1 };
const inputLevel = { inputLevel: 1 };

const GuitarAmp = (options) => {
return Object.assign({}, distortion, volume, cabinet, options);
};

const BassAmp = (options) => {
return Object.assign({}, lowCut, volume, cabinet, options);
};

const ChannelStrip = (options) => {
return Object.assign({}, inputLevel, lowCut, volume, options);
};

const msgGuitarAmp = "should have distortion, volume, and cabinet";
const levelGuitarAmp = 2;
const cabinetGuitarAmp = "vintage";

const actualGuitarAmp = GuitarAmp({
distortion: levelGuitarAmp,
volume: levelGuitarAmp,
cabinet: cabinetGuitarAmp
});
const expectedGuitarAmp = {
distortion: levelGuitarAmp,
volume: levelGuitarAmp,
cabinet: cabinetGuitarAmp,
};

console.log("1.", actualGuitarAmp, expectedGuitarAmp, msgGuitarAmp);

const msgBassAmp = "should have volume, lowCut, and cabinet";
const levelBassAmp = 2;
const cabinetBassAmp = "vintage";

const actualBassAmp = BassAmp({
lowCut: levelBassAmp,
volume: levelBassAmp,
cabinet: cabinetBassAmp
});
const expectedBassAmp = {
lowCut: levelBassAmp,
volume: levelBassAmp,
cabinet: cabinetBassAmp,
};

console.log("2.", actualBassAmp, expectedBassAmp, msgBassAmp);

const msgChannel = "should have inputLevel, lowCut, and volume";
const levelChannel = 2;

const actualChannel = ChannelStrip({
inputLevel: levelChannel,
lowCut: levelChannel,
volume: levelChannel,
});
const expectedChannel = {
inputLevel: levelChannel,
lowCut: levelChannel,
volume: levelChannel,
};

console.log("2.", actualChannel, expectedChannel, msgChannel);



1. { distortion: 2, volume: 2, cabinet: 'vintage' } { distortion: 2, volume: 2, cabinet: 'vintage' } should have distortion, volume, and cabinet
2. { lowCut: 2, volume: 2, cabinet: 'vintage' } { lowCut: 2, volume: 2, cabinet: 'vintage' } should have volume, lowCut, and cabinet
2. { inputLevel: 2, lowCut: 2, volume: 2 } { inputLevel: 2, lowCut: 2, volume: 2 } should have inputLevel, lowCut, and volume
undefined


If you look carefully, you might see that we're being much more
specific about which objects get which properties because with
composition, we can. It wasn't really an option with class
inheritance. When you inherit from a class, you get everything,
even if you don’t want it.


At this point, you may be thinking to yourself, "that's nice,
but where are the prototypes?"


To understand that, you have to understand that there are three
different kinds of prototypal OO.





5.4.2. Concatenative
inheritance




The process of inheriting features directly from one object to
another by copying the source objects properties. In JavaScript,
source prototypes are commonly referred to as mixins. Since ES6,
this feature has a convenience utility in JavaScript called
`Object.assign()`. Prior to ES6, this was commonly done with
Underscore/Lodash's `.extend()` jQuery's `$.extend()`, and so
on… The composition example above uses concatenative
inheritance.





5.4.3. Prototype delegation




In JavaScript, an object may have a link to a prototype for
delegation. If a property is not found on the object, the lookup
is delegated to the delegate prototype, which may have a link to
its own delegate prototype, and so on up the chain until you
arrive at `Object.prototype`, which is the root delegate. This
is the prototype that gets hooked up when you attach to a
`Constructor.prototype` and instantiate with `new`. You can also
use `Object.create()` for this purpose, and even mix this
technique with concatenation in order to flatten multiple
prototypes to a single delegate, or extend the object instance
after creation.

The JS prototype chain:




+---------------------+ +----------------------+ +---------------------+
| George | | Prototype +--------+ Object.prototype |
| name +-------+ hello() | | ... |
| [[Prototype]] | | [[Prototype]] | | |
| | | | | |
+---------------------+ +----------------------+ +---------------------+





5.4.4. Functional
inheritance




In JavaScript, any function can create an object. When that
function is not a constructor (or `class`), it's called a
factory function. Functional inheritance works by producing an
object from a factory, and extending the produced object by
assigning properties to it directly (using concatenative
inheritance). Douglas Crockford coined the term, but functional
inheritance has been in common use in JavaScript for a long
time.


As you're probably starting to realize, concatenative
inheritance is the secret sauce that enables object composition
in JavaScript, which makes both prototype delegation and
functional inheritance a lot more interesting.


When most people think of prototypal OO in JavaScript, they
think of prototype delegation. By now you should see that
they’re missing out on a lot. Delegate prototypes aren't the
great alternative to class inheritance — object composition is.






5.5. Why Composition is Immune
to the Fragile Base Class Problem




To understand the fragile base class problem and why it doesn’t
apply to composition, first you have to understand how it happens:

`A` is the base class

`B` inherits from `A`

`C` inherits from `B`

`D` inherits from `B`


`C` calls `super`, which runs code in `B`. `B` calls `super` which
runs code in `A`.


`A` and `B` contain unrelated features needed by both `C` &
`D`. `D` is a new use case, and needs slightly different behavior
in `A`'s init code than `C` needs. So the newbie dev goes and
tweaks `A`'s init code. `C` breaks because it depends on the
existing behavior, and `D` starts working.


What we have here are features spread out between `A` and `B` that
`C` and `D` need to use in various ways. `C` and `D` don't use
every feature of `A` and `B`… they just want to inherit some stuff
that's already defined in `A` and `B`. But by inheriting and
calling `super`, you don't get to be selective about what you
inherit. You inherit everything:


** …the problem with object-oriented languages is they’ve
got all this implicit environment that they carry around with
them. You wanted a banana but what you got was a gorilla holding
the banana and the entire jungle.” ~ Joe Armstrong — “Coders at
Work **

With Composition

Imagine you have features instead of classes:



feat1, feat2, feat3, feat4


`C` needs `feat1` and `feat3`, `D` needs `feat1`, `feat2`,
`feat4`:



const C = compose(feat1, feat3);
const D = compose(feat1, feat2, feat4);


Now, imagine you discover that `D` needs slightly different
behavior from `feat1`. It doesn’t actually need to change `feat1`,
instead, you can make a customized version of `feat1` and use
that, instead. You can still inherit the existing behaviors from
`feat2` and `feat4` with no changes:



const D = compose(custom1, feat2, feat4);

And `C` remains unaffected.


The reason this is not possible with class inheritance is because
when you use class inheritance, you buy into the whole existing
class taxonomy.


If you want to adapt a little for a new use-case, you either end
up duplicating parts of the existing taxonomy (the duplication by
necessity problem), or you refactor everything that depends on the
existing taxonomy to adapt the taxonomy to the new use case due to
the fragile base class problem.

Composition is immune to both.





5.6. You think you know
Prototypes, but…




If you were taught to build classes or constructor functions and
inherit from those, what you were taught was not prototypal
inheritance. You were taught how to mimic class inheritance using
prototypes.


In JavaScript, class inheritance piggybacks on top of the very
rich, flexible prototypal inheritance features built into the
language a long time ago, but when you use class inheritance —
even the ES6+ `class` inheritance built on top of prototypes,
you're not using the full power & flexibility of prototypal
OO. In fact, you're painting yourself into corners and opting into
all of the class inheritance problems.


Using class inheritance in JavaScript is like driving your new
Tesla Model S to the dealer and trading it in for a rusted out
1973 Ford Pinto.








6. Referential Transparency /
Opacity in JS




Referential transparency is used in many domains, for example:
mathematics, logic, linguistics, philosophy and programming. It has
quite changed meanings in each of these domains.


Referential transparency in programming relates to programs. By the
way of programs are composed of subprograms that are programs
themselves. It relates to those subprograms, also. Subprograms can
be signified by methods.

Example:




var identifty = (i) => { return i }



  • We have defined a simple function named identity in the above code
    snippet.

  • This function return whatsoever we're passing as its input.

  • That is, if we passes 10, it returns back the value 10.

  • As the function is only acts as a mirror and identity.

  • Our function does work only on the incoming argument `i`.

  • There is no global reference inside our function.


At the present conceive this function is used between other function
calls like this:




sum(4,5) + identity(1)


By means of our Referential Transparency definition we may change
the above statement into this:




sum(4,5) + 1


  • At this moment this process is called a Substitution model.


  • By way of we can directly substitute the result of the function as
    is with its value.


  • Generally due to the function doesn’t rely on other global
    variables for its logic.

  • This leads to equivalent code and caching.


  • We may easily run the above function with several threads deprived
    of even the need of synchronizing.


  • The motive for synchronizing comes from the detail that threads
    shouldn't do global data when running equivalent.


  • Functions that follow Referential Transparency depends only on
    inputs from its argument.


  • Therefore, threads are allowed to run without any locking
    mechanism.


  • Meanwhile the function returns the same value for the given input.


  • We may, actually cache it. For instance, see there is a function
    called factorial.

  • Calculates the factorial of the given number.


  • Factorial takes the input as its argument for which the factorial
    needs to be calculated.

  • We all recognize the factorial of 5 going to be 120.


  • What would be if the user calls the factorial of 5 a second time?


  • We see that the result is to be 120 if the factorial function
    follows Referential transparency as before.

  • It only be contingent on the input argument.


  • We may cache the values of our factorial function by keeping in
    mind these characters.


  • Thus if a factorial is called for the second time with the input
    as `5`, we can return the cached value instead of calculating once
    again.


`An expression is called referentially transparent if it can be
replaced with its corresponding value (and vice-versa) without
changing the program's behavior.[1] This requires that the
expression be pure – its value must be the same for the same inputs
and its evaluation must have no side effects. An expression that is
not referentially transparent is called referentially opaque.`

Examples of both:




var g = 0;

function rt (x) {
return x + 1
}

function ro (x) {
g++;
return x + g;
}

console.log(rt(1));
console.log(rt(1));
console.log(rt(1));

console.log(ro(1));
console.log(ro(1));
console.log(ro(1));



2
2
2
2
3
4
undefined




7. What is: Closure




Closures are important because they control what is and isn't in
scope in a particular function, along with which variables are
shared between sibling functions in the same containing scope.
Understanding how variables and functions relate to each other is
critical to understanding what's going on in your code, in both
functional and object oriented programming styles.


Coding without understanding of Closures is like trying to
speak English without understanding the grammar rules.
You might be able to get your ideas across, but probably a
bit awkaardly.


In JS closures are frequently used for data privacy, in event
handlers and callback functions, and in partial applications,
currying and other functional programming patterns.




7.1. What is it?




Is the combination of a function bundled together (enclosed) with
references to its surrounding sate (the
lexical environment). In other words, a closure gives you access to an outer
function's scope from a inner function. In JavaScript, closures
are created every time a function is created, at function creation
time.


To use a Closure, define a function inside another function and
expose it. To expose a function, return it or pass it to another
function.


The inner function will have access to the variables in the outer
function scope, even after the outer function has returned.





7.2. Using Closures (Examples)




Among other things, closures are commonly used to give objects
data privacy. Data privacy is an essential property that helps us
program to an interface, not an implementation. This is an important concept that helps us build more robust
software because implementation details are more likely to change
in breaking ways than interface contracts.


In JavaScript, closures are the primary mechanism used to enable
data privacy. When you use closures for data privacy, the enclosed
variables are only in scope within the containing (outer)
function. You can’t get at the data from an outside scope except
through the object’s privileged methods. In JavaScript, any
exposed method defined within the closure scope is privileged. For
example:



const getSecret = (secret) => {
return {
get: () => secret
};
};

test('Closure for object privacy.', assert => {
const msg = '.get() should have access to the closure.';
const expected = 1;
const obj = getSecret(1);

const actual = obj.get();

try {
assert.ok(secret, 'This throws an error.');
} catch (e) {
assert.ok(true, `The secret var is only available
to privileged methods.`);
}

assert.equal(actual, expected, msg);
assert.end();
});



In the example above, the `.get()` method is defined inside the
scope of `getSecret()`, which gives it access to any variables
from `getSecret()`, and makes it a privileged method. In this
case, the parameter, `secret`.


Objects are not the only way to produce data privacy. Closures can
also be used to create stateful functions whose return values may
be influenced by their internal state, e.g.:

`const secret = msg => () => msg;`



// Secret - creates closures with secret messages.
// https://gist.github.com/ericelliott/f6a87bc41de31562d0f9
// https://jsbin.com/hitusu/edit?html,js,output

// secret(msg: String) => getSecret() => msg: String
const secret = (msg) => () => msg;

test('secret', assert => {
const msg = 'secret() should return a function that returns the passed secret.';

const theSecret = 'Closures are easy.';
const mySecret = secret(theSecret);

const actual = mySecret();
const expected = theSecret;

assert.equal(actual, expected, msg);
assert.end();
});



In functional programming, closures are frequently used for
partial application & currying. This requires some
definitions:




7.2.1. Application:




The process of applying a function to its arguments in order to
produce a return value.





7.2.2. Partial Application:




The process of applying a function to some of its arguments. The
partially applied function gets returned for later use. Partial
application fixes (partially applies the function to) one or
more arguments inside the returned function, and the returned
function takes the remaining parameters as arguments in order to
complete the function application.


Partial application takes advantage of closure scope in order to
fix parameters. You can write a generic function that will
partially apply arguments to the target function. It will have
the following signature:



partialApply(targetFunction: Function, ...fixedArgs: Any[]) =>
functionWithFewerParams(...remainingArgs: Any[])


It will take a function that takes any number of arguments,
followed by arguments we want to partially apply to the
function, and returns a function that will take the remaining
arguments


An example will help. Say you have a function that adds two
numbers:




const add = (a, b) => a + b;


Now you want a function that adds 10 to any number. We'll call
it `add10()`. The result of `add10(5)` should be `15`. Our
`partialApply()` function can make that happen:



const add10 = partialApply(add, 10);
add10(5);


In this example, the argument, `10` becomes a fixed parameter
remembered inside the `add10()` closure scope.

Let's look at a possible `partialApply()` implementation:



// Generic Partial Application Function
// https://jsbin.com/biyupu/edit?html,js,output
// https://gist.github.com/ericelliott/f0a8fd662111ea2f569e

// partialApply(targetFunction: Function, ...fixedArgs: Any[]) =>
// functionWithFewerParams(...remainingArgs: Any[])
const partialApply = (fn, ...fixedArgs) => {
return function (...remainingArgs) {
return fn.apply(this, fixedArgs.concat(remainingArgs));
};
};

test('add10', assert => {
const msg = 'partialApply() should partially apply functions'

const add = (a, b) => a + b;

const add10 = partialApply(add, 10);

const actual = add10(5);
const expected = 15;

assert.equal(actual, expected, msg);
});



As you can see, it simply returns a function which retains
access to the `fixedArgs` arguments that were passed into the
`partialApply()` function.







8. What is: Pure Functions





8.1. General Definition



Is a function where:



  • Given the same input, always returns the same output

  • Produces no side effects






8.2. Functions as a whole




Is a process of taking some input, called arguments, and producing
some output to the called return value.

Main purposes:




  • Mapping: Process output based on given inputs. Maps input values
    to output values


  • Procedures: A function to perform a sequence of steps. This is
    style is procedural programming.


  • I/O: In order to communicate with the system: screen, storage,
    logs, network.





8.3. Back to definition



If a function where:



  • Given the same input, always returns the same output

  • Produces no side effects

If I have a function like:




function double(number) {
return 2 * number;
}

I can say that these are both "the same"




console.log(double(5));
console.log(10); // They produces the same result


If you want Referential Transparency you need to use
pure functions.


A dead giveaway that a function is impure is if it makes sense to
call it without using its return value. For pure functions, that's
a nope.





8.4. Recommendation




Favor pure functions: If it is practical to implement a program
requirement using pure functions, you should use them over other
options. They are the simplest reusable build blocks of code in a
program.





8.5. The most important design
principle (KISS)



Keep it Simple Stupid or, Keep it Stupid Simple


Pure functions are completely independent of outside state, and as
such, they are immune to entire classes of bugs that happen with a
shared mutable state.


This independent nature makes them great candidates for parallel
processing across many CPUs and distribute clusters.


They are also easier to move around, refactor, and reorganize in
the code, making programs more flexible and adaptable to future
changes.





8.6. Problem with Shared State




In an example: if you make requests from a query text field as the
user types. You may have times where the last request "Java" for
example comes and occupies the UI response element, before the
request for "JavaScript" was even sent.


To fix this, you should build a manager that cancels the previous
AJAX request.


Martin Odersky (Creator of Scala) puts it: `non-determinism =
parallel processing + mutable state`

You should avoid it.


Program determinism is a LOT desirable in computing. If you think
JS is immune because it only use single thread. Remember that
AJAX, API I/O event listeners, web workers, iframes and timeouts
can introduce indeterminism into your program. Combine that with
shared state, you have a recipe for bugs.





8.7. Given the same Input,
Always Returns the Same Output




Our `double` function will always return 10 for 5 as parameter, it
doesn't matter how many times we call it

But we can't say the same for `Math.random()` for example.



Math.random();
// 0.8335683328172347

Math.random();
// 0.8910118593581697

Math.random();
//0.3099123827043109



Even tough we didn't pass any arguments into any of the function
calls, they produced different output. This function is not pure.



const time = () => new Date().toLocaleTimeString();

time(); // '8:45:51 AM'



This is also not pure, even thought it repeats its output one time
each day.

Now an example of pure function:




const highpass = (cutoff, value) => value >= cutoff;

highpass(5, 5); // => true Always the same result given the same inputs
highpass(5, 5); // => true
highpass(5, 5); // => true

highpass(5, 123); // true Many inputs may map to the same ouputs
highpass(5, 6); // true
highpass(5, 18); // true
highpass(5, 1); // false
highpass(5, 3); // false
highpass(5, 4); // false



A pure function must not rely on any external mutable state,
because it would no longer be deterministic or referentially
transparent.





8.8. Pure functions Produce No
SIDE EFFECTS




A pure function produces no side effects, which means that it
can't alter any external state.




8.8.1. Immutability




JavaScript's object arguments are references, which means that
if a function were to mutate a property on an object or array
parameter, that would mutate state that is accessible outside
the function. Pure functions must not mutate external state.

Consider this mutating, impure `addToCart()` function:



// impure addToCart mutates existing cart
const addToCart = (cart, item, quantity) => {
cart.items.push({
item,
quantity
});
return cart;
};

test('addToCart()', assert => {
const msg = 'addToCart() should add a new item to the cart.';
const originalCart = {
items: []
};
const cart = addToCart(
originalCart,
{
name: "Digital SLR Camera",
price: '1495'
},
1
);

const expected = 1; // num items in cart
const actual = cart.items.length;

assert.equal(actual, expected, msg);

assert.deepEqual(originalCart, cart, 'mutates original cart.');
assert.end();
});



It works by passing a cart and item to add to the cart. The
function then returns the same cart, with the item added to it.


The problem is that we've just mutated some shared state. Other
functions may be relying on that cart object state to be what it
was before the function was called and now we have to worry
about what impact it will have on the program logic if we change
the order. Refactoring the code could result in bugs popping up
and unhappy customers.

Consider this version:




// Pure addToCart() returns a new cart
// It does not mutate the original.
const addToCart = (cart, item, quantity) => {
const newCart = lodash.cloneDeep(cart);

newCart.items.push({
item,
quantity
});
return newCart;

};

test('addToCart()', assert => {
const msg = 'addToCart() should add a new item to the cart.';
const originalCart = {
items: []
};

// deep-freeze on npm
// throws an error if original is mutated
deepFreeze(originalCart);

const cart = addToCart(
originalCart,
{
name: "Digital SLR Camera",
price: '1495'
},
1
);

const expected = 1; // num items in cart
const actual = cart.items.length;

assert.equal(actual, expected, msg);

assert.notDeepEqual(originalCart, cart,
'should not mutate original cart.');
assert.end();
});



In this example, we have an array nested in an object, which is
why I reached for a deep clone. This is more complex state than
you’ll typically be dealing with. For most things, you can break
it down into smaller chunks.


For example, Redux lets you compose reducers rather than deal
with the entire app state inside each reducer. The result is
that you don't have to create a deep clone of the entire app
state every time you want to update just a small part of it.
Instead, you can use non-destructive array methods, or
`Object.assign()` to update a small part of the app state.







9. New features of ES13





9.1. Class Field Declarations




Before ES13 we could not declare class fields outside the
constructor:




class Car {
constructor() {
this.color = 'blue';
this.age = 2;
}
}
const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2

ES13 removes this limitation:




class Car {
color = 'blue';
age = 2;
}
const car = new Car();
console.log(car.color); // blue
console.log(car.age); // 2





9.2. Private Methods and
Fields




Members were usually prefixed with an _ to indicate it should be
private. But could still be accessed from outside.




class Person {
_firstName = 'Joseph';
_lastName = 'Stevens';
get name() {
return `${this._firstName} ${this._lastName}`;
}
}

const person = new Person();
console.log(person.name); // Joseph Stevens

// Members intended to be private can still be accessed
// from outside the class
console.log(person._firstName); // Joseph
console.log(person._lastName); // Stevens

// They can also be modified
person._firstName = 'Robert';
person._lastName = 'Becker';
console.log(person.name); // Robert Becker


Now we use # to add private fields to our classes




class Person {
#firstName = 'Joseph';
#lastName = 'Stevens';
get name() {
return `${this.#firstName} ${this.#lastName}`;
}
}

const person = new Person();

console.log(person.name);

// SyntaxError: Private field '#firstName' must be
// declared in an enclosing class
console.log(person.#firstName);
console.log(person.#lastName);






9.3. await Operator at Top
level




Previously we could only use await with async function. We could
not use this in the global scope.




function setTimeoutAsync(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
}

// SyntaxError: await is only valid in async functions
await setTimeoutAsync(3000);


Now it is possible:




function setTimeoutAsync(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
}

// Waits for timeout - no error thrown
await setTimeoutAsync(3000);






9.4. Static Class Fields and
Static Private Methods




We can now declare static fields and static private methods for a
class in ES13. Static methods can access other private/public
static members in the class using the this keyword, and instance
methods can access them using this.constructor.



class Person {
static #count = 0;
static getCount() {
return this.#count;
}
constructor() {
this.constructor.#incrementCount();
}
static #incrementCount() {
this.#count++;
}
}

const person1 = new Person();
const person2 = new Person();
console.log(Person.getCount()); // 2






9.5. Class static Block




ES13 allows the definition of static blocks that will be executed
only once, at the creation of the class. This is similar to static
constructors in other languages with support for object-oriented
programming, like C# and Java.


A class can have any number of static {} initialization blocks in
its class body. They will be executed, along with any interleaved
static field initializers, in the order they are declared. We can
use the super property in a static block to access properties of
the super class.



class Vehicle {
static defaultColor = 'blue';
}

class Car extends Vehicle {
static colors = [];
static {
this.colors.push(super.defaultColor, 'red');
}
static {
this.colors.push('green');
}
}

console.log(Car.colors); // [ 'blue', 'red', 'green' ]






9.6. Ergonomic Brand Checks
for Private Fields




We can use this new feature to check if an object has a particular
private field in it, using the in operator.




class Car {
#color;
hasColor() {
return #color in this;
}
}

const car = new Car();
console.log(car.hasColor()); // true;



The in operator can correctly distinguish private fields with the
same names from different classes:




class Car {
#color;
hasColor() {
return #color in this;
}
}

class House {
#color;
hasColor() {
return #color in this;
}
}

const car = new Car();
const house = new House();
console.log(car.hasColor()); // true;
console.log(car.hasColor.call(house)); // false
console.log(house.hasColor()); // true
console.log(house.hasColor.call(car)); // false






9.7. at() Method for Indexing




We typically use square brackets ([]) in JavaScript to access the
Nth element of an array, which is usually a simple process. We
just access the N - 1 property of the array.



const arr = ['a', 'b', 'c', 'd'];
console.log(arr[1]); // b


However, we have to use an index of arr.length - N if we want to
access the Nth item from the end of the array with square
brackets.



const arr = ['a', 'b', 'c', 'd'];

// 1st element from the end
console.log(arr[arr.length - 1]); // d

// 2nd element from the end
console.log(arr[arr.length - 2]); // c



The new at() method lets us do this more concisely and
expressively. To access the Nth element from the end of the array,
we simply pass a negative value of -N to at().



const arr = ['a', 'b', 'c', 'd'];

// 1st element from the end
console.log(arr.at(-1)); // d

// 2nd element from the end
console.log(arr.at(-2)); // c



Apart from arrays, strings and TypedArray objects also now have
at() methods.



const str = 'Coding Beauty';

console.log(str.at(-1)); // y
console.log(str.at(-2)); // t

const typedArray = new Uint8Array([16, 32, 48, 64]);

console.log(typedArray.at(-1)); // 64
console.log(typedArray.at(-2)); // 48






9.8. RegExp Match Indices




This new feature allows us to specify that we want the get both
the starting and ending indices of the matches of a RegExp object
in a given string. Previously, we could only get the starting
index of a regex match in a string.



const str = 'sun and moon';
const regex = /and/;
const matchObj = regex.exec(str);

// [ 'and', index: 4, input: 'sun and moon', groups: undefined ]
console.log(matchObj);



['and '(\, index:) 4 (\, input:) 'sun and moon '(\, groups:) undefined]


We can now specify a d regex flag to get the two indices where the
match starts and ends. With the d flag set, the object returned
will have an indices property that contains the starting and
ending indices.



const str = 'sun and moon';
const regex = /and/d;
const matchObj = regex.exec(str);
/*
[
'and',
index: 4,
input: 'sun and moon',
groups: undefined,
indices: [ [ 4, 7 ], groups: undefined ]
]
*/
console.log(matchObj);


['and '(\, index:) 4 (\, input:) 'sun and moon '(\, groups:) undefined (\, indices:) [[4 (\, 7)] (\, groups:) undefined]]




9.9. Object.hasOwn() Method




In JavaScript, we can use the Object.prototype.hasOwnProperty()
method to check if an object has a given property.



class Car {
color = 'green';
age = 2;
}
const car = new Car();

console.log(car.hasOwnProperty('age')); // true
console.log(car.hasOwnProperty('name')); // false



But there are certain problems with this approach. For one, the
Object.prototype.hasOwnProperty() method is not protected - it can
be overridden by defining a custom hasOwnProperty() method for a
class, which could have completely different behavior from

Object.prototype.hasOwnProperty().



class Car {
color = 'green';
age = 2;
// This method does not tell us whether an object of
// this class has a given property.
hasOwnProperty() {
return false;
}
}

const car = new Car();

console.log(car.hasOwnProperty('age')); // false
console.log(car.hasOwnProperty('name')); // false



Another issue is that for objects created with a null prototype
(using Object.create(null)), trying to call this method on them
will cause an error.



const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;

// TypeError: obj.hasOwnProperty is not a function
console.log(obj.hasOwnProperty('color'));



One way to solve these issues is to use to call the call() method
on the Object.prototype.hasOwnProperty Function property, like
this:



const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;

console.log(Object.prototype.hasOwnProperty.call(obj, 'color')); // true
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // false



This isn’t very convenient. We can write a reusable function to
avoid repeating ourselves:



function objHasOwnProp(obj, propertyKey) {
return Object.prototype.hasOwnProperty.call(obj, propertyKey);
}

const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;

console.log(objHasOwnProp(obj, 'color')); // true
console.log(objHasOwnProp(obj, 'name')); // false



No need for that though, as we can use the new built-in
Object.hasOwn() method. Like our reusable function, it takes an
object and property as arguments and returns true if the specified
property is a direct property of the object. Otherwise, it returns
false.



const obj = Object.create(null);
obj.color = 'green';
obj.age = 2;
obj.hasOwnProperty = () => false;

console.log(Object.hasOwn(obj, 'color')); // true
console.log(Object.hasOwn(obj, 'name')); // false






9.10. Error Cause




Error objects now have a cause property for specifying the
original error that caused the error about to be thrown. This
helps to add additional contextual information to the error and
assist the diagnosis of unexpected behavior. We can specify the
cause of an error by setting a cause property on an object passed
as the second argument to the Error() constructor.



function userAction() {
try {
apiCallThatCanThrow();
} catch (err) {
throw new Error('New error message', { cause: err });
}
}
try {
userAction();
} catch (err) {
console.log(err);
console.log(`Cause by: ${err.cause}`);
}





9.11. Array Find from Last




In JavaScript, we can already use the Array find() method to find
an element in an array that passes a specified test condition.
Similarly, we can use findIndex() to find the index of such an
element. While find() and findIndex() both start searching from
the first element of the array, there are instances where it would
be preferable to start the search from the last element instead.


There are scenarios where we know that finding from the last
element might achieve better performance. For example, here we’re
trying to get the item in the array with the value prop equal to
y. With find() and findIndex():



const letters = [
{ value: 'v' },
{ value: 'w' },
{ value: 'x' },
{ value: 'y' },
{ value: 'z' },
];

const found = letters.find((item) => item.value === 'y');
const foundIndex = letters.findIndex((item) => item.value === 'y');

console.log(found); // { value: 'y' }
console.log(foundIndex); // 3



This works, but as the target object is closer to the tail of the
array, we might be able to make this program run faster if we use
the findLast() and findLastIndex() methods to search the array
from the end.



const letters = [
{ value: 'v' },
{ value: 'w' },
{ value: 'x' },
{ value: 'y' },
{ value: 'z' },
];

const found = letters.findLast((item) => item.value === 'y');
const foundIndex = letters.findLastIndex((item) => item.value === 'y');

console.log(found); // { value: 'y' }
console.log(foundIndex); // 3



Another use case might require that we specifically search the
array from the end to get the correct item. For example, if we
want to find the last even number in a list of numbers, find() and
findIndex() would produce a wrong result:



const nums = [7, 14, 3, 8, 10, 9];
// gives 14, instead of 10

const lastEven = nums.find((value) => value % 2 === 0);
// gives 1, instead of 4

const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);
console.log(lastEven); // 14
console.log(lastEvenIndex); // 1



We could call the reverse() method on the array to reverse the
order of the elements before calling find() and findIndex(). But
this approach would cause unnecessary mutation of the array, as
reverse() reverses the elements of an array in place. The only way
to avoid this mutation would be to make a new copy of the entire
array, which could cause performance problems for large arrays.


Also, findIndex() would still not work on the reversed array, as
reversing the elements would also mean changing the indexes they
had in the original array. To get the original index, we would
need to perform an additional calculation, which means writing
more code.



const nums = [7, 14, 3, 8, 10, 9];

// Copying the entire array with the spread syntax before
// calling reverse()
const reversed = [...nums].reverse();

// correctly gives 10

const lastEven = reversed.find((value) => value % 2 === 0);
// gives 1, instead of 4

const reversedIndex = reversed.findIndex((value) => value % 2 === 0);
// Need to re-calculate to get original index

const lastEvenIndex = reversed.length - 1 - reversedIndex;

console.log(lastEven); // 10
console.log(reversedIndex); // 1
console.log(lastEvenIndex); // 4



It’s in cases like where the findLast() and findLastIndex()
methods come in handy.



const nums = [7, 14, 3, 8, 10, 9];

const lastEven = nums.findLast((num) => num % 2 === 0);

const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);

console.log(lastEven); // 10
console.log(lastEvenIndex); // 4








Author: Rahul M. Juliato


Created: 2022-10-01 Sáb 18:08



Validate