zoukankan      html  css  js  c++  java
  • 【Js闭包】由一道面试题简单扩展

    在一个前端公众号,看到这么一个号称简单的面试题:

    1、以下程序输出什么?

    <script type="text/javascript">
        function init(){
            for (var i = 0; i < 10; ++i) {
                setTimeout(function () { 
                 console.log(i);
                }, 0);
            }
             
        }
        window.onload=init;
    </script>

    2、若需要输出0123456789,应该怎么修改?

    结果,输出的为10101010101010101010

    若要输出0123456789,则可以将代码改成

    <script type="text/javascript">
        function init2(){
            for (var i = 0; i < 10; ++i) {
                setTimeout(
                    (function(i){
                        return function(){
                            console.log(i)
                        }
                    })(i), 0);
            }
        }
        window.onload=init2;
    </script>

    解释:

    1、for循环每次注册一个延迟函数, setTimeout是异步的,传入事件队列中,在循环结束后进行处理,当循环结束时,i为10。

    setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 因此 i 变化时, 也影响到了匿名function.

    2、for循环执行时,给点击事件绑定的匿名函数传递i后,立即执行返回一个内部的匿名函数,因为参数是按值传递的,

         所以此时形参保存的就是当前i的值,内部的匿名函数一直保存着当前i的值。 返回的匿名函数执行弹出各自保存的 i 的引用的值。

    看到解释到这里,我是一脸懵逼,关键在于闭包的参数传递,所以我自己对闭包的知识进行了一下下扩展。

    0、前言:Js变量的作用域

         变量的作用域包括:全局变量、局部变量

         Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

       var n=999;
      function f1(){
        alert(n);
      }
      f1(); // 999

        在函数外部无法读取函数内部的局部变量

      function f1(){
        var n=999;
      }
      alert(n); // error

        注意:若函数内部声明变量的时候,一定要用var,否则实际上声明了全局变量,

        会造成其他函数误用变量、全局对象过于庞大,影响访问速度等不良影响

     function f1(){
        n=999;
      }
      f1();
      alert(n); // 999

    Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立

    1、闭包的解释

      闭包是指有权访问另一个函数作用域变量的函数,创建闭包的通常方式,是在一个函数内部创建另一个函数。

        闭包的本质是一个函数,将函数内部和外部连接起来的桥梁。

    2、闭包的写法

          好像,还是,有那么点抽象,那就网上找个例子:

    function func1(){
            var age = 22;
            function func2(){
                return age;
            }
            return func2;
        }
        var result = func1();      //得到的是函数func2这一整个函数
        var _num = result();       //得到的是func2的执行结果也就是age的值
        console.log(result);       //function func2(){ return age; }
        console.log(_num);         //22

           结果分析:

           a)首先要明白一个函数名称加不加括号的区别,比如说func1和func1(),func1表示的是这个函数本身,func1()表示的是这个名叫func1的函数的执行结果也就是它的返回值。

           b) 其次我们来分析闭包的原理,因为func2是函数func1的子函数,所以在func2中可以访问到变量age,并将这个值作为函数func2的返回值返回,最后将整个func2函数作为func1函数的返回值。

           c)最后我们来分析一下result和_num的值,result是函数func1的执行结果,也就是func1的返回值func2函数本身,_num的值为func2的执行结果也就是变量age。

           d)至此,我们实际上已经将局部变量age的值获取到了并存在变量_num中,实现了在函数外部访问局部变量的值的问题。

    3、闭包的用途

        a) 匿名自执行函数

      创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。

             ① 匿名函数:顾名思义,就是没有方法名的函数

             ② 匿名函数调用方式:

                 使用()将匿名函数括起来,然后后面再加一对小括号(包含参数列表)  

           (function(a,b)
            {
               console.log('匿名函数加圆括号:'+(a+b));
            }(1,2));

                将匿名函数赋值给一个变量,通过变量引用进行函数调用

           var noName= function(a,b){

    console.log('匿名函数赋值变量:'+(a+b));

    }(1,4);

        b) 缓存(未整理验证)

    再来看一个例子,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,
    那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,
    然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,
    从而函数内部的值可以得以保留。

     

       c)实现封装

           可以先来看一个关于封装的例子,在person之外的地方无法访问其内部的变量,而通过提供闭包的形式来访问

       d)面向对象编程

          实例独立访问成员变量,互不影响

  • 相关阅读:
    sqli-labs Less29-Less31
    sqli-labs Less23-Less28a
    sqli-labs Less20-Less22
    sqli-labs Less17--Less19
    sqli-labs Less13-Less16
    为什么选择centos,而不是Dibian、Ubuntu【转】
    sublime 安装常用插件
    Linux常用命令
    在UEFI下安装windows和Ubuntu双系统目前不可行
    nginx 环境搭建(基于linux)
  • 原文地址:https://www.cnblogs.com/alwaysblog/p/6498331.html
Copyright © 2011-2022 走看看