zoukankan      html  css  js  c++  java
  • 「JavaScript面向对象编程指南」闭包

    闭包


    JS只有函数作用域,函数外为全局变量,函数内为局部变量
    绿圆是函数fn的作用域,在这范围内可访问局部变量b和全局变量a,橙圆是fn内部函数inner的作用域,此范围内可访问自身作用域内的变量c,也可访问父级作用域的变量b,这就形成了一条作用域链
    全局空间(蓝圆)为0级作用域,绿圆是1级作用域,橙圆为2级作用域,依次类推
    橙圈可访问绿圈或蓝圈内的数据,但蓝圈内访问不了绿圈,绿圈访问不了橙圈,也就是可以一路由内向外访问,但由外到内却不行

    若把橙圈拿到绿圈外,蓝圈内的位置,就形成了闭包

    现在橙圈也就是函数inner和变量a一样,都是在全局作用域中,并且inner还记得它被定义时所设定的环境,因此它依旧可访问绿圈也就是函数fn的作用域并使用变量b
    突破作用域链的途径,升级为全局变量传递(返回)给全局作用域即可

    闭包#1

    var a = 'global var';
    var fn = function () {
        var b = 'local var';
        var inner = function () {
            var c = 'inner';
            return b;
        }
        return inner;
    }
    var f2 = fn(); //或者直接fn()();
    f2();
    

    函数fn中返回了inner,而inner内返回的变量b,现在inner和b都可通过作用域链进行访问

    闭包#2

    var f2; //函数占位符,这不是必须的,但最好写上
    var fn = function () {
        var b = 'local var';
         var inner = function () {
            var c = 'inner';
            return b;
        }
        f2 = inner;
    }
    fn();
    f2();
    

    将函数inner在fn内赋值给全局变量f2,由于inner是在fn内定义的,所以即使该函数后来升级成了全局函数,也依然保留着对fn作用域的访问权

    闭包#3

    function fn(param) {
       var inner = function () {
           return param;
       }
       param++;
       return inner;
    }
    var f2 = fn(123);
    f2();
    

    返回函数被调用时,param++已执行一次,所以f2()结果是124
    由此可见,函数绑定的是作用域本身,而不是函数定义时该作用域中的变量或变量当前返回的值

    闭包#4循环中的闭包

    依次输出0,1,2

    function fn() {
        var arr = [],i;
        for(i = 0; i < 3; i++){
            arr[i] = function () {
                return i;
            }
        }
        return arr;
    }
    var arr2 = fn();
    > arr2[0](); //3 , >表示在控制台输入
    > arr2[1](); //3
    > arr2[2](); //3
    

    这里创建了三个闭包,都指向一个共同的局部变量i,但闭包不会记录它们的值,它们拥有的只是相关域在创建时的一个引用
    对这三个函数任一个而言,当它去获取某个变量时,会从其所在的域开始逐级寻找那个距离最近的i值。由于循环结束时i为3,所以这三个函数都指向了这一共同值

    若想输出的是0,1,2,则需要换种闭包形式

    function fn() {
        var arr = [],i;
        for(i = 0; i < 3; i++){
            arr[i] = (function (x) {
                return function () {
                    return x;
                };
            }(i));
        }
        return arr;
    }
    var arr2 = fn();
    > arr2[0](); //0
    > arr2[1](); //1
    > arr2[2](); //2
    

    这里不再直接创建一个返回i的函数,而是将i传递给另一个自调用函数,在该函数中
    i被赋值给了局部变量x,这样一来,每次迭代中i的值就能保存在局部变量x中

    也可以使用函数声明的方式

    function fn() {
        var arr = [],i;
        for(i = 0; i < 3; i++){
            arr[i] = binder(i);
        }
        return arr;
        function binder(x) {
            return function () {
                return x;
            }
        }
    }
    var arr2 = fn();
    arr2[0](); //0
    arr2[1](); //1
    arr2[2](); //2
    

    闭包#5

    假设有个变量,表示的是某类特定值,不想将其暴露给外部,因为那样的话其他部分的代码就可能直接修改它,所以需要将其保护在相关函数内部,然后提供getter和setter函数用以访问和设置该变量的值

    var getValue,setValue;
    (function () {
        var secret = 0;
        getValue = function(){
            return secret;
        };
        setValue = function(v){
            if(typeof v === 'number'){  //假设这里有验证措施
                secret = v;
            }
        };
    }())
    > getValue(); //0
    > setValue(123);
    > getValue(); //123
    

    将getter和setter函数放在一个共同的函数中,并在该函数中定义secret变量,使这两函数能共享同一作用域
    通过一个自调用函数,在其中定义了全局函数setValue和getValue函数,并以此确保局部变量secret的不可直接访问性

    闭包#6迭代器

    有时会面对复杂的数据结构,它们通常有着与数组截然不同的序列规则,此时就需要将一些“谁是下一个”的复杂逻辑封装成易于使用的next()函数,然后只需简单地调用next()就能实现相关的遍历操作

    function setup(x) {
        var i = 0;
        return function () {
            return x[i++];
        }
    }
    var next = setup(['a','b','c']);
    > next(); // “a”
    > next(); // "b"
    

    在setup()函数中定义一个私有指针i,该指针会始终指向数组的下一个元素

  • 相关阅读:
    如何复制百度文库中的文章转的,不用担心下载要币了[转]
    什么是中间件(转)
    android实用代码 (转)
    [Java]读取文件方法大全(转)
    Solaris下查看磁盘、内存、CPU使用程度
    Gene Ontology (GO) 简介
    如何在网上查某个基因的转录因子及启动子
    kmeans k均值聚类的弱点/缺点
    层次聚类
    什么是非负矩阵分解 NMF(Nonnegative Matrix Factorization )
  • 原文地址:https://www.cnblogs.com/Grani/p/10514102.html
Copyright © 2011-2022 走看看