discuz支持反对_支持和反对“让”

discuz支持反对

In this post I'm going to examine the case for (and perhaps against?) one of the new features coming in JavaScript ES6: the let keyword. let enables a new form of scoping not previously accessible generally to JS developers: block scoping.

在这篇文章中,我将研究JavaScript ES6中的一项新功能(或反对)的情况: let关键字。 let启用了JS开发人员以前通常无法访问的新范围范围: block scoping

功能范围 (Function Scope)

Let's briefly review the basics of function scoping -- if you need more indepth coverage, check out my "You Don't Know JS: Scope & Closures" book, part of the "You Don't Know JS" book series.

让我们简要地回顾一下函数作用域的基础知识-如果需要更深入的介绍,请阅读我的“您不知道JS:范围和闭包”一书,该书是“您不知道JS”这本书系列的一部分。

Consider:

考虑:

foo();    // 42

function foo() {
    var bar = 2;
    if (bar > 1 || bam) {
        var baz = bar * 10;
    }

    var bam = (baz * 2) + 2;

    console.log( bam );
}


You may have heard the term "hoisting" to describe how JS var declarations are treated within scopes. It's not exactly a technical description for how it works, but more a metaphor. But for our purposes here, it's good enough to illustrate. That above snippet is essentially treated as if it had been written like:

您可能已经听说过“起重”一词,以描述在范围内如何处理JS var声明。 这并不是有关其工作原理的确切技术描述,而是更多的隐喻。 但是出于我们这里的目的,足以说明问题。 上面的代码段基本上被当作是这样写的:

function foo() {
    var bar, baz, bam;

    bar = 2;

    if (bar > 1 || bam) {
        baz = bar * 10;
    }

    bam = (baz * 2) + 2;

    console.log( bam );
}

foo();  // 42


As you can see, the foo() function declaration was moved (aka "hoisted", aka lifted) to the top of its scope, and similarly the bar, baz, and bam variables were hoisted to the top of their scope.

如您所见, foo()函数声明被移动(又被“提升”,又被提升)到其作用域的顶部,并且类似地, barbazbam变量也被提升至其作用域的顶部。

Because JS variables have always behaved in this hoisting manner, many developers choose to automatically put their var declarations at the top of each (function) scope, so as to match code style to its behavior. And it's a perfectly valid way of going about things.

由于JS变量始终以这种方式运行,因此许多开发人员选择将var声明自动置于每个(函数)作用域的顶部,以使代码样式与其行为匹配。 这是处理事情的一种完全有效的方法。

But have you ever seen code which does that, but also will do things like this in the same code:

但是您是否见过执行此操作的代码,但是还会在同一代码中执行以下操作:

for (var i=0; i<10; i++) {
    // ..
}


That is also extremely common. Another example that's fairly common:

这也是非常普遍的。 另一个很常见的例子:

var a, b;

// other code

// later, swap `a` and `b`
if (a && b) {
    var tmp = a;
    a = b;
    b = tmp;
}


The var tmp inside the if block sorta violates the ostensible "move all declarations to the top" coding style. Same of the var i in the for loop in the earlier snippet.

if块sorta中的var tmp违反了表面上“将所有声明移到顶部”的编码样式。 与先前代码段中for循环中的var i相同。

In both cases, the variables will "hoist" anyway, so why do developers still put those variable declarations deeper into the scope instead of at the top, especially if all the other declarations have already been manually moved?

在这两种情况下,变量无论如何都会“提升”,为什么开发人员仍将那些变量声明放到作用域的更深处而不是顶部,特别是如果所有其他声明都已经被手动移动了呢?

块作用域 (Block Scoping)

The most salient reason is because developers (often instinctively) want some variables to act as if they belong to a smaller, more limited section of the scope. In specific terms, there are cases where we want to scope a variable declaration to the block that it's solely associated with.

最明显的原因是因为开发人员(通常是本能地)希望某些变量的作用就像它们属于范围的较小,更有限的部分一样。 具体来说,在某些情况下,我们希望将变量声明的作用域限定为仅与之关联的

In the for (var i=..) .. case, it's almost universal that the developer intends for the i to only be used for the purposes of that loop, and not outside of it. In other words, the developer is putting the var i declaration in the for loop to stylistically signal to everyone else -- and their future self! -- that the i belongs to the for loop only. Same with the var tmp inside that if statement. tmp is a temporary variable, and only exists for the purposes of that if block.

for (var i=..) ..情况下,开发人员打算将i仅用于该循环的目的,而不是在循环之外使用,这几乎是普遍的。 换句话说,开发人员将var i声明放入 for循环中,以样式方式向其他所有人以及他们自己的未来发出信号! i仅属于for循环。 与if语句中的var tmp相同。 tmp是一个临时变量,仅在if块的目的存在。

Stylistically, we're saying: "don't use the variable anywhere else but right here".

从风格上讲,我们在说:“除了在这里,不要在其他任何地方使用变量”。

最小特权原则 (Principle of Least Privilege)

