YDKJS - UP & GOING, Takeaways for Experienced JavaScript Developer

·

9 min read

YDKJS - UP & GOING, Takeaways for Experienced JavaScript Developer

These are a few of the important takeaways from Kyle Simpson's YDKJS - UP & GOING book series.

  • The JavaScript you know right now is probably parts handed down to you by others who've been burned by incomplete understanding. That JavaScript is but a shadow of the true language. You don't really know JavaScript, yet, but if you dig into this series, you will. Read on, my friends. JavaScript awaits you.

  • It's typically asserted that JavaScript is interpreted because your JavaScript source code is processed each time it's run. But that's not entirely accurate. The JavaScript engine actually compiles the program on the fly and then immediately runs the compiled code.

  • Comments should explain why not what. They can optionally explain how if that's particularly confusing.

  • The return value from the typeof operator is always one of six (seven as of ES6! - the "symbol" type) string values. That is, typeof "abc" returns "string", not string.

    typeof array; // "object" arrays are special objects
    typeof null; // "object" -- weird, bug

  • The specific list of "falsy" values in JavaScript is as follows:

    "" (empty string)
    0, -0, NaN (invalid number)
    null, undefined
    false

  • Any value that's not on this "falsy" list is "truthy." Here are some examples of those:

    "hello"
    42
    true
    [ ], [ 1, "2", 3 ] (arrays)
    { }, { a: 42 } (objects)
    function foo() { .. } (functions)

Equality

The difference between == and === is usually characterized that == checks for value equality and === checks for both values and type equality. However, this is inaccurate. The proper way to characterize them is that == checks for value equality with coercion allowed, and === checks for value equality without allowing coercion; === is often called "strict equality" for this reason.

Consider the implicit coercion that's allowed by the == loose-equality comparison and not allowed with the === strict-equality:

var a = "42";
var b = 42;

a == b;            // true
a === b;        // false

In the a == b comparison, JS notices that the types do not match, so it goes through an ordered series of steps to coerce one or both values to a different type until the types match, where then simple value equality can be checked.

If you think about it, there are two possible ways a == b could give true via coercion. Either the comparison could end up as 42 == 42 or it could be "42" == "42". So which is it?

The answer: "42" becomes 42, to make the comparison 42 == 42. In such a simple example, it doesn't really seem to matter which way that process goes, as the end result is the same. There are more complex cases where it matters not just what the end result of the comparison is but how you get there.

The a === b produces false, because the coercion is not allowed, so the simple value comparison obviously fails. Many developers feel that === is more predictable, so they advocate always using that form and staying away from ==. I think this view is very shortsighted. I believe == is a powerful tool that helps your program, if you take the time to learn how it works.

To boil down a whole lot of details to a few simple takeaways, and help you know whether to use == or === in various situations, here are my simple rules:

  • If either value (aka side) in comparison could be the true or false value, avoid == and use ===.
  • If either value in comparison could be one of these specific values (0, "", or [] -- empty array), avoid == and use ===.
  • In all other cases, you're safe to use ==. Not only is it safe, but in many cases, it simplifies your code in a way that improves readability.

You should take special note of the == and === comparison rules if you're comparing two non-primitive values, like objects (including function and array). Because those values are actually held by reference, both == and === comparisons will simply check whether the references match, not anything about the underlying values.

For example, arrays are by default coerced to strings by simply joining all the values with commas (,) in between. You might think that two arrays with the same contents would be == equal, but they're not:

var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";

a == c;        // true
b == c;        // true
a == b;        // false

Hoisting

Wherever a var appears inside a scope, that declaration is taken to belong to the entire scope and accessible everywhere throughout. Metaphorically, this behavior is called hoisting, when a var declaration is conceptually "moved" to the top of its enclosing scope. Technically, this process is more accurately explained by how code is compiled, but we can skip over those details for now.

Consider:

var a = 2;

foo();                // works because `foo()`
                        // declaration is "hoisted"

function foo() {
    a = 3;

    console.log( a );    // 3

    var a;            // declaration is "hoisted"
                        // to the top of `foo()`
}

console.log( a );    // 2

Closure

You can think of closure as a way to "remember" and continue to access a function's scope (its variables) even once the function has finished running.

Consider:

