博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
读书笔记-你不知道的JS上-词法作用域
阅读量:4943 次
发布时间:2019-06-11

本文共 2577 字,大约阅读时间需要 8 分钟。

JS引擎

 

编译与执行

  Javascript引擎会在词法分析和代码生成阶段对运行性能进行优化,包含对冗余元素进行优化(例如对语句在不影响结果的情况下进行重新组合)。

  对于Javascript来说,大部分情况下编译发生在代码执行前的很短时间内,涉及的概念有引擎、编译器、作用域。

  变量声明例如var a = 2这条表达式,编译阶段会先查询作用域是否有同名变量,如果有就忽略声明(仅仅忽略var的声明),继续编译。如果没有,会在当前作用域的变量集合中创建一个变量,命名为a。

{        //编译阶段找不到a 执行var a        var a = 1;        //运行阶段执行 a = 1        console.log(a); //1        //编译阶段找到了变量a 忽略此条语句        var a = 2;        //运行阶段执行 a = 2        console.log(a); //2    }

 

LHS查询与RHS查询

  当变量被使用时,会进行查询操作,例如赋值操作a = 1是LHS查询,另外一种称为RHS查询。

  简单理解就是,对于赋值操作(包括隐性的)为LHS,作为变量进行引用时时RHS查询。

{        //赋值操作 LHS查询        var a = 1;        function fn(a) {            //进入函数体存在一个隐性的a = 2操作            //log使用RHS查询            console.log(a);        }        //函数调用发生RHS查询 2作为引用传入        fn(2);    }

 

作用域嵌套  

  一个块或函数嵌套在另一个块或函数中时,就会形成作用域嵌套,在当前作用域无法找到某个变量时,就会在外层作用域继续查找,直到找到该变量;或者没找到,抛出一个错误,查询停止。

 

 

词法作用域

 

  作用域有两个工作模型,一种是词法作用域,另一种叫动态作用域。

//全局作用域    //包含f1    function f1(a) {        var b = a * 2;        //f1的作用域        //包含a,b,f2,        function f2(c) {            //f2的作用域            //包含c            console.log(a, b, c); // 2,4,4        }        f2(b)    }    f1(2)

  变量查找会先从最内部的作用域开始查找,找不到会去上一级嵌套的作用域。

  无论函数在哪里被调用,也无论如何被调用,词法作用域都只由函数被声明时所处的位置决定。(闭包)

function outer() {        //找到后返回a        var a = 1;        //保留对outer作用域中a的引用        function inner() {            //被调用时开始查询变量a            return a;        }        return inner();    }    function f1() {        //outer作用域决定于声明处        //所以不会查找到f1的a        var a = 2;        //在这里调用词法作用域依然不会变        outer();    }    f1(); //1

  顺便讲一下闭包为什么会改变变量的生命周期,闭包需要两个条件。

  1、外部函数内部有另外一个函数,并对外部函数的某个变量保持了引用。

  2、外部函数提供一个内部函数的访问接口。

  而内部函数对外部作用域的引用保持就叫闭包。

  生命周期的问题还要提到GC回收问题,一般常见的回收算法有两种:引用计数算法、标记算法,由于引用计数会出现循环引用问题,这里以标记算法举例简单讲一下。标记算法会将代码构建成类似于DOM树之类的结构,根节点为window,第一轮先开始向下遍历,可以被根节点引用的变量上一个标记(假设为false)。遍历完后,第二轮开始清除操作,所有标记为true将会被垃圾回收。看不懂的话,还是看代码注释吧。

function fn1() {        var a = 1;        //闭包条件1        function fn2() {            a = a + 1;            return a;        }        //闭包条件2        return fn2;    }    var fn = fn1();    // a变量并没有被回收    // window.fn => fn2 => a 可以获取到a 保留    console.log(fn()); //2    console.log(fn()); //3    function fn3() {        var b = 1;        b++;        //此函数没用        function fn4() {            console.log(b);        }        return b;    }    //window.fn3 => ??? b无法被引用 消除    console.log(fn3()); //2    console.log(fn3()); //2 被回收了并再次初始化

 

   另外,可以通过eval和with语法强行修改作用域,但并不推荐使用,主要是性能方面的问题。因为引擎在编译时会对词法作用域进行分析优化,保证代码运行时能更快的找到对应的变量,但是如果有eval问题就不一样了,无法保证会传入什么代码,有可能会影响现有的作用域导致优化失败,所以引擎可能会根本不优化,

转载于:https://www.cnblogs.com/QH-Jimmy/p/6444185.html

你可能感兴趣的文章
转:哈夫曼树详解
查看>>
.Net Core Identity外面使用Cookie中间件
查看>>
C#中泛型之Dictionary
查看>>
使用Code First模式开发如何更新数据库(转载)
查看>>
Codeforces Round #376 (Div. 2)
查看>>
Codeforces 607D Power Tree 线段树 (看题解)
查看>>
写在人生的路上——2016年上半年总结
查看>>
C语言、C语言的起源以及类似C语言的编程语言的历史简直不要太漫长,我简单总结列表如下:...
查看>>
sp1.3-1.4 Neural Networks and Deep Learning
查看>>
JavaScript易错知识点整理
查看>>
Biological Clocks
查看>>
2018-10-11
查看>>
国内NLP的那些人那些会
查看>>
SQL 将一个表中的所有记录插入到一个临时表中
查看>>
nmea协议
查看>>
js 中对象的特性
查看>>
hdoj3714【三分】
查看>>
嵌入式开发入门(4)—驱动入门之时序图分析【20121211修改,未完】
查看>>
Python 使用字符串
查看>>
Quartz Core之CALayer
查看>>