There's a software engineering called "principle of least privilege (or exposure)", which suggests that proper software design hides details unless and until it's necessary to expose them. We often do exactly this in module design, by hiding private variables and functions inside a closure, and exposing only a smaller subset of functions/properties as the public API.

有一种软件工程称为“最低特权原则(或公开原则)”,这表明适当的软件设计会隐藏细节,除非并且直到有必要公开这些细节为止。 我们经常做的正是这个模块设计,通过隐藏私有变量和函数的闭包内,只有露出的功能/性能更小的子集作为公共API。

Block scoping is an extension of this same mindset. What we're suggesting is, proper software puts variables as close as possible, and as far down in scoping/blocking as possible, to where it's going to be used.

块作用域是这种思维方式的扩展。 我们的建议是,适当的软件将变量尽可能地靠近变量,并将范围/阻塞尽可能地降低到要使用的位置。

You already instinctively know this exact principle. You already know that we don't make all variables global, even though in some cases that would be easier. Why? Because it's bad design. It's a design that will lead to (unintentional) collisions, which will lead to bugs.

您已经本能地知道这个确切的原理。 你已经知道,我们不会让所有变量的全局,即使在某些情况下会更容易些 。 为什么? 因为设计不好。 这种设计会导致(意外)碰撞,从而导致bug

So, you stick your variables inside the function they are used by. And when you nest functions inside of other functions, you nest variables inside those inner functions, as necessary and appropriate. And so on.

因此,您可以将变量放在使用它们的函数中。 并且,当您将函数嵌套在其他函数内部时,可以根据需要和适当的方式将变量嵌套在这些内部函数中。 等等。

Block scoping simply says, I want to be able to treat a { .. } block as a scope, without having to make a new function to encapsulate that scope.

块作用域简单地说,我希望能够将{ .. }块视为作用域,而不必创建新函数来封装该作用域。

You're following the principle by saying, "If I'm going to only use i for this for loop, I'll put it right in the for loop definition."

您遵循的原则是:“如果我仅将i用于此for循环,则将其放在for循环定义中。”

JS缺少块作用域 (JS Missing Block Scoping)

Unfortunately, JS has not historically had any practical way to enforce this scoping style, so it's been up to best behavior to respect the style being signaled. Of course, the lack of enforcement means these things get violated, and sometimes it's OK while other times it leads to bugs.

不幸的是,JS一直以来都没有任何切实可行的方法来强制执行这种范围定义样式,因此要尊重所发出的样式取决于最佳行为。 当然,缺乏强制执行意味着这些事情会被违反,有时还可以,而有时会导致错误。

Other languages (e.g., Java, C++) have true block scoping, where you can declare a variable to belong to a specific block instead of to the surrounding scope/function. Developers from those languages know well the benefits of using block scoping for some of their declarations.

其他语言(例如Java,C ++)具有真正的块作用域 ,您可以在其中声明变量属于特定块而不是周围的作用域/函数。 这些语言的开发人员都非常了解在某些声明中使用块作用域的好处。

They often feel JS has been lacking in expressive capability by missing a way to make an inline scope within a { .. } block instead of the heavier-weight inline function definition (aka IIFE -- Immediately Invoked Function Expression).

他们通常会感觉JS缺少表达能力,因为缺少了在{ .. }块中创建内联作用域而不是较重的内联函数定义(aka IIFE-立即调用函数表达式)的方法。

And they're totally right. JavaScript has been missing block scoping. Specifically, we've been missing a syntactic way to enforce what we already are comfortable expressing stylistically.

他们是完全正确的。 JavaScript已经缺少块作用域。 具体来说,我们一直缺少一种语法方法来强制执行我们已经习惯于使用样式表示的内容。

并非一切 (Not Everything)

Even in languages that have block scoping, not every variable declaration ends up block scoped.

即使在具有块作用域定义的语言中,并非每个变量声明都以块作用域为最终对象。

Take any well-written code base from such a language, and you are certainly going to find some variable declarations that exist at the function level, and others which exist at smaller block levels. Why?

从这种语言中获取任何编写良好的代码库,您肯定会找到在函数级别存在的一些变量声明,在较小的块级别存在的其他变量声明。 为什么?

Because that's a natural requirement of how we write software. Sometimes we have a variable we're going to use everywhere in the function, and sometimes we have a variable that we're going to use in just a very limited place. It's certainly not an all-or-nothing proposition.

因为那是我们编写软件的自然要求。 有时我们有一个变量要在函数中的任何地方使用,有时我们有一个变量要在非常有限的地方使用。 这当然不是一个全有或全无的主张。

Proof? Function parameters. Those are variables that exist for the entire function's scope. To my knowledge, no one seriously advances the idea that functions shouldn't have explicit named-parameters because they wouldn't be "block scoped", because most reasonable developers know what I'm asserting here:

证明? 功能参数。 这些是存在于整个函数范围内的变量。 据我所知,没有人认真地提出这样的想法,即函数不应具有显式的命名参数,因为它们不会被“块作用域”,因为大多数理性的开发人员都知道我在这里断言的内容:

Block scoping and function scoping are both valid and both useful, not just one or the other. This kind of code would be quite silly:

