作用域与闭包


作用域

  • 什么是作用域

    作用域就是一套规则,它负责解决(1)将变量存在哪儿?(2)如何找到变量?的问题

  • 作用域工作的前提

    • 谁赋予了作用域的权利?——js引擎
    • 传统编译语言编译的过程
      • 分词/词法分析:字符串 =》 词法单元
      var a=2; => var a = 2 ;(共5个单元)
      • 解析/语法分析:词法单元流 =》 抽象语法树(Abstract syntax tree,AST)
      • 代码的生成: AST =》 可执行代码(机器指令)
    • js引擎编译的特点:
      • 代码在执行前进行编译(需要用到JIT(just in time)进行延迟编译甚至实施重编译来保证性能最佳)
      • 针对 ** var a = 2; **例子的编译流程
        • 分词/词法分析:将var a=2; => var a = 2 ; 分成5个词法单元
        • 解析/语法分析:解析成抽象语法树
        • 代码生成:** 重点来了 **
          • var a (声明操作)=》此时编译器会 询问作用域是否有叫 a 的变量存在 ?忽略该声明,继续编译 : 在当前作用域下声明一个新变量,命名为a
          • a=2 (赋值操作) =》此时编译器会 询问作用域是否有叫 a 的变量存在 ?使用该变量 : 引擎根据作用域链继续向上查找该变量。

            是否能找到叫 a 的变量 ?直接赋值 : 引擎抛出异常。
        • 引擎在查找变量a是否被声明的过程中,是如何进行的查找?
          • 作用域的协助
          • 查找的目的是赋值=》LHS(left hand side)查询 :(1) 当变量出现在赋值操作的左侧时,使用 LHS 查询。类似赋值 a=2; (2)若在顶层作用域内没有找到,严格模式下:Reference异常;非严格模式下:自动隐式创建全局变量
          • 查找的目的是取值=》RHS(right hand side)查询 :(1) 当变量出现在赋值操作的右侧时,使用 RHS 查询。类似取值 a; (2) 若在顶层作用域内没有找到,直接抛出Reference异常
  • 作用域是如何工作的?词法作用域,动态作用域。

    • 词法作用域

      • 什么是词法作用域

        • 词法作用域就是定义在词法分析阶段的作用域。词法作用域就是在写代码时将变量和块作用域写在哪里决定的。一般是不会变的。
      • 词法作用域的查找规则(作用域链
        • 作用域查找从运行时所处的最内部的作用域开始,逐级向上或向外查找,直到遇见第一个匹配的标识符(变量、函数)为止。
      • 改变词法作用域(二般情况出现:欺骗词法
        • 2种方法

          • eval();接受一个字符串为参数,即动态插入程序代码,伪装成词法期就存在的代码。
          function foo(str, a){
          eval(str);
          console.log(a, b);
          }
          var b=2;
          foo('var b=3;',1); //1,3
          • with;通常被当做重复引用同一个对象的多个属性的快捷方式,可以不需要重复引用对象本身。
          var obj = {
          a:1,
          b:2,
          c:3
          };
          //使用with
          with(obj){
          a=3;
          b=4;
          c=5;
          }
        • 后果
          • 引擎无法在编译时对作用域对的查找进行优化
          • 在严格模式下,with被完全禁止,eval(...)也被禁止
  • 常见的作用域单元

    • 函数作用域

      • 什么是函数作用域

        • 属于这个函数的全部变量都可以在整个函数的范围内使用及复用
      • 函数的好处:隐藏内部实现,规避同名标志符之间的冲突(有以下两个方法)
        • 声明全局命名空间(一个对象,用来存储全局作用域种的变量)
      • 函数相关常识
        • 函数声明 vs 函数表达式
        function是声明中的第一个词 ? 函数声明 : 函数表达式。
        • 具名 vs 匿名 :
        有无名字的区别
        函数表达式可以没有名字,但是函数声明必须有名字
        鼓励所有的函数都有名字
        • 立即执行函数表达式
        两种写法:
        (function(){}());
        (function(){})();//常用,第一个括号( )将函数变成表达式,第二个( )执行这个函数
    • 块作用域(es5之前并没有该概念)
      • 什么是块作用域
      变量的声明离使用的地方越近越好,并最大程度的本地化。
      • 块作用域的例子

        • with关键字 : with从对象中创建出的作用域仅在with声明中而非外部作用域有效
        • try/catch : catch分句创建作用域,且声明的变量只能在catch中使用
        • let : let为其声明的变量提供块作用域{...},且let进行的声明在块中不会变量提升
        • const : const声明常量。
  • 提升

    • 声明提升

      • 变量声明提升
      • 函数声明提升
      • 函数表达式声明不会被提升
      foo();//Uncaught TypeError: foo is not a function//foo()对于undefined值进行函数调用而导致非法操作
      var foo= function bar(){
      console.log('1');
      };
      foo();//1
      foo;//function bar(){}
      bar;//ReferenceError: bar is not defined
      bar();//ReferenceError: bar is not defined
    • 提升的优先级
      • 函数优先,其次才是变量
      • 避免在块内声明函数

  • 练习题(1)找出所有的 LHS 查询和 RHS 查询
function foo(a) {
var b=a;
return a+b;
}
var c= foo(2);

答案:

LHS(3处) c=..., a=2(隐式变量分配),b=...,

RHS(4处) foo(2..., =a, a.., ..b


作用域闭包(晦涩难懂,常常搞错的地方)

  • 什么是闭包?

    • 函数可以记住并访问所在的词法作用域时,就产生了闭包。即使是在当前词法作用域之外的地方执行。
    闭包     = 那些能够访问自由变量的函数 = 函数 + 函数能访问的自由变量
    自由变量 = 在函数中使用,但既不是函数参数也不是函数的局部变量
  • 闭包作用
    • 可以读取函数内部的变量
    • 让这些变量的值始终保持在内存中。
  • 闭包例子
    • 回调函数(定时器,ajax跨域,异步等)
    • for循环
    for(var i=1;i<=5;i++){
    setTimeout( function timer(){
    console.log(i);
    },i*1000);
    } //以每秒一次的频率输出5个6 for(var i=1;i<=5;i++){
    (function(j){
    setTimeout( function timer(){
    console.log(j);
    },j*1000);
    })(i);
    } //立即执行函数为每一次迭代生成一个新的作用域,以每秒一次依次输出1-5 for(let i=1;i<=5;i++){
    setTimeout(function timer(){
    console.log(i)
    },i*1000);
    }

动态作用域

  • 什么是动态作用域

    动态作用域是在函数运行时确定的,类似this。

    词法作用域关注在何处声明,动态作用域关注在何处调用。

最新文章

  1. Glassfish在SpringMVC服务端接收请求时字符出现乱码的解决办法
  2. Mac下安装UPnP Inspector
  3. [php-src]理解Php内核中的函数与INI
  4. is running beyond physical memory limits. Current usage: 2.0 GB of 2 GB physical memory used; 2.6 GB of 40 GB virtual memory used
  5. 导入maven项目后无法找到sun tools toos-15.0.jar
  6. tcp服务的测试程序开源
  7. 阿里巴巴fastJson进行json数据解析
  8. km算法的个人理解
  9. 中级Perl 第三章课后习题
  10. tomcat安全配置之禁用Directory Listing
  11. [asp.net mvc 奇淫巧技] 03 - 枚举特性扩展解决枚举命名问题和支持HtmlHelper
  12. logback使用配置详解
  13. exit、_exit、abort、return的区别
  14. Regex 正则替换指定范围全部字符串
  15. 难以理解的AQS(上)
  16. encode与decode
  17. mongodb学习(入门。。。。。)
  18. AI - 参考消息(References)
  19. LeetCode算法题-Two Sum II - Input array is sorted
  20. java中的强,软,弱,虚引用

热门文章

  1. Scrapy定制命令开启爬虫
  2. Java入门 - 高级教程 - 06.邮件收发
  3. 微软的github 上面 有 Docker.DotNet 嗯 作为 菜 只有欣赏的额
  4. kafka概念扫盲
  5. docker-Gitlab、GitLab Runner安装
  6. Static、Final、static final
  7. linux---&gt;ab测试工具使用
  8. windows丢失文件的恢复技巧
  9. Docker基础内容之容器
  10. C++基类和派生类的构造函数