资讯专栏INFORMATION COLUMN

You-Dont-Know-JS - 作用域

pingan8787 / 2139人阅读

摘要:编译器对赋值的操作当编译器遇到它会询问域是否存在变量,如果存在编译器忽略这个声明,如果不存在编译器要求域声明一个新变量给自己。两种引用引用和引用,对于编译器来说,指变量用于赋值,是指变量用于取值。里面的也是引用,因为它用于获取的值。

You-Dont-Know-JS是github上一个拥有9000多枚星星的JS教学文档,评价很高,为了避免和其他翻译文一样,容易陷入不宜读的混乱,也试图避免原文中过多数的术语导致我露怯,我只提取提取我理解的一些点,通俗的讲出来。今天先从第一章作用域开始吧:

原文
我的翻译folk,希望有识之士能跟我共同完成

JS的编译

js和传统语言一样,也需要编译执行,编译的过程通常分三步:

标记+词法分析 Tokenizing/Lexing:: 把程序语言切成一段一段的片段,称之为tokens。比如,程序中的 var a = 2;,会被切成如下tokens:var, a, =, 2, ;。

解析 Parsing: 把token流转换成树,是嵌套元素形式,符合语法结构和规则,这个树被称之为"AST" (Abstract Syntax Tree).
var a = 2;的树,开始于顶层节点 VariableDeclaration, 包含子节点 Identifier (值 a), 另外一个子节点 AssignmentExpression 包含另外子节点 NumericLiteral (值 2).

代码生成 Code-Generation: 这个过程是把AST转换成可执行代码, 这部分各程序语言和个平台之间会有极大的不同。 所以,刨除细节,var a = 2;会被转换成机器语言,在内存中创建一个变量 a,而后把值储存其中。

编译器对赋值的操作

当编译器遇到var a,它会询问域是否存在变量a,如果存在编译器忽略这个声明,如果不存在编译器要求域声明一个新变量a给自己。

编译器而后为引擎产生可执行代码,处理a = 2, 引擎首先询问域是否有这个变量,如果没有执行其他操作。

两种引用

LHS(Left-hand Side)引用和 RHS(Right-hand Side)引用,对于编译器来说,LHS指变量用于赋值,RHS是指变量用于取值。

function foo(a) {
    console.log( a ); // 2
}

foo( 2 );

这里,foo(), 引用(变量)foo是RHS引用,因为它是用于取值的操作(将函数foo的值取出来),参数a被隐含赋值2,a = 2这里的引用a是LHS引用,因为它用于赋值。console.log(a)里面的a也是LHS引用,因为它用于获取a的值。

这里说明的是, 代码里的变量, 对于编译器来说有两种类型,用于获取它值的是一种,本身是用于被赋值的是另外一种。

嵌套域 (Nested Scope)

嵌套域很好理解,如下代码

function foo(a) {
    console.log( a + b );
}

var b = 2;

foo( 2 ); // 4

foo函数域中没有变量b,当引用b被调用的时候,程序会逐级往上查找,直到找到变量b,最顶层为全局变量 (global scope),如下图所示:

错误

但是各位有没有想过一个问题,被赋值的引用(LHS)如果本域内不存在会产生什么情况?当然,大多数人都经历过ReferenceError,引用错误。

当LHS引用(赋值引用)在域中找不到的时候,js引擎会直接抛出错误,而RHS引用(调用引用)如果在域中找不到的时候,js引擎会向上一级域中查找,如果依然没有,会直接在全局域中为你自动创建一个(非严格模式下, 严格模式下也会报ReferenceError错误)。

考虑从如下代码:

function foo(a) {
    console.log( a + b );
}

foo( 2 );

这里的变量b因为在函数域中未定义,所以会报错

function foo(a) {
    b = a;
}

foo( 2 );

这里的变量b也未定义,但是由于是复制操作,系统会为你在外层全局域里自动创建一个。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91466.html

相关文章

  • You-Dont-Know-JS - 词法作用

    摘要:原文原文原文词法作用域作用域有两种常见的模型,一种叫做词法作用域,一种叫做动态作用域。其中词法作用域更常见,被大多数语言采用,包括。值得注意的是,一个函数作用域只有可能存在于一个父级作用域中,不会同时存在两个父级作用域。 原文: 原文1 | 原文2 Lexical Scope - 词法作用域 作用域有两种常见的模型,一种叫做 词法作用域 Lexical Scope,一种叫做...

    bang590 评论0 收藏0
  • 闭包,又见闭包。。。。?

    摘要:完美的闭包,对,闭包就这么简单。这仅仅是闭包的一部分,闭包利用函数作用域达到了访问外层变量的目的。此时一个完整的闭包实现了,的垃圾回收机制由于闭包的存在无法销毁变量。 1.闭包是指有权访问另一个函数作用域中的变量的函数。 上面这段话来自 javascript 高级程序设计 第三版 P178 。作者说闭包是一个函数,它有访问另一个函数作用域中的变量的能力。 2.函数访问它被创建时所处的...

    keelii 评论0 收藏0
  • 前端_JavaScript

    摘要:为此决定自研一个富文本编辑器。例如当要转化的对象有环存在时子节点属性赋值了父节点的引用,为了关于函数式编程的思考作者李英杰,美团金融前端团队成员。只有正确使用作用域,才能使用优秀的设计模式,帮助你规避副作用。 JavaScript 专题之惰性函数 JavaScript 专题系列第十五篇,讲解惰性函数 需求 我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意...

    Benedict Evans 评论0 收藏0
  • js中的this学习

    摘要:考虑这个问题还需要了解一个概念调用栈到达当前执行位置而被调用的所有方法的堆栈。之后只要调用函数,就会调用函数,绑定的值始终不变。就是中新增的函数箭头函数。 在之前的对象原型的文章中,我们讲到了在函数前面加new然后进行调用之后发生的4件事情,当时只把跟原型有关的东西介绍了一下。现在我们来学习一下其他的内容。 首先先来回顾一下会有哪四件事情发生吧: 一个全新的对象被创建 这个新的对象会...

    roadtogeek 评论0 收藏0
  • 【理解】一道 JS 面试题

    摘要:最近在一个前端学习群里,有人抛出了这么一道面试题。以下表示形式的是函数表达式,有多种形式。函数声明式的函数名是可修改的。重新声明变量通过上面的分析解释,希望你可以掌握这道面试题,举一反三。原文链接理解一道面试题 最近在一个前端学习群里,有人抛出了这么一道 JS 面试题。 var foo = 1; (function foo(){ foo = 100; console.l...

    binaryTree 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<