块作用域和功能作用域既有效又有用 ,而不仅仅是一个或另一个。 这种代码很傻:

function foo() {    // <-- Look ma, no named parameters!
    // ..

    {
        var x = arguments[0];
        var y = arguments[1];

        // do something with `x` and `y`
    }

    // ..
}


You almost certainly wouldn't write code like that, just to have a "block scoping only" mentality about code structure, anymore than you'd have x and y be global variables in a "global scoping only" mentality.

您几乎肯定不会写这样的代码,只是对代码结构具有“仅块作用域”的心态,而不是在“仅全局作用域”的心态中拥有xy是全局变量。

No, you'd just name the x and y parameters, and use them wherever in the function you need.

不,您只需命名xy参数,然后在所需的函数中的任何位置使用它们。

The same would be true of any other variable declarations you might create which you intend and need to use across the entire function. You'd probably just put a var at the top of the function and move on.

对于您可能要创建并需要在整个函数中使用的任何其他变量声明,也是如此。 您可能只是将var放在函数的顶部并继续前进。

介绍let (Introducing let)

Now that you understand why block scoping is important, and importantly swallowed the sanity check that it amends function/global scoping rather than replacing it, we can be excited that ES6 is finally introducing a direct mechanism for block scoping, using the let keyword.

既然您了解了块作用域为何重要的原因,并且重要地吞噬了对功能/全局作用域进行修改(而不是替换)的健全性检查,我们很高兴ES6 终于使用let关键字引入了一种直接的块作用域机制。

In its most basic form, let is a sibling to var. But declarations made with let are scoped to the blocks in which they occur, rather than being "hoisted" to the enclosing function's scope as vars do:

在最基本的形式中, letvar的同级对象。 但是,用let进行的声明的作用域仅限于发生它们的块,而不是像var那样“提升”到封闭函数的作用域中:

function foo() {
    a = 1;                  // careful, `a` has been hoisted!

    if (a) {
        var a;              // hoisted to function scope!
        let b = a + 2;      // `b` block-scoped to `if` block!

        console.log( b );   // 3
    }

    console.log( a );       // 1
    console.log( b );       // ReferenceError: `b` is not defined
}


Yay! let declarations not only express but also enforce block scoping!

好极了! let声明不仅可以表达,还可以强制执行块作用域!

Basically, any place a block occurs (like a { .. } pair), a let can create a block scoped declaration inside it. So wherever you need to create limited-scope declarations, use let.

基本上,任何发生块的地方(例如{ .. }对), let都可以在其中创建块作用域声明。 因此,无论何时需要创建有限范围的声明,都可以使用let

Note: Yeah, let doesn't exist pre-ES6. But quite a few ES6-to-ES5 transpilers exist -- for example: traceur, 6to5, and Continuum -- which will take your ES6 let usage (along with most of the rest of ES6!) and convert it to ES5 (and sometimes ES3) code that will run in all relevant browsers. The "new normal" in JS development, given that JS is going to start rapidly evolving on a feature-by-feature basis, is to use such transpilers as a standard part of your build process. This means that you should start authoring in the latest and greatest JS right now, and let tools worry about making that work in (older) browsers. No longer should you foregoe new language features for years until all previous browsers go away.

注意:是啊, let不存在预ES6。 但是存在相当多的ES6-to-ES5编译器-例如: traceur6to5Continuum-它将占用您的ES6 let用法(以及大部分ES6!),并将其转换为ES5(有时是) ES3)代码,将在所有相关的浏览器中运行。 鉴于JS将开始逐个功能地发展,因此JS开发中的“新常态”是将这样的编译器用作构建过程的标准部分。 这意味着,你应该开始在最新和最伟大的JS创作,现在 ,让工具担心使这项工作在(以上)的浏览器。 多年以前,您不再应该放弃新的语言功能,直到所有以前的浏览器都消失了。

隐式与显式 (Implicit vs Explicit)

It's easy to get lost in the excitement of let that it's an implicit scoping mechanism. It hijacks an existing block, and adds to that block's original purpose also the semantics of being a scope.

let感到兴奋的是,它是一个隐式作用域机制,很容易迷失方向。 它劫持现有的块,并增加了该块的最初目的的是一个范围语义。

if (a) {
    let b = a + 2;
}


Here, the block is an if block, but let merely being inside it means that the block also becomes a scope. Otherwise, if let was not there, the { .. } block is not a scope.

这里,该块是一个if块,但let仅仅是在其内部意味着块也成为一个范围。 否则,如果let不存在,则{ .. }不是作用域

Why does that matter?

为什么这么重要?

Generally, developers prefer explicit mechanisms rather than implicit mechanisms, because usually that makes code easier to read, understand, and maintain.

通常,开发人员更喜欢显式机制而不是隐式机制 ,因为通常这会使代码更易于阅读,理解和维护。

For example, in the realm of JS type coercion, many developers would prefer an explicit coercion over an implicit coercion:

例如,在JS类型强制的领域中,许多开发人员更喜欢显式强制而不是隐式强制

