我不知道的js(一)做用域与闭包

做用域与闭包


做用域

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

  • 做用域工做的前提
    • 谁赋予了做用域的权利?——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。 词法做用域关注在何处声明,动态做用域关注在何处调用。 ***
相关文章
相关标签/搜索