function makeAdder(x) {
    // parameter `x` is an inner variable

    // inner function `add()` uses `x`, so
    // it has a "closure" over it
    function add(y) {
        return y + x;
    };

    return add;
}

The reference to the inner add(..) function that gets returned with each call to the outer makeAdder(..) is able to remember whatever x value was passed in to makeAdder(..).

this Identifier

Another very commonly misunderstood concept in JavaScript is the this identifier. Again, there's a couple of chapters on it in the this & Object Prototypes title of this series, so here we'll just briefly introduce the concept.

While it may often seem that this is related to "object-oriented patterns," in JS this is a different mechanism.

If a function has a this reference inside it, that this reference usually points to an object. But which object it points to depends on how the function was called.

It's important to realize that this does not refer to the function itself, as is the most common misconception.

Here's a quick illustration:

function foo() {
    console.log( this.bar );
}

var bar = "global";

var obj1 = {
    bar: "obj1",
    foo: foo
};

var obj2 = {
    bar: "obj2"
};

// --------

foo();                // "global"
obj1.foo();            // "obj1"
foo.call( obj2 );        // "obj2"
new foo();            // undefined

There are four rules for how this gets set, and they're shown in those last four lines of that snippet.

  1. foo() ends up setting this to the global object in non-strict mode -- in strict mode, this would be undefined and you'd get an error in accessing the bar property -- so "global" is the value found for this.bar.
  2. obj1.foo() sets this to the obj1 object.
  3. foo.call(obj2) sets this to the obj2 object.
  4. new foo() sets this to a brand new empty object.

Bottom line: to understand what this points to, you have to examine how the function in question was called. It will be one of those four ways just shown, and that will then answer what this is.

Prototypes

When you reference a property on an object, if that property doesn't exist, JavaScript will automatically use that object's internal prototype reference to find another object to look for the property on. You could think of this almost as a fallback if the property is missing.

The internal prototype reference linkage from one object to its fallback happens at the time the object is created. The simplest way to illustrate it is with a built-in utility called Object.create(..).

Consider:

var foo = {
    a: 42
};

// create `bar` and link it to `foo`
var bar = Object.create( foo );

bar.b = "hello world";

bar.b;        // "hello world"
bar.a;        // 42 <-- delegated to `foo`

The a property doesn't actually exist on the bar object, but because bar is prototype-linked to foo, JavaScript automatically falls back to looking for a on the foo object, where it's found.

This linkage may seem like a strange feature of the language. The most common way this feature is used -- and I would argue, abused -- is to try to emulate/fake a "class" mechanism with "inheritance."

But a more natural way of applying prototypes is a pattern called "behavior delegation," where you intentionally design your linked objects to be able to delegate from one to the other for parts of the needed behavior.

Non-JavaScript

So far, the only things we've covered are in the JS language itself. The reality is that most JS is written to run in and interact with environments like browsers. A good chunk of the stuff that you write in your code is, strictly speaking, not directly controlled by JavaScript. That probably sounds a little strange.

The most common non-JavaScript JavaScript you'll encounter is the DOM API. For example:

var el = document.getElementById( "foo" );

The document variable exists as a global variable when your code is running in a browser. It's not provided by the JS engine, nor is it particularly controlled by the JavaScript specification. It takes the form of something that looks an awful lot like a normal JS object, but it's not really exactly that. It's a special object, often called a "host object."

Moreover, the getElementById(..) method on document looks like a normal JS function, but it's just a thinly exposed interface to a built-in method provided by the DOM from your browser. In some (newer-generation) browsers, this layer may also be in JS, but traditionally the DOM and its behavior are implemented in something more like C/C++.

Another example is input/output (I/O).

Everyone's favorite alert(..) pops up a message box in the user's browser window. alert(..) is provided to your JS program by the browser, not by the JS engine itself. The call you make sends the message to the browser internals and it handles drawing and displaying the message box.

The same goes with console.log(..); your browser provides such mechanisms and hooks them up to the developer tools.


That's all folks for today, Very soon I will be writing another blog for the next book in the series i.e. SCOPE & CLOSURE.

Want an even shorter version of this? Check out below:

Open to feedback and comments if I missed any concept which was covered in this book.

🙏 Credit:

Did you find this article valuable?

Support Alok Raj by becoming a sponsor. Any amount is appreciated!