var a = "21";

var b = a * 2;          // <-- implicit coercion -- yuck :(
b;                      // 42

var c = Number(a) * 2;  // <-- explicit coercion -- much better :)
c;                      // 42


Note: To read more on this side-topic of implicit/explicit coercion, see my "You Don't Know JS: Types & Grammar" book, specifically Chapter 4: Coercion.

注意:要了解有关隐式/显式强制的此主题的更多信息,请参阅我的“您不知道JS:类型和语法” ,尤其是第4章:强制

When an example shows a block with only one or a few lines of code inside it, it's fairly easy to see if the block is scoped or not:

当示例显示其中仅包含一行或几行代码的块时,很容易看到该块是否为作用域:

if (a) {    // block is obviously scoped
    let b;
}


But in more real world scenarios, many times a single block can have dozens of lines of code, maybe even a hundred or more. Setting aside the preference/opinion that such blocks shouldn't exist -- they do, it's a reality -- if let is buried way down deep in the middle of all that code, it becomes much harder to know if any given block is scoped or not.

但是在更现实的情况下,很多时候一个块可以包含数十行代码,甚至一百甚至更多行。 抛开这些块不应该存在的偏好/观点-它们确实存在,这是现实-如果let被深深地埋在所有代码的中间,则很难确定任何给定块的作用域或不。

