You Don't Know JS: Scope & Closures(翻译)

Chapter 1: What is Scope?

第一章:什么是作用域

One of the most fundamental paradigms of nearly all programming languages is the ability to store values in variables, and later retrieve or modify those values. In fact, the ability to store values and pull values out of variables is what gives a program state.

在几乎所有编程语言中,储存值到变量中和稍后检索或改变这些值都是最基本的能力之一。事实上,这种存储值到变量和将值移除变量的能力才给了程序以“状态”。

Without such a concept, a program could perform some tasks, but they would be extremely limited and not terribly interesting.

没了这个概念的话,一个程序也能完成一些任务,但他们就会有很多地方被限制并且一点都不有趣了。

But the inclusion of variables into our program begets the most interesting questions we will now address: where do those variables live? In other words, where are they stored? And, most importantly, how does our program find them when it needs them?

但是把变量包含到我们的程序中带来的最有趣的问题是:这些变量在哪儿?或者说,他们存储在哪儿?最重要的是,当我们的程序需要这些变量的时候,它是如何找到他们的?

These questions speak to the need for a well-defined set of rules for storing variables in some location, and for finding those variables at a later time. We'll call that set of rules: Scope.

这个问题需要一个定义明确的规则,用于存储变量在某些位置,以及在之后再找出这些变量,我们可以把这个规则叫:作用域

But, where and how do these Scope rules get set?

但是,作用域规则在哪儿设置?怎么设置的?

Compiler Theory

编译原理

It may be self-evident, or it may be surprising, depending on your level of interaction with various languages, but despite the fact that JavaScript falls under the general category of "dynamic" or "interpreted" languages, it is in fact a compiled language. It is not compiled well in advance, as are many traditionally-compiled languages, nor are the results of compilation portable among various distributed systems.

这可能是不言而喻的,或者可能令人惊讶——取决于你融会贯通各种语言的能力,但是尽管JS属于一般范畴的动态型和解释型语言,实际上它却是一个编译型语言。它不是提前编译好的,因为有许多传统编译语言也不是各种分布式编译系统的产物。

But, nevertheless, the JavaScript engine performs many of the same steps, albeit in more sophisticated ways than we may commonly be aware, of any traditional language-compiler.

但是,尽管如此,JS引擎也执行着许多相同的步骤,尽管它使用的是一种更为复杂的方式——比我们可能意识到的任何传统编译语言

In traditional compiled-language process, a chunk of source code, your program, will undergo typically three steps before it is executed, roughly called "compilation": 

在传统的编译语言过程中,一块源代码,也就是你的程序,会在执行前遵从典型的‘三步走’,也就是‘编译’

  1. Tokenizing/Lexing: breaking up a string of characters into meaningful (to the language) chunks, called tokens. For instance, consider the program: var a = 2;. This program would likely be broken up into the following tokens: var, a, =, 2, and ;. Whitespace may or may not be persisted as a token, depending on whether it's meaningful or not.

    Note: The difference between tokenizing and lexing is subtle and academic, but it centers on whether or not these tokens are identified in a stateless or stateful way. Put simply, if the tokenizer were to invoke stateful parsing rules to figure out whether a should be considered a distinct token or just part of another token, that would be lexing.

       1.分词/词法分析:分词指将一段字符串破成有意义的(相对于这门语言)小块。比如,考虑这段代码:var a=2;这条程序可能会被破成下面的部分:var , a, =,2,还有;  空白可能成为分词小块也可能不会,这得取决于它是否有含义。

          注:分词和词法分析之间的区别既微妙又学术,但关键在于这些小块被明确为有状态的还是无状态的。简单点儿说,如果分词块儿调用的是有状态的词法分析规则,来区分a应该作为一个单独的词法块儿还是仅仅是另外一块词法块儿的一部分,那么这个过程就称作词法分析。(我的理解就是 把字符串分为有意义的tokens这是分词,确定每一小块代码段是应该独立还是应该和另外一块合并成一个token,这是词法分析)。

     2.Parsing: taking a stream (array) of tokens and turning it into a tree of nested elements, which collectively represent the grammatical structure of the program. This tree is called an "AST" (Abstract Syntax Tree).

        The tree for var a = 2; might start with a top-level node called VariableDeclaration, with a child node called Identifier (whose value is a), and another child called AssignmentExpression which itself has a child called NumericLiteral (whose value is 2).

      2.解析:取一段分词过的代码流,然后把它变成一个代表了程序语法结构的嵌套元素树,这个树的名字叫AST(Abstract Syntax Tree).

        在这个树状结构的解析中,对于var a=2;可能从最高层——变量声明(VariableDeclaration)开始,然后是它的子节点——标识符(Identifier)(它的值是a),已经另外一个子节点AssignmentExpression的子节点叫NumericLiteral(它的值是2)。

      3.Code-Generation: the process of taking an AST and turning it into executable code. This part varies greatly depending on the language, the platform it's targeting, etc.

         So, rather than get mired in details, we'll just handwave and say that there's a way to take our above described AST for var a = 2; and turn it into a set of machine instructions to actually create a variable called a (including reserving memory, etc.), and then store a value into a.

         Note: The details of how the engine manages system resources are deeper than we will dig, so we'll just take it for granted that the engine is able to create and store variables as needed.

      3.代码生成:这个过程是使用AST将代码块变为可执行的代码,这部分的过程很依赖于语言本身,包括它的目标平台等。

         所以为了避免在细节里太纠结,我们就直接说上述的AST处理了我们的var a = 2;并且把它变成了一行机器指令——这行指令创造出一个变量a,并且存了一个值进去。

         注:引擎如何管理系统资源这是一个很深的坑,超出了我们的讨论范围。所以我们只需要知道引擎可以按需求创造出变量并且给它赋值就可以了。

  The JavaScript engine is vastly more complex than just those three steps, as are most other language compilers. For instance, in the process of parsing and code-generation, there are certainly steps to optimize the performance of the execution, including collapsing redundant elements, etc.

  JS引擎做的工作远比这三步要复杂,比如,在解析和代码生成这两步的过程中,还有一定的步骤去以优化性能的执行,包括折叠冗余元素等。

  So, I'm painting only with broad strokes here. But I think you'll see shortly why these details we do cover, even at a high level, are relevant.

  所以,我们就言止于此好了,但是你很快就会意识到为什么我们需要关心这个,即使处于一个很高的水平,这些也是息息相关的。

   For one thing, JavaScript engines don't get the luxury (like other language compilers) of having plenty of time to optimize, because JavaScript compilation doesn't happen in a build step ahead of time, as with other languages.

   还有一件事是js引擎并不像其他语言编译一样有充分的时间去优化,因为js编译并没有像其他语言一样发生在构造之前

   For JavaScript, the compilation that occurs happens, in many cases, mere microseconds (or less!) before the code is executed. To ensure the fastest performance, JS engines use all kinds of tricks (like JITs, which lazy compile and even hot re-compile, etc.) which are well beyond the "scope" of our discussion here.

  对于js来说,在许多情况下编译发生在执行这段代码前几微秒(或者更少!)的时间里,为了确保最快的性能,js引擎使用了各种技巧,这些都远远超出了我们讨论的“作用域”。

   Let's just say, for simplicity's sake, that any snippet of JavaScript has to be compiled before (usually right before!) it's executed. So, the JS compiler will take the program var a = 2; and compile it first, and then be ready to execute it, usually right away.

  我们就这么说吧,为了简单起见,任何js代码都会在执行前被编译,所以,js编译器会拿走程序var a = 2,然后先编译它,然后准备去执行它。

原文地址:https://www.cnblogs.com/xisitan/p/5377539.html