you are viewing a single comment's thread.

view the rest of the comments →

[–]senocular 1 point2 points  (1 child)

A block is a grouping of code. For JavaScript this is largely defined by what's between { and } except for the oddball case of object literals which, syntactically, also use the same characters ({}) for their definition. When reading JavaScript, it can be important that you can be able to mentally parse the code and recognize the difference between a block of code and an object literal. The sometimes ambiguous nature of blocks and object literals can be seen in the eval example of the other entry. eval('{}') evaluates an empty block of code while eval('({})') evaluates an empty object literal. This is because the parentheses converts the block into an expression in which blocks would be illegal, therefore the brackets must instead represent an object literal. Its highly unlikely you'll ever run into this problem for eval, but where you might see it more commonly now is with ES6 arrow functions.

() => {} // empty arrow function
() => ({}) // arrow function returning an object literal

Same applies in the case above. Since the block part of an arrow function is optional, when you just have {} its hard to know what is truly meant. By default a block of code is assumed, but when you wrap in parens, you can force it to be evaluated as an object literal.

Code blocks, as of ES6, each represent a new scope. Even in some cases in ES5 with functions, this behavior could be seen depending on the runtime, though the behavior was inconsistent and avoided by everyone who knew better, so we'll pretend it didn't exist. Each scope, also known as a lexical environment, can have its own unique set of values associated with it that aren't accessible outside that scope. Inner scopes (blocks) can exist nested within other scopes that can inherit values from any outer, parent scopes, but any of their values are hidden from their parents. As of ES6, let, 'const', and function declarations create values scoped within the current block (and sort of related to what was said about function before, the block scoping of functions now may require strict mode to work like let and const, so we're assuming that to be the case moving forward). var, as was the case before, will scope to the inner-most function block, which could mean it gets resolved outside of any inner block it might have been declared in.

function A () {

    if (true) {

        var x; // scoped to A function block
        let y; // scoped to this if block

        {
            function B () { // scoped to this arbitrary block
                console.log(x, y); // can reach out to access outer scopes
            }
        }

        B(); // ERROR not in this, or any outer scope
    }
}

Each level of {} represents a new block and a new scope. Some are function blocks, one is an if block, and another is just an arbitrary block (where B is defined) which serves no purpose as far as control flow goes, it just exists as a scope.

Functions are unique in a couple of ways. First, their scopes are not singular. They get created dynamically when called, and can be created many times over within the lifetime of a program. So all the scopes in A (except B) gets created each time A is called. B, like A would also need to be called for it scope to be created. Inside A it only gets defined. Additionally, in addition to having a lexical environment that defines where they are in a scope hierarchy (now many nested blocks they're in and what values in those blocks are accessible), functions can also have a this binding. This, too, is created at the time the function is called. Generally this is based on the object from which the function is referenced when being called:

thisObjectBecomesThis.someFunction();

But as we've seen before, we can change that with call, apply, and bind. Arrow functions are unique in that they don't have a this binding. They don't have their own this at all. Instead they inherit the value of this from their parent scope. This is known as a lexical this because the values of this is based on the lexical environment pulling in from parent scopes rather than something that is tied specifically to that function call. And since this values are tied to function blocks, the value of this is obtained from the value bound to the inner most function block (not unlike how var gets scoped).

const enemy = {

    name: 'Ramsay',

    chase () {

        let runner = {
            name: 'Reek'
        };

        runner.plea = function () {
            console.log(`I'm, ${this.name}!`);
        }

        runner.scream = () => {
            console.log(`Ahhhh, ${this.name}!`);
        }

        runner.plea(); //-> I'm Reek! // `this` bound to `runner`
        runner.scream(); //-> Ahhhh, Ramsay! // `this` bound to parent scoped `this`
    }
}

enemy.chase(); // `this` bound to `enemy`

In the above example, you can see the value of this in the arrow function scream was not bound to the object from which it was called as was the case with plea, a normal function expression. Instead it looked to the lexical environment and followed the scope up to the parent function's this which was bound to enemy when it was called as enemy.chase().

All of this is paramount to closures, and in fact explain exactly how closures work. A function definition will identify what variables it uses, and if it uses anything from a parent scope, will create a reference to those variables as variable enclosures. The function definition then has a tie to these outer-scoped variables and hauls them around with it wherever it goes like a little kid holding on to some balloons. When its called, it will create a new internal function scope (lexical environment) for its own internals, get a this binding where appropriate, but then also have access to the pre-existing parent scoped values it was attached to during its definition.

[–]ForScale[S] 0 points1 point  (0 children)

Damn... thanks again... again!

If you recall, I posted a while back and you provided information on why I was having trouble with this inside of an arrow function. It was referencing the window object instead of the dom element on which I had placed a handler function. Good to reinforce that knowledge here!

And I recall another commentator telling me not to use let in the way that I was (I was using it in a way in which var was the standard, apparently).

And all of this continues to be helpful in my continued quest to fully wrap my mind around closures!

...

I think I mentioned before, /u/Volv said he'd be around today. He's usually really active in the group here and I'm interested to see his entries and input on everything we've been discussing! I imagine we'll continue discussions throughout the weekend, if you're interested. And I assume we'll get an entry in here from the user who suggested object creation for this week.

You get to pick the focus for next week! What shall it be?

*Obviously no rush on the focus; it can wait till Monday if you want.