• 沒有找到結果。

Polyfilling Block Scope

在文檔中 Scope and Closures (頁 84-90)

Now I Can See

B. Polyfilling Block Scope

try {

throw undefined;

} catch (a) { a = 2;

console.log( a );

} }

console.log( a );

So, with the use of such tools, we can start taking advantage of block scope regardless of if we are targeting ES6 or not, because try/

catch has been around (and worked this way) from ES3 days.

70 | Appendix B: Polyfilling Block Scope

Implicit Versus Explicit Blocks

In Chapter 3, we identified some potential pitfalls to code maintaina‐

bility/refactorability when we introduce block scoping. Is there another way to take advantage of block scope but to reduce this down‐

side?

Consider this alternate form of let, called the let block or let state‐

ment (contrasted with let declarations from before).

let (a = 2) {

console.log( a ); // 2 }

console.log( a ); // ReferenceError

Instead of implicitly hijacking an existing block, the let statement creates an explicit block for its scope binding. Not only does the ex‐

plicit block stand out more, and perhaps fare more robustly in code refactoring, it produces somewhat cleaner code by, grammatically, forcing all the declarations to the top of the block. This makes it easier to look at any block and know what’s scoped to it and not.

As a pattern, it mirrors the approach many people take in function scoping when they manually move/hoist all their var declarations to the top of the function. The let statement puts them there at the top of the block by intent, and if you don’t use let declarations strewn throughout, your block-scoping declarations are somewhat easier to identify and maintain.

But, there’s a problem. The let statement form is not included in ES6.

Neither does the official Traceur compiler accept that form of code.

We have two options. We can format using ES6-valid syntax and a little sprinkle of code discipline:

/*let*/ { let a = 2;

console.log( a );

}

console.log( a ); // ReferenceError

But, tools are meant to solve our problems. So the other option is to write explicit let statement blocks, and let a tool convert them to valid, working code.

Implicit Versus Explicit Blocks | 71

2. let-er on GitHub

So, I built a tool called let-er2 to address just this issue. let-er is a build-step code transpiler, but its only task is to find let statement forms and transpile them. It will leave alone any of the rest of your code, including any let declarations. You can safely use let-er as the first ES6 transpiler step, and then pass your code through something like Trace‐

ur if necessary.

Moreover, let-er has a configuration flag --es6, which when turned on (off by default), changes the kind of code produced. Instead of the try/catch ES3 polyfill hack, let-er would take our snippet and pro‐

duce the fully ES6-compliant, non-hacky:

{

let a = 2;

console.log( a );

}

console.log( a ); // ReferenceError

So, you can start using let-er right away, and target all pre-ES6 envi‐

ronments, and when you only care about ES6, you can add the flag and instantly target only ES6.

And most important, you can use the more preferable and more ex‐

plicit let statement form even though it is not an official part of any ES version (yet).

Performance

Let me add one last quick note on the performance of try/catch, and/

or to address the question, “Why not just use an IIFE to create the scope?”

First, the performance of try/catch is slower, but there’s no reason‐

able assumption that it has to be that way, or even that it always will be that way. Since the official TC39-approved ES6 transpiler uses try/

catch, the Traceur team has asked Chrome to improve the perfor‐

mance of try/catch, and they are obviously motivated to do so.

Secondly, IIFE is not a fair apples-to-apples comparison with try/

catch, because a function wrapped around any arbitrary code changes the meaning, inside of that code, of this, return, break, and

72 | Appendix B: Polyfilling Block Scope

continue. IIFE is not a suitable general substitute. It could only be used manually in certain cases.

The question really becomes: do you want block scoping, or not. If you do, these tools provide you that option. If not, keep using var and go on about your coding!

Performance | 73

APPENDIX C

Lexical this

Though this title does not address the this mechanism in any detail, there’s one ES6 topic that relates this to lexical scope in an important way, which we will quickly examine.

ES6 adds a special syntactic form of function declaration called the arrow function. It looks like this:

var foo = a => { console.log( a );

};

foo( 2 ); // 2

The so-called “fat arrow” is often mentioned as a shorthand for the tediously verbose (sarcasm) function keyword.

But there’s something much more important going on with arrow functions that has nothing to do with saving keystrokes in your dec‐

laration. Briefly, this code suffers a problem:

var obj = { id: "awesome",

cool: function coolFn() { console.log( this.id );

} };

var id = "not awesome"

obj.cool(); // awesome

setTimeout( obj.cool, 100 ); // not awesome

75

The problem is the loss of this binding on the cool() function. There are various ways to address that problem, but one often-repeated sol‐

ution is var self = this;.

That might look like:

Without getting too much into the weeds here, the var self = this “solution” just ends-around the whole problem of understanding and properly using this binding, and instead falls back to something we’re perhaps more comfortable with: lexical scope. self becomes just an identifier that can be resolved via lexical scope and closure, and cares not what happened to the this binding along the way.

People don’t like writing verbose stuff, especially when they do it over and over again. So, a motivation of ES6 is to help alleviate these sce‐

narios, and indeed, fix common idiom problems, such as this one.

The ES6 solution, the arrow function, introduces a behavior called lexical this.

在文檔中 Scope and Closures (頁 84-90)

相關文件