闭包

作用域链

要学习闭包,首先要了解作用域链

作用域链:环境对象中定义的变量,会放到作用域中,形成一个链式结构。

定义三个变量 a b c 使其在不同的作用域

var a = 10;
        function F() {
            var b = 20;
            //内部函数:函数内部定义的函数
            function N() {
                var c = 30;
                return a + b + c;
            }
            return N();
        }
        

        //验证N中是否都能使用所有的链上变量
        console.log(F());//60

在变量 c 的作用域中,可以调用到 a b 在 b 的作用域中,可以调用到 a
但是反向就不能实现,a 的作用域中无法调用 b c ,b的作用域中 无法调用 c

如果我们想在变量作用域中访问其子作用域中定义的变量,要怎样实现呢?闭包的使用能解决这个问题


利用闭包突破作用域链

首先来看闭包的概念

闭包_第1张图片
闭包的概念、优点、缺点.png

闭包的原理就是在被访问的变量a的作用域中创建一个函数,将需要访问的变量当做新创建的函数返回值,再将函数定义给最外层的变量b,就能通过执行变量b来访问到变量a

原理如图:

闭包_第2张图片
利用闭包突破作用域链.png

代码如下:

//第一种写法
var a = 10;
        function F() {
            var b = 20;
            function N(){
                return b
            }
            return N;
        }
        var M = F();
        console.log(M());

        // 原理一样,写法不一样,第二种写法
        var inner;
        var a = 10;
        function F() {
            var b = 20;
            function N(){
                return b
            }
            inner = N;
        }
        F();
        console.log(inner());

不过需要注意:

闭包会常驻内存,使用需要谨慎

闭包的概念清楚了,下面来讲一下闭包的应用


闭包的应用1

循环中的闭包

我们可以试着将函数里循环中的每一个变量拿出来。

敲一下代码:

 function F() {
            var arr = [];

            for (var i = 0; i < 3; i++) {
                arr[i] = function(){
                    return i;
                };
            }
            return arr;
         }

         var str = F();

         console.log(str[0](), str[1](), str[2]());//3 3 3 

咦?什么鬼,怎么出现了三个一样的数

分析一下,不难发现,arr[i]中保存的是 i , i 是相当于直接定义在 F 函数的作用域中,执行完 F 函数 i 的值直接就已经变为 3 ,这样,我们就应该在循环的时候,就把数组里的值保存下来,这样就对了

闭包本身不保存上一作用域中变量的值,当使用时是去上面的作用域中查找相同名字变量,使用最近那一层作用域中的变量

修改代码如下:

function F() {
            var arr = [];

            for (var i = 0; i < 3; i++) {
                arr[i] = (function(x){
                    return function(){
                        return x;
                    };
                })(i);
            }
            return arr;
        }

        var str = F();

        console.log(str[0](), str[1](), str[2]());//0 1 2

也有原理图,在此:

闭包_第3张图片
2.png

这样就能保存到我们想要的数据了


闭包的应用2

getter 和 setter

有时候,为了变量的安全考虑,我们一般会不让变量直接被访问到
就可以利用闭包来实现

看看代码:

var setNum;
var getNum;


        (function(){
            var num = 0;

            //setter方法
            setNum = function(x) {
                //安全的考虑
                if (typeof x === "number") {
                    num = x;
                }
            };

            //getter方法
            getNum = function() {
                return num;
            };

        })();

        console.log(num);//0

        setNum(100);
        console.log(getNum());//100

这样的话,直接访问 num 是访问不到的,同时要修改 num 的值也不能直接修改,通过闭包的方法,我们还能控制赋给变量新赋值的类型


闭包的应用3

迭代器

迭代: 一个一个寻找下一个目标

知道遍历简单的数组,以后便利的数据结构可能比较复杂

使用“下一个是谁”的想法去遍历

代码如下:

var arr  = [1,2,3,4,5];

        function setUp(x) {
            var i = 0;
            return function (){
                if(x.length == i-1){
                    i = 0;
                }
                return x[i++];
            }
        }

        var next = setUp(arr);
        console.log(next());//1
        console.log(next());//2
        console.log(next());//3
        console.log(next());//4
        console.log(next());//5

        console.log(next());//undefined
        console.log(next());//1
        console.log(next());//2

这样就实现了迭代的方法,一次一次找到下一个值


你可能感兴趣的:(闭包)