Conversely, if you find a let declaration somewhere in the code, and you want to know to which block it belongs, instead of just visually scanning upwards to the nearest function keyword, you now need to visually scan to the nearest { opening curly brace. That's harder to do. Not a lot harder, but harder nonetheless.

相反,如果您在代码中的某个地方找到了一个let声明,并且想知道它属于哪个块,而不是仅仅视觉上向上扫描到最近的function关键字,现在就需要视觉上扫描到最近的{开括号。 那很难做到。 不是很多困难,但困难仍然。

It's a bit more mental tax.

这是更多的精神税。

隐性危害 (Implicit Hazards)

But it's not only a mental tax. Whereas var declarations are "hoisted" to the top of the enclosing function, let declarations are not treated as having been "hoisted" to the top of the block. If you accidentally try to use a block-scoped variable in the block earlier than where its declaration exists, you'll get an error:

但这不仅是精神税。 var声明“悬挂”在封闭函数的顶部,而let声明不被视为“悬挂”在块的顶部。 如果您不小心尝试在其声明所在的位置之前在块中使用块作用域变量,则会收到错误消息:

if (a) {
    b = a + 2;      // ReferenceError: `b` is not defined

    // more code

    let b = ..

    // more code
}


Note: The period of "time" between the opening { and where the let b appears is technically called the "Temporal Dead Zone" (TDZ) -- I'm not making that up! -- and variables cannot be used in their TDZ. Technically, each variable has its own TDZ, and they sort of overlap, again from the opening of the block to the official declaration/initialization.

注意:{到开始出现let b之间的“时间”段在技术上被称为“临时死区”(TDZ)–我没有做过! -并且变量不能在其TDZ中使用。 从技术上讲,每个变量都有其自己的TDZ,并且从块的打开到正式声明/初始化,它们都有某种重叠。

Since we had previously put the let b = .. declaration further down in the block, and then we wanted to come back and use it earlier in the block, we have a hazard -- a footgun -- where we forgot we needed to go find the let keyword and move it to the earliest usage of the b variable.

由于我们之前已经将let b = ..声明放在代码块的更下方,然后我们想返回并在代码块的较早位置使用它,所以我们有一个危险–脚枪–我们忘了去找到let关键字并将其移至b变量的最早用法。

In all likelihood, developers are going to get bitten by this TDZ "bug", and they'll eventually learn from that bad experience to always put their let declarations at the top of the block.

开发人员极有可能会被TDZ“错误”所咬,最终他们将从不良的经验中学习,始终将let声明放在块的顶部。

And there's another hazard to implict let scoping: the refactoring hazard.

而且还有另外一个危害隐let划定范围:重构危险。

Consider:

考虑:

if (a) {
    // more code

    let b = 10;

    // more code

    let c = 1000;

    // more code

    if (b > 3) {
        // more code

        console.log( b + c );

        // more code
    }

    // more code
}


Let's say later, you realize the if (b > 3) part of the code needs to be moved outside the if (a) { .. block, for whatever reason. You realize you also need to grab the let b = .. declaration to move along with it.

稍后再说,您意识到无论出于何种原因,代码的if (b > 3)部分都需要移出if (a) { ..块之外。 您意识到您还需要抓住let b = ..声明才能与之一起移动。

But you don't immediately realize that the block relies on c as well -- because it's a bit more hidden down in the code -- and that c is block scoped to the if (a) { .. block. As soon as you move the if (b > 3) { .. block, now the code breaks, and you have to go find the let c = .. declaration and figure out if it can move, etc.

但是您不会立即意识到该块也依赖c因为它在代码中更加隐蔽-并且c的范围仅限于if (a) { ..块。 一旦移动了if (b > 3) { ..块,现在代码就中断了,您必须去查找let c = ..声明并弄清楚它是否可以移动,等等。

I could keep coming up with other scenarios -- hypothetical yes, but also extremely informed by lots of experience not only with my own but with others own real world code -- but I think you get the point. It's awfully easy to get yourself into these hazard traps.

我可以继续提出其他方案-假设是的,但是我不仅拥有自己的经验,而且还拥有与其他人自己的真实世界代码的丰富经验,他们对此非常有帮助-但我想您明白了。 让自己陷入这些危险陷阱非常容易。

If there had been explicit scopes for b and c, it would probably have been a little bit easier to figure out what refactoring is necessary, rather than stumbling along to figure it out implicitly.

如果bc有明确的作用域,那么找出必要的重构可能要容易一些,而不是隐式地找出它。

明确let范围 (Explicit let Scope)

If I've convinced you that the implicit nature of let declarations could be a problem/hazard -- if you're not extremely careful, as well as every other developer that ever works on your code! -- then what's the alternative? Do we avoid block scoping entirely?

如果我已经说服您, let声明的隐式性质可能会带来问题/危险-如果您以及其他所有使用过代码的开发人员都不太谨慎,请注意! -那么还有什么选择? 我们是否完全避免块作用域?

No! There are better ways.

没有! 有更好的方法。

Firstly, you can force yourself into a style/idiom that not only puts your let declarations at the top of the scope, but also that creates an explicit block for such scope. For example:

首先,您可以强迫自己进入一种样式/习惯用法,不仅将您的let声明放在作用域的顶部,而且还为该作用域创建了一个显式块。 例如:

if (a) {
    // more code

    // make an explicit scope block!
    { let b, c;
        // more code

        b = 10;

        // more code

        c = 1000;

        // more code

        if (b > 3) {
            // more code

            console.log( b + c );

            // more code
        }
    }

    // more code
}


You'll see here I created a naked { .. } pair, and put the let b, c; declaration right at the very top, even on the same line. I'm making it as clear and explicit as possible that this is a scope block, and that it holds b and c.

您会在这里看到我创建了一个赤裸的{ .. }对,并将let b, c;放进去let b, c; 声明就在最上面,即使在同一行。 我正在尽可能清楚地表明这是一个作用域块,并且它包含bc

If at a later time I need to move some b code around, and I go find the combined scope for b and c, it's not only easier to recognize, but easier to accomplish, that I can move the entire { let b, c; .. } block safely.

如果以后需要移动一些b代码,然后找到bc的组合范围,则可以移动整个{ let b, c; .. } { let b, c; .. }安全地阻止。

Is this perfect? Of course not. But it's better, and has less hazards and less mental tax (even by little bit) than the implicit style/idioms from earlier. I implore all of you, as you begin to use let block scoping, please consider and prefer a more explicit form over the implicit form.

这样完美吗? 当然不是。 但是比起以前的隐式样式/习惯用法, 它更好 ,危害更少,精神税更少(甚至一点点)。 我恳请大家,当您开始使用let块作用域时,请考虑并选择一种比隐式形式更明确的形式。

总是显性的? (Always Explicit?)

In fact, I'd say being explicit is so important that the only exception I've found to that "rule" is that I like and use for (let i=0; .. ) ... It's debatable if that's implicit or explicit. I'd say it's more explicit than implicit. But it's perhaps not quite as explicit as { let i; for (i=0; ..) .. }.

实际上,我想说的是明确的是如此重要,以至于我发现该“规则”的唯一例外是我喜欢并for (let i=0; .. ) .. 如果是隐式显式的,则值得商bat。 我会说这是比更加明确 。 但它也许不是很明确{ let i; for (i=0; ..) .. } { let i; for (i=0; ..) .. }

There's actually a really good reason why for (let i=0; ..) .. could be better, though. It relates to scope closures, and it's very cool and powerful!

但是,实际上有一个很好的理由说明为什么for (let i=0; ..) ..会更好。 它与作用域关闭有关,非常酷而强大!

{ let i;
    for (i=1; i<=5; i++) {
        setTimeout(function(){
            console.log("i:",i);
        },i*1000);
    }
}


That code will, like its more typical var counterpart, not work, in that it'll print out i: 6 five times. But this code does work:

该代码将像其更典型的var对应代码那样不起作用 ,因为它将打印出i: 6五次。 但是这段代码确实有效

for (let i=1; i<=5; i++) {
    setTimeout(function(){
        console.log("i:",i);
    },i*1000);
}


It'll print out i: 1, i: 2, i: 3, etc. Why?

它会打印出i: 1i: 2i: 3等。为什么?

Because the ES6 specification actually says that let i in a for loop header scopes i not only to the for loop, but to each iteration of the for loop. In other words, it makes it behave like this:

由于ES6规范实际上说, let ifor环头作用域i不仅要for循环,但到的每个迭代for循环 。 换句话说,它的行为如下:

{ let k;
    for (k=1; k<=5; k++) {
        let i = k; // <-- new `i` for each iteration!
        setTimeout(function(){
            console.log("i:",i);
        },i*1000);
    }
}


That's super cool -- it solves a very common problem developers have with closures and loops!

太酷了-解决了开发人员在闭包和循环中遇到的一个非常普遍的问题!

Note: This doesn't work in browsers yet, even those with let. The ES6 spec requires it, but at time of writing, no browsers are compliant on this particular per-iteration nuance. If you want proof, try putting the code into ES6fiddle. See...

注意:即使在使用let浏览器中,这仍然无法使用。 ES6规范要求这样做,但是在撰写本文时,没有任何浏览器符合此特定的每项细微差别。 如果需要证明,请尝试将代码放入ES6fiddle中 。 看到...

for-let scoping

更明确的let作用域 (Even More Explicit let Scope)

OK, so maybe I've convinced you that explicit scopes are a bit better. The disadvantage of the above is that it's not enforceably required that you follow that style/idiom of { let b, c; .. }, which means you or someone else on your team could mess up and not follow it.

好的,所以也许我已经说服了您, 显式作用域要好一些。 上面的缺点是没有强制要求您遵循{ let b, c; .. } { let b, c; .. } ,这意味着您或您团队中的其他人可能会搞砸并且无法遵循。

There's another option. Instead of using the "let declaration form", we could use the "let block form":

还有另一种选择。 除了使用“ let声明形式”,我们可以使用“ let块形式”:

if (a) {
    // make an explicit scope block!
    let (b, c) {
        // ..
    }
}


It's a slight change, but look closely: let (b, c) { .. } creates an explicit block of scope for b and c. It's syntactically requiring b and c to be declared at the top, and it's a block that's nothing but a scope.

这是一个微小的变化,但请仔细观察: let (b, c) { .. }bc创建一个显式范围块。 从句法上讲,要求bc在顶部声明,并且它是一个块,只不过是一个作用域。

In my opinion, this is the best way to use let-based block scoping.

我认为,这是使用基于let的块作用域的最佳方法。

But there's a problem. The TC39 committee voted to not include this particular form of let in ES6. It may come in later, or never, but it's definitely not in ES6.

但是有一个问题。 在TC39委员会投票决定不包含的这种特殊形式let的ES6。 它可能会在以后出现,或者永远不会出现,但是绝对不是在ES6中。

Ugh. But this isn't the first, nor the last, that something that's more preferable loses out to an inferior option.

啊。 但这不是第一个也不是最后一个,更可取的东西会输给次等选择。

So, are we just stuck in the previous form?

那么,我们只是停留在以前的形式中吗?

Perhaps not. I've built a tool called "let-er", which is a transpiler for "let block form" code. By default, it's in ES6-only mode, and it takes code like:

也许不是。 我建立了一个名为“ let-er”的工具,该工具是“ let block form”代码的编译器。 默认情况下,它处于仅限ES6的模式,并且需要如下代码:

let (b, c) {
    ..
}


And produces:

并产生:

{ let b, c;
    ..
}


That's not too awful, is it? It's a pretty simple transformation, actually, to get non-standard "let block form" into standard "let declaration form". After you run let-er for this transformation, you can then use a regular ES6 transpiler to target pre-ES6 environments (browsers, etc).

那不是太可怕了吗? 实际上,这是一个非常简单的转换,可以将非标准的“ let块形式”转换为标准的“ let声明形式”。 您运行这一转变让儿后,就可以使用普通的ES6 transpiler到目标预ES6环境(浏览器等)。

If you'd like to use let-er standalone without any other transpilers, only for let-based block scoping, you can optionally set the ES3 mode/flag, and it will instead produce this (admittedly hacky junk):

如果您想使用让-ER独立使用,无需任何其他transpilers,只为let基于块的作用域,你可以选择设置ES3模式/标志,并且这反而产生这种(公认哈克垃圾):

try{throw void 0}catch( b ){try{throw void 0}catch( c ){
    ..
}}


Yeah, it uses the little-known fact that try..catch has block scoping built into the catch clause.

是的,它使用了鲜为人知的事实,即try..catchcatch子句中内置了块作用域。

No one wants to write that code, and no one likes the degraded performance that it brings. But keep in mind, it's compiled code, and it's only for targeting really old browsers like IE6. The slower performance is unfortunate (to the tune of about 10% in my tests), but your code is already running pretty slowly/badly in IE6, so...

没有人愿意编写该代码,也没人喜欢它带来的性能下降。 但是请记住,它是编译后的代码,仅用于定位真正的旧浏览器(如IE6)。 不幸的是,性能下降很慢(在我的测试中约为10%),但是您的代码已经在IE6中运行得非常慢/很差,所以...

Anyway, let-er by default targets standard ES6, and thus plays well with other ES6 tools like standard transpilers.

无论如何, 让儿默认目标标准ES6,从而与其他ES6工具,如标准transpilers打得很好。

The choice to make is would you rather author code with let (b, c) { .. } style or is { let b, c; .. } OK enough?

做出的选择是您宁愿编写具有let (b, c) { .. }样式的代码,还是{ let b, c; .. } { let b, c; .. }好吗?

I use let-er in my projects now. I think it's the better way. And I'm hoping maybe in ES7, the TC39 members realize how important it is to add the "let block form" into JS, so that eventually let-er can go away!

我现在在项目中使用leter 。 我认为这是更好的方法。 我希望在ES7中,TC39成员意识到将“ let block form”添加到JS中的重要性,这样最终let-er可以消失!

Either way, explicit block scoping is better than implicit. Please block scope responsibly.

无论哪种方式, 显式块作用域都比隐式更好。 请负责任地阻止范围。

let Replaces var吗? (let Replaces var?)

Some prominent members of the JS community and the TC39 committee like to say, "let is the new var." In fact, some have literally suggested (hopefully in jest!?) to just do a global find-n-replace of var for let.

JS社区和TC39委员会的一些杰出成员喜欢说:“ let新的var成为现实。” 实际上,有些人确实建议(希望开玩笑!?)只是为let做一个全局的find-n- var替换。

I cannot express how incredibly stupid that advice would be.

我无法表达这种建议多么愚蠢。

Firstly, the hazards we mentioned above would be enormously more likely to crop up in your code, as the odds are your code is not perfectly written with respect to var usage. For example, this kind of code is extremely common:

首先,我们上面提到的危险很有可能在您的代码中冒出来,因为很有可能您的代码就var用法而言并不是很完美。 例如,这种代码非常普遍:

if ( .. ) {
    var foo = 42;
}
else {
    var foo = "Hello World";
}


We can all probably agree it should have been written as:

我们都可能同意它应该写成:

var foo;

if ( .. ) {
    foo = 42;
}
else {
    foo = "Hello World";
}


But it's not written that way yet. Or, you're accidentally doing things like:

但这还不是那样写的。 或者,您不小心在执行以下操作:

b = 1;

// ..

var b;


Or you're accidentally relying on non-block-scoped closure in loops:

或您无意中依赖循环中的非块范围闭包:

for (var i=0; i<10; i++) {
    if (i == 2) {
        setTimeout(function(){
            if (i == 10) {
                console.log("Loop finished");
            }
        },100);
    }
}


So, if you just blindly replace var with let in existing code, there's a pretty good chance that at least some place will accidentally stop working. All of the above would fail if let replaced var, without other changes.

因此,如果只用现有代码中的let盲目替换var ,那么很有可能至少有一些地方会意外停止工作。 如果let替换var ,而没有其他更改,上述所有操作将失败。

If you're going to retrofit existing code with block scoping, you need to go case by case, carefully, and you need to reason about and rationalize if it's a place where block scoping is appropriate or not.

如果要使用块作用域对现有代码进行改造,则需要仔细进行个案研究,并且需要推理和合理化它是否适合使用块作用域。

There will certainly be places where a var was used stylistically, and now a let is better. Fine. I still don't like the implicit usage, but if that's your cup o' tea, so be it.

肯定会有在样式上使用var地方,现在let更好。 精细。 我仍然不喜欢隐式用法,但是如果那是您的茶水,那就这样吧。

But there will also be places that, in your analysis, you realize the code has structural issues, where a let would be more awkward, or would create more confusing code. In those places, you may choose to fix the code, but you may also quite reasonably decide to leave var alone.

但是在分析中,也有一些地方您意识到代码存在结构性问题, let代码变得更尴尬,或者创建更多混乱的代码。 在那些地方,您可以选择修复代码,但也可以相当合理地决定不使用var

Here's what bugs me the most about "let is the new var": it assumes, whether they admit it or not, an elitist view that all JS code should be perfect and follow proper rules. Whenever you bring up those earlier cases, proponents will simply strike back, "well, that code was already wrong."

这就是“ let成为新的var ”给我最大的var :无论他们是否接受,它都假设一个精英主义者的观点,即所有JS代码都应该是完美的并遵循正确的规则。 每当您提起那些较早的案例时,支持者就会简单地反击,“嗯,该代码已经是错误的”。

Sure. But that's a side point, not the main point. It's equally hostile to say, "only use let if your scoping is already perfect, or you're prepared to rewrite it to make it perfect, and keep it perfect."

当然。 但这只是一个侧面,而不是重点。 同样地说,“仅当您的作用域已经完善时才使用let ,或者您准备重写它以使其完美并保持完美”。

Other proponents will try to temper it with, "well, just use let for all new code."

其他支持者将尝试使用“好吧,对于所有新代码都使用let ”来改善它。

This is equivalently elitist, because again it assumes that once you learn let and decide to use it, you'll be expected to write all new code without ever running into any hazard patterns.

这相当于精英人士,因为它再次假设一旦您学习let并决定使用它,就可以期望编写所有新代码而不会遇到任何危险模式。

I bet TC39 members can do that. They're really smart and really intimate with JS. But the rest of us are not quite so lucky.

我敢打赌TC39成员可以做到这一点。 他们非常聪明,并且与JS非常亲密。 但是我们其他人并不是很幸运。

letvar的新伴侣 (let is the new companion to var)

The more reasonable and more realistic perspective, the one I take because my primary interface with JS is through the students/attendees that I speak to, teach, and work with, is to embrace refactoring and improving code as a process, not an event.

之所以说这是一种更合理,更现实的观点,是因为我与JS的主要接口是通过与之交谈,教学和合作的学生/参与者,是将重构和改进代码视为一个过程,而不是一个事件。

Sure, as you learn good scoping best practices, you should make code a little better each time you touch it, and sure, your new code should be a little better than your older code. But you don't just flip a switch by reading a book or blog post, and now all of a sudden you have everything perfect.

当然,当您学习良好的范围界定最佳实践时,您应该在每次触摸时使代码变得更好,并且可以肯定的是,新代码应该比旧代码更好。 但是,您不只是通过阅读书籍或博客文章来进行切换,现在突然之间您拥有了完美的一切。

Instead, I think you should embrace both the new let and the old var as useful signals in your code.

相反,我认为您应该在代码中同时包含新的let和旧的var作为有用的信号。

Use let in places you know you need block scoping, and you've specifically thought about those implications. But continue to use var for variables that either cannot easily be block scoped, or which shouldn't be block scoped. There are going to be places in real world code where some variables are going to be properly scoped to the entire function, and for those variables, var is a better signal.

在您知道需要块作用域的地方使用let ,并且您已经特别考虑了这些含义。 但是,对于无法轻松进行块作用域定义或不应该被块作用域定义的变量,请继续使用var 。 在现实世界的代码中,某些变量将被适当地限定在整个函数中,而对于那些变量, var是一个更好的信号。

function foo() {
    var a = 10;

    if (a > 2) {
        let b = a * 3;
        console.log(b);
    }

    if (a > 5) {
        let c = a / 2;
        console.log(c);
    }

    console.log(a);
}


In that code, let screams out at me, "hey, I'm block scoped!" It catches my attention, and I thus pay it more care. The var just says, "hey, I'm the same old function-scoped variable, because I'm going to be used across a bunch of scopes."

在该代码中, let大喊:“嘿,我被限制了范围!” 它引起了我的注意,因此我要加倍注意。 var只是说:“嘿,我是相同的旧函数作用域变量,因为我将在多个作用域中使用它。”

What about just saying let a = 10 at the top level of the function? You can do that, and it'll work fine.

只说let a = 10函数顶部的let a = 10等于let a = 10呢? 您可以这样做,它将很好地工作。

But I don't think it's a good idea. Why?

但是我认为这不是一个好主意。 为什么?

First, you lose/degrade the difference in signal between var and let. Now, it's just position that signals the difference, rather than syntax.

首先,您要丢失/降级varlet之间的信号差。 现在,只是位置发出信号,而不是语法。

Secondly, it's still a potential hazard. Ever had a weird bug in a program, and started throwing try..catch around things to try to figure it out? I sure do.

其次,它仍然是潜在的危害。 曾经在程序中遇到一个怪异的错误,然后开始抛出try..catch解决问题吗? 我当然知道

Oops:

糟糕:

function foo() {
    try {
        let a = 10;

        if (a > 2) {
            let b = a * 3;
            console.log(b);
        }
    }
    catch (err) {
        // ..
    }

    if (a > 5) {
        let c = a / 2;
        console.log(c);
    }

    console.log(a);
}


Block scoping is great, but it's not a silver bullet, and it's not appropriate for everything. There are places where function scoping of vars, and indeed of the "hoisting" behavior, are quite useful. These are not abject failures in the language that should be removed. They are things that should be used responsibly, as should let.

块范围界定固然不错,但这不是万灵丹,也不适合所有情况。 在某些地方,对var进行函数作用域定义以及“提升”行为的确很有用。 这些不是应该删除的语言中的绝对失败。 应该let它们负责任地使用它们。

Here's the better way to say it: "let is the new block scoping var". That statement emphasizes that let should replace var only when var was already signaling block scoping stylistically. Otherwise, leave var alone. It's still doing its job pretty well!

这是更好的说法:“ let块作用域为 var ”。 该声明强调, 仅当 var已经在样式上发出信号let作用域时,才应替换var 。 否则,请不要var 。 它仍然做得很好!

摘要 (Summary)

Block scoping is cool, and let gives us that. But be explicit about your block scopes. Avoid implicit let declarations strewn about.

块范围很酷, let我们知道。 但是要明确您的块范围。 避免散布隐式的let声明。

let + var, not s/var/let/. Just frown then smirk at the next person who tells you, "let is the new var."

let + var ,而不是s/var/let/ 。 只是皱眉,然后对下一个告诉你的人傻笑,“ let新的var 。”

let improves scoping options in JS, not replaces. var is still a useful signal for variables that are used throughout the function. Having both, and using both, means scoping intent is clearer to understand and maintain and enforce. That's a big win!

let改善了JS中的范围设定选项,而不是替代。 对于在整个函数中使用的变量, var仍然是有用的信号。 同时拥有和使用两者,意味着了解,维护和执行作用域意图更加清晰。 那是一个巨大的胜利!

翻译自: https://davidwalsh.name/for-and-against-let

discuz支持反对

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值