• JavaScript-作用域 和 闭包


    • 作用域定义和作用

          作用域定义:JavaScript中存储变量和查找变量的一套规则。

           JavaScript中一般遵循词法作用域。    

          变量查找遵循从内到外查找:  

          引擎从当前的执行作用域开始查找变量,如果找不到就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是找不到,查找过程都会停止。

    • 词法作用域

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

    •  作用域的产生

              JavaScript中没有块状作用域的概念。那么什么是块状作用域呢 简单说就是{..}括起来的区域就是一个最小单元的块。

     我们可以简单 回顾一下c#或者Java语言中变量的作用域和申明周期 一般情况下是这样的

         C#或者java中变量的作用域:

        作用域从变量定义的位置开始,到该变量所在的那对大括号结束;

    生命周期:

        变量从定义的位置开始就在内存中活了;

        变量到达它所在的作用域的时候就在内存中消失了;

     

    举一个最简单的例子 for循环 

    C#中 for中定义的i 变量在 循环外访问不到

     

     

              JavaScript 中在for循环外 可以正常访问

     

     

     

       那么JavaScript 在什么时候创建作用域呢

    1.   全局作用域  

               这个无需做过多的解释,相信大家都理解

              2. 函数

               javaScript中每创建一个函数都会为其自身创建一个作用域气泡。属于这个函数的所有变量可以在整个函数内部使用及复用

    JavaScript 的作用域是基于函数的吧,变量的作用域为:

          使用关键字var声明一个变量,那么这个变量就属于当前函数的作用域,如果声明是发生在任何函数外的顶层声明,那么这个变量则属于全局作用域。

          没有使用var 关键字声明的变量会默认添加到全局作用域中,要尽量避免这样写。

    • 块状作用域 

          上面说了js中没有块状作用域,但是凡事都有例外,js中有几种情况可以提供块状作用域的效果

    1: Try/catch   

    Catch 分句会创建一个块状作用域,其中声明的变量仅在catch内部有效  

            <script type="text/javascript">
                try{
                    undefine();
                }catch(err)
                {
                    console.log(err);   //可以访问
                    var a=10;
                    console.log(a);
                }
                console.log("a"+a);  //可以访问  
                console.log(err);   //不可访问
            </script>

       

     

        以上还是要补充说明一下: 这个块状作用域是指 catch()后的这个括号的什么的变量。 在catch(){}花括号里面申明的变量不属于块状作用域。

      2: Let

    Let 可以将变量的作用域绑定在所在的 {…}块状作用域中

    Var foo=true;

    If(foo){

      Let bar=foo*2;

      Console.log(bar);  //正常访问

    }

    Console.log(bar); // ReferenceError

    l  使用 let 进行的声明不会再块状作用域中进行提升。

    {

      Console.log(bar);// ReferenceError

     Let bar=2;

    }

                  3:Const

    Const 也会创建块状作用域,但是它的值是固定的,之后任何试图修改的操作都会引起错误。

    4: 立即执行函数

     

    <script type="text/javascript">
    
        {
            (function(){
                var j=5;  //立即执行函数定义的变量属于作用快
              console.log(j);
            })();
            
            console.log(j); //这里访问不到
        }
        </script>
        

     

     

    • 作用域提升

                 定义:  无论var出现在一个作用域中的哪个位置,这个声明都属于整个作用域,在其中到处都可以访问的,这一行为被比喻为提升。

          特点:函数声明和变量声明会提升。而赋值或其他运行逻辑会留在原地。当函数声明和变量声明同时出现时,函数会首先被提升,然后才是变量。

                           函数表达式不会被提升

      作用域提升会被提升到所在块状作用域的的顶部,即所在{…}的顶部 

    <script type="text/javascript">
        foo(); //访问不到
      //  var a=true;
                {
                   foo(); //可以访问得到
                   function foo(){
                       console.log("a");
                   }
               }
           </script> 
    • 作用域闭包  

         前提背景:函数一般在执行后,整个函数内部作用域就会被销毁。

         定义: 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域以外执行,闭包可以使函数继续访问定义时的词法作用域。

         产生的场景:  函数作为返回值赋值给一个变量。(回调函数)

    示例一:

    <script type="text/javascript">
        function foo() {
            var a = 2;
    
            function bar() {
                console.log(a);
            }
            return bar;
        }
    
        var baz = foo();
        baz(); // foo()作用域并美誉在执行后被销毁,a还可以被正常访问,这就是闭包的效果
    </script>

     示例二:

        <script type="text/javascript">
            function foo()
            {
                var a=2;
                function baz()
                {
                  console.log(a);
                }
                bar(baz);
            }
            
            function bar(fn){
                fn();   //这就是闭包的效果
            }
            foo();
            
        </script>

         无论通过何种手段将内部函数传递到所在词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行整个函数都会使用闭包。

     常见闭包场景

    场景一:这个示例比较简单

    <script type="text/javascript">
          function wait(message){
               setTimeout(function timer(){
                   console.log(message);
               },1000);
          }
            
            wait("Hello,closure!"); // timer();函数被添加到任务队列中。 函数保留了词法作用域对wait();函数作用域的引用,所有可以访问到message变量
        </script>

    场景二:在循环中 很多同学都会有点迷惑

    <script type="text/javascript">
        for(var i=1;i<=5;i++)
        {
            setTimeout(function timer(){
                console.log(i);
            },i*1000);
        }
        </script>

    上面的代码会输出什么,正常情况下我我们对这段代码的期待是分别输出数组1~5,每秒一个,每次一个。但是实际上,这段代码在运行时会以每秒一次的频率输出5次6;

        

     为什么会这样呢? timer()函数会在for循环结束后执行,fou循环结束的条件是i<=5;即i=6;  timer();保留了for循环的作用域引用,此时i=6; 所有会输出5个6;

    如果要实现我们的预期效果就需要为每一次的循环创建作用域闭包(块状作用域)

    方式一:let

        <script type="text/javascript">
        for(var i=1;i<=5;i++)
        {
            let j=i;  //使用let 创建块状作用域
            setTimeout(function(){
                console.log(j);
            },i*1000);
        }
        或者
          for(let a=5;a>=1;a--)  // //使用let 创建块状作用域
        {
            
            setTimeout(function(){
                console.log(a);
            },a*1000);
        }
        
        
        </script>

    方式二:立即执行函数

      for(var i=1;i<=5;i++)
        {
            (function(){
                var j=i;
            setTimeout(function(){
                console.log(j);
            },j*1000);
            })();
        }
        </script>
    • 立即执行函数

    产生和意义:

    在任意代码判断外部包装一个函数,可以达到封装的的目的,函数内部的变量和函数定义不会被外部作用域访问。但是必须声明一个具名函数,这个函数的名称也会污染所在的作用域。而且必须要显示的调用这个函数才能运行其中的代码。

    如果函数不需要指定函数名称,并且能够自动执行。这时候立即执行函数就诞生了

    语法:把函数包含在()中可以形成还是表达式,通过在未尾加上() 可以立即执行这个函数。(function fn(){})();

    • 函数表达式

      定义:函数表达式区别于函数声明,也是一种定义函数的方式,形似与变量赋值,这个值就是函数体,例

      var a = function(){}; // 函数表达式之匿名函数

      var a = function fn(){}; // 函数表达式之具名函数

      (function(){})(); // 匿名函数之立即执行函数

      特点:

      1 . 区别于函数声明,和普通变量一样使用前必须声明,不声明在非严格模式下被认为是全局的变量,在严格模式下报错

     

  • 相关阅读:
    如何在Nginx下配置PHP程序环境
    PHPStorm2016如何安装主题
    Yii2初级入门教程
    递归递归!!
    PHP值传递和引用传递的区别
    网站移植到linux上后常犯的错误
    PHP之function_handling 函数
    PHP如何使用免费在线客服插件
    PHP快速入门 常用配置
    PHP快速入门 如何操作MySQL
  • 原文地址:https://www.cnblogs.com/cuner/p/12453565.html
Copyright © 2020-2023  润新知