Renan Roggia
I consider myself a tech problem solver.
We must be aware of the nuanced differences between an equality comparison and an equivalence comparison.
All value comparisons in JS consider the type of the values being compared, not just the ===
operator. Specifically, ===
disallows any sort of type conversion (aka, "coercion") in its comparison, where other JS comparisons do allow coercion.
Coercion means a value of one type being converted to its respective representation in another type (to whatever extent possible).
The ==
operator performs an equality comparison similarly to how the ===
performs it. In fact, both operators consider the type of the values being compared. And if the comparison is between the same value type, both ==
and ===
do exactly the same thing, no difference whatsoever.
In other words, they both want to compare values of like types, but ==
allows type conversions first, and once the types have been converted to be the same on both sides, then ==
does the same thing as ===
.
There's a pretty good chance that you'll use relational comparison operators like <
, >
(and even <=
and >=
).
This operator actually has two symmetrical forms: spread and rest (or gather, as I prefer). The spread form is an iterator-consumer.
There are two possibilities in JS: an array or an argument list for a function call.
Maps have a different default iteration than seen here, in that the iteration is not just over the map's values but instead its entries. An entry is a tuple (2-element array) including both a key and a value.
In the for..of
loop over the default map iteration, we use the [btn,btnName]
syntax (called "array destructuring") to break down each consumed tuple into the respective key/value pairs
For the most part, all built-in iterables in JS have three iterator forms available: keys-only (keys()
), values-only (values()
), and entries (entries()
).
Closure is when a function remembers and continues to access variables from outside its scope, even when the function is executed in a different scope.
These closures are not a snapshot of the msg
variable's value; they are a direct link and preservation of the variable itself. That means closure can actually observe (or make!) updates to these variables over time.
this
KeywordOne common misconception is that a function's this
refers to the function itself. Because of how this
works in other languages, another misconception is that this
points the instance that a method belongs to. Both are incorrect.
when a function is defined, it is attached to its enclosing scope via closure. Scope is the set of rules that controls how references to variables are resolved.
But functions also have another characteristic besides their scope that influences what they can access. This characteristic is best described as an execution context, and it's exposed to the function via its this
keyword.
Scope is static and contains a fixed set of variables available at the moment and location you define a function, but a function's execution context is dynamic, entirely dependent on how it is called (regardless of where it is defined or even called from).
this
is not a fixed characteristic of a function based on the function's definition, but rather a dynamic characteristic that's determined each time the function is called.
The benefit of this
-aware functions—and their dynamic context—is the ability to more flexibly re-use a single function with data from different objects.
A function that closes over a scope can never reference a different scope or set of variables. But a function that has dynamic this
context awareness can be quite helpful for certain tasks.
A prototype is a characteristic of an object,
Think about a prototype as a linkage between two objects;
This prototype linkage occurs when an object is created; it's linked to another object that already exists.
A series of objects linked together via prototypes is called the "prototype chain."
To define an object prototype linkage, you can create the object using the Object.create(..)
Delegation through the prototype chain only applies for accesses to lookup the value in a property. If you assign to a property of an object, that will apply directly to the object regardless of where that object is prototype linked to.
In many languages, the developer can choose between assigning/passing a value as the value itself, or as a reference to the value. In JS, however, this decision is entirely determined by the kind of value.
primitive values are always assigned/passed as value copies.
By contrast, references are the idea that two or more variables are pointing at the same value, such that modifying this shared value would be reflected by an access via any of those references. In JS, only object values (arrays, objects, functions, etc.) are treated as references.
Primitives are held by value, objects are held by reference.
The function expression here is referred to as an anonymous function expression, since it has no name identifier between the function
keyword and the (..)
parameter list.
The name
property of a function will reveal either its directly given name (in the case of a declaration) or its inferred name in the case of an anonymous function expression. That value is generally used by developer tools when inspecting a function value or when reporting an error stack trace.
However, name inference only happens in limited cases such as when the function expression is assigned (with =
).
An anonymous function doesn't have an identifier to use to refer to itself from inside itself—for recursion, event unbinding, etc.
Since I don't think anonymous functions are a good idea to use frequently in your programs, I'm not a fan of using the =>
arrow function form. This kind of function actually has a specific purpose (i.e., handling the this
keyword lexically), but that doesn't mean we should use it for every function we write. Use the most appropriate tool for each job.
if
and ? :
-ternary statements, as well as the test clauses in while
and for
loops, all perform an implicit value comparison.