zoukankan      html  css  js  c++  java
  • 二、词法作用域 (学习笔记)—— 《你不知道的JavaScript》


    词法作用域

    作用域工作模型:

    • 词法作用域(大多数编程语言采用)
    • 动态作用域

    词法阶段

    大部分标准语言编译器的第一个工作阶段就是词法化。

    词法化的过程:会对源代码中的代码进行检查,如果是有状态的解析过程,还会赋予单词语义。

    词法作用域:就是定义在词法阶段的作用域。在写代码时,将变量和块作用域写在哪里决定的。

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

    上面demo有3个逐级嵌套的作用域。便于理解,可以看成是逐级嵌套的气泡。

    ① 包含整个全局作用域,其中只有一个标识符:foo
    ② 包含 foo 创建的作用域,其中有三个标识符:a b bar
    ③ 包含 bar 创建的作用域,其中只有一个标识符:c

    作用域气泡由其对应的作用域块代码写在哪里决定,是逐级包含的关系。

    查找

    作用域气泡的结构和互相之间的关系给引擎提供了足够的位置信息,引擎通过这些信息来查找标识符的位置。

    在上一个代码片段中, 引擎执行 console.log(..) 声明, 并查找 a、 b 和 c 三个变量的引
    用。可参考下图。

    作用域查找,会在找到第一个匹配的标识符时终止。

    遮蔽效应:在多层嵌套的作用域中定义多个同名的标识符。(内部标识符会“遮蔽”外部标识符)

    抛开遮蔽效应,作用域查找,始终从运行时所处的最内部作用域开始查找,逐级向上进行,直到找到第一个匹配的标识符。

    被同名变量遮蔽的全局变量可以通过 window.a 来访问。

    词法作用域只会查找一级标识符。如果引用 foo.bar.baz,只会查找 foo, 找到这个变量,对象属性访问规则会接管对 bar baz 属性的访问。

    欺骗词法

    词法作用域完全由写代码时函数定义的位置来定义,如何在运行时修改(欺骗)?

    有两种机制:

    eval

    还是先来看个栗子吧!

    function foo(str, a) {
        eval(str); // 欺骗引擎 var b = 3; 原本就在这里
        console.log(a, b);
    }
    
    var b = 2;
    foo('var b = 3', 1); // 1 3
    

    可以看到,通过 eval(str),将原本不在 foo 中的的 var b = 3; 欺骗成书写时就代码就在那了,以此修改了词法作用域。

    在严格模式中,eval()在运行时有自己的词法作用域,其中的声明无法修改所在的作用域。

    // eval 严格模式
    function foo(str) {
        'use strict';
        eval(str);
        console.log(a);
    }
    foo('var a = 3'); // Uncaught ReferenceError: a is not defined
    

    with

    先来一个 demo

    function foo(obj) {
    	with(obj) {
    		a = 2;
    	}
    }
    
    var obj1 = { // obj1 有 a 属性
    	a: 1
    }
    
    foo(obj1);
    console.log(obj1.a); // 2
    
    var obj2 = { // obj2 没有 a 属性
    	b: 1
    }
    
    foo(obj2);
    console.log(obj2.a); // undefined
    console.log(a); // 2,a 被挂在到全局作用域
    

    with 声明会根据传入的对象凭空创建一个全新的词法作用域。

    传入 obj2 时,为什么 a 被挂在到全局作用域,可以按下图来理解:

    注意:使用 eval() 和 with() 会有性能问题。

    性能

    性能肯定是不好的,可参考下面图片,具体的文字解说可参考《你不知道的JavaScript上卷》

    注:以上所有的文字、代码都是本人一个字一个字敲上去的,图片也是一张一张画出来的,转载请注明出处,谢谢!

  • 相关阅读:
    多任务GUI窗口系统(类window,有源码,支持汉字显示、顶层、非矩形和透明窗口)gicell源码
    怎样判断treeview当前节点为treeview显示出来的第一个节点和最后一个节点?
    用 dbgrid 或 dbgrideh 如何让所显示数据自动滚动
    vue路由懒加载及组件懒加载
    vue 中使用rem布局
    纯css实现移动端横向滑动列表&&overflow:atuo;隐藏滚动条
    分布式一致性算法Raft
    HDU_1753 大明A+B
    POJ——3630 Phone List
    HDU_2647 Reward (拓扑排序)
  • 原文地址:https://www.cnblogs.com/lwl0812/p/9792162.html
Copyright © 2011-2022 走看看