zoukankan      html  css  js  c++  java
  • you don't know js -- Scope and Closures学习笔记——第二章(词法作用域)

    以下内容为自己看原版尝试做的翻译,仅当一个自己的看书记录,书中内容绝大部分都翻译了,但由于个人能力有限,建议各位看客不要迷信翻译的质量,推荐购买其英文原版学习观看。

    在第一章,我们定义了“作用域”是一组引擎可以通过标识符查找变量的规则,既可以在当前作用域,也可以在任何包含当前作用域的嵌套作用域中。

    这里有两个表明作用域如何工作的主要模型。第一个是词法作用域(lexical scope),第二个是动态作用域(dynamic scope)。

    Lex-time

    在第一章里面讨论过,第一个传统阶段标准的语言编译器叫做lexing(也叫tokenizing)。the
    lexing process examines a string of source code characters and assigns
    semantic meaning to the tokens as a result of some stateful parsing.

    这个概念,提供了理解词法作用域的基础,以及这个名字的来源。

    词法作用域是在词法分析阶段定义的作用域。换句话说,词法作用域是基于变量和作用域块在何处被authored,对你而言,是在写下代码的时候,因此(通常)在词法分析程序处理你的代码的时,不会改变。

    We will see in a little bit that there are some ways to cheat lexical
    scope, thereby modifying it after the lexer has passed by, but
    these are frowned upon. It is considered best practice to treat
    lexical scope as, in fact, lexical-only, and thus entirely authortime
    in nature.

    考虑如下的代码:

    function foo(a){
    	var b = a * 2;
    	function bar(c){
    		console.log(a, b, c);
    	}
    	bar(b * 3);
    }
    
    foo(2);	// 2, 4, 12
    

    在这个例子中,有3个嵌套的作用域。可以把它们想象成彼此嵌套的泡泡。

    第一个泡泡包含全局作用域,并且有一个叫foo的标识符。

    第二个泡泡包含foo的作用域,在里面有a,barb三个标识符。

    第三个泡泡包含bar的作用域,在里面只有一个叫c的标识符。

    作用域泡泡的嵌套规则在代码块写下的时候就已经确定了。

    bar的泡泡整个被包含在foo的泡泡中,只因为我们选择在foo里面定义了bar

    注意这些嵌套的泡泡都是严格的嵌套的。这些泡泡并不会跨越界限。换句话说,没有一个泡泡可以同时(部分)存在于两个外部的作用域泡泡中,正如没有一个函数可以在两个父级函数中各定义一部分。

    查找

    这些作用域泡泡的结构和相应的位置向引擎完整的解释了它在查找一个标识符时所需要的位置信息。

    在前面的代码段中,引擎执行console.log(..)语句,并且查找引用的a,b,c三个变量。他从最里面的作用域泡泡开始——bar(..)函数的作用域。在这里他没有找到a,因此会向上一层,达到下一个最近的作用域泡泡——foo(..)的作用域。在这里找到了a并使用了它。对b同理。但是对于c,已经在bar(..)函数里面找到了。

    已经有一个c同时在bar(..)内部和foo(..)内部,console.log(..)语句会找到并使用在bar(..)里面的c,而不会去使用foo(..)里面的。

    作用域查找会在遇到首次匹配时就停止同样的标识符名字可以在嵌套作用域的不同层中指定,这被称为shadowing(内部标识符shadows外部标识符)。;撇开shadowing,作用域查找总是在最内层作用域被执行时开始,一直到最外/最上,直到遇到首次匹配时,就停止。

    Global variables are automatically also properties of the global
    object (window in browsers, etc.), so it is possible to reference
    a global variable not directly by its lexical name, but instead
    indirectly as a property reference of the global object.
    window.a
    This technique gives access to a global variable that would
    otherwise be inaccessible due to it being shadowed. However,
    non-global shadowed variables cannot be accessed.

    不管一个函数何处被调用,或者甚至何时被调用,它的词法作用域仅仅与函数被声明的地方有关。

    词法作用域查找程序应用于first-class identifiers,比如a,b,c。如果你的代码中有一个对foo.bar.baz的引用,词法作用域查找会用于查找foo标识符,并且一旦它定位到了这个变量,对象的属性访问规则会接管来分别处理barbaz属性。

    Cheating Lexical

    。。。

    eval

    。。。

    with

    。。。

    Performance

    。。。

    复习

    词法作用域指作用域由author-time确定并取决于函数在何处被声明。编译的词法阶段本质上可以知道所有变量在何处以及怎么被声明,因此可以预测他们在执行时会如何被找到。

    JavaScript中的eval(..)with可以“欺骗”作用域。前者可以通过evaluating一个含有1个或多声明的字符串“代码”来修改已有的词法作用域(在运行时)。后者通过将一个对象引用处理成一个作用域,以及将对象的属性处理成作用域里的标识符,其本质上创建了一个新的词法作用域(同样也是在运行时)

    这些机制的缺点是使得引擎执行编译时对作用域的查找优化能力失效,因为引擎不得不悲观的假设这些优化将会无效。带来的结果是,使用上述任何一种机制时代码会变得较慢。不要使用它们。

  • 相关阅读:
    Docs-.NET-C#-指南-语言参考-预处理器指令:#error(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#warning(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#undef(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#define(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#endif(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#elif(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#else(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#if 预处理指令
    Docs-.NET-C#-指南-语言参考-预处理器指令:C# 预处理器指令
    1212 最大公约数
  • 原文地址:https://www.cnblogs.com/terrible/p/4438231.html
Copyright © 2011-2022 走看看