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":
在传统的编译语言过程中,一块源代码,也就是你的程序,会在执行前遵从典型的‘三步走’,也就是‘编译’
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,然后先编译它,然后准备去执行它。
You Don't Know JS: Scope & Closures(翻译)的更多相关文章
- You Don't Know JS: Scope & Closures (第一章:什么是Scope)
Content What is Scope? Lexical Scope Function Vs. Block Scope Hoisting Scope Closures Appendix: Dyna ...
- You Don't Know JS: Scope & Closures (第4章: Hoisting)
Chapter4: Hoisting 变量附加到哪个层次的scope,由它们在哪里和如何声明(let, var)来决定. Function scope/Block scope都有相同的法则:任何变量在 ...
- You Don't Know JS: Scope & Closures (第3章: 函数 vs 块作用域)
第二章,作用域由一系列的bubbles组成.每一个都代表了一个container或bucket,装着被声明的identifiers(variables, functions).这些bubbles相互嵌 ...
- You Don't Know JS: Scope & Closures (第2章: Lexical Scope)
2种主要的models for how scope work. 最普遍的是Lexical Scope. 另一种 Dynamic Scope.(在Appendix a中介绍.和Lexical Scope ...
- You Don't Know JS: Scope & Closures (附加:Lexical/dynamic作用域)(附加:Lexical-this)
JavaScript只有Lexical Scope 模式 Lexical Scope就是在写代码的时候,定义函数的时候创建的作用域! 而动态作用域是在runtime时,函数被调用的地方的作用域! 实际 ...
- (未完成👃)You Don't Know JS: Scope & Closures (第5章: Scope & Closures)
Chapter 5: Scope Closure 我们到达这里时,已经对作用域如何工作有了非常健康稳固的理解. 下面,我们转移注意力到一个及其重要,但长期难以理解,几乎是神话中的部分语言:Closur ...
- [Android]使用Dagger 2依赖注入 - 自定义Scope(翻译)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5095426.html 使用Dagger 2依赖注入 - 自定义 ...
- 网页3D引擎“Babylon.JS”入门教程翻译总结
使用三个月的业余时间把官方教程的入门部分译为中文并上传到github,在下一步编程前做一个总结. 历程: 最早接触游戏编程是在大三下学期,用汇编语言和实验室里的单片机.触摸屏.电机(提供声效)编的打地 ...
- js的closures(闭包)
JS中的闭包(closure) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面就是我的学习笔记,对于Javascript初学者应该是很有用 ...
随机推荐
- DataList删除操作
<asp:DataList ID="fileList" runat="server" RepeatColumns="1" Repeat ...
- Oracle 正则表达式使用示例
正则表达式的基本例子 在使用这个新功能之前,您需要了解一些元字符的含义.句号 (.) 匹配一个正规表达式中的任意字符(除了换行符).例如,正规表达式 a.b 匹配的字符串中首先包含字母 a,接着是其它 ...
- post上传文件
- (BOOL)sendPhotoToTumblr:(NSString *)photo withCaption:(NSString *)caption; { //get image d ...
- 手动建库时一个小错误:ORA-32004: obsolete or deprecated parameter(s) specified for RDBMS instance
此前执行了CREATE SPFILE FROM MEMORY. 重新使用SPFILE启动时,出错如下: SYS@ bys3>startup ORA-32004: obsolete or dep ...
- 利用CNN进行人脸年龄预测
很久之前做的东西了,最近做了一个人脸相似度检测,里面用到了这里的一个模型,所以抽个空把人脸年龄检测的思路总结一下. 与其他CNN分类问题类似,人脸年龄预测无非就是将人脸分为多个类别,然后训练卷积神经网 ...
- python之excel自动报表
一.自动报表前期工作: 需要安装XlsxWriter模块,可以从github上下载以后解压.setup.py install. 二.程序: #!/usr/bin/env python # -*- co ...
- [已解决] C3p0连接配置
#用户名 c3p0.user=test c3p0.user=root # 用户密码--> c3p0.password=test c3p0.password=root c3p0.driverCla ...
- MOCK DATA -- node路由
前后端分离,有时候后端接口给的不是很及时,这就需要前端自己mock data, 本文讲的简单的node模拟数据 api路由跳转 首先有个data.js(json)文件, 路由: 配置在dev-serv ...
- tcpdump高级过滤技巧
基本语法 ========过滤主机--------- 抓取所有经过 eth1,目的或源地址是 192.168.1.1 的网络数据# tcpdump -i eth1 host 192.168.1.1- ...
- 启动tomcat时,报错:IOException while loading persisted sessions: java.io.EOFException解决方法
报错原因:加载持久化session错误,tomcat加载时读取的文件是是*.ser,session序列化文件,文件的位置是tomcat\work\Catalina\localhost,找到sessio ...