5js面向对象基础-闭包的概念及应用

5 js面向对象基础 - 闭包的概念及应用

闭包的概念

注意:预解析,变量声明,词法作用域,作用域链等知识的理解,对闭包的彻底理解起重要的作用。没有基础的建议先看上一篇 4 js面向对象基础 - 预解析,词法作用域,作用域链

字面意义:

  • 闭 : 关闭,封闭
  • 包 : 包裹, 打包

  • 闭包的含义就是一个被包裹的隔离的空间

在 js 中, 什么是闭包 ?

  • 在 js 中函数是一个具有变量作用域隔离特性的一个内存结构, 函数的内部内容外部无法访问,是一个被封闭的内容,即为一个闭包。

学习闭包, 在 js 中到底要解决什么问题

在 js 中闭包要解决的问题就是间接的访问到这个被隔离的数据.

    function foo () {
        var num = 123;
        return num; // return 只是进行了 拷贝,外界还是没有真正的访问 num
    }
    var num = foo();

在外界想访问到 num 中的数据. 怎么做? =》 我们可以使用闭包的间接访问


闭包的间接访问

使用 return 数据不能直接访问原来的数据, 那么可以考虑利用函数的返回访问原始数据

    function foo() {
        var num = Math.random();    // 建随机数

        function fuc() {
            return num;  /* 原始数据 num */
        }
        return fuc; // 把函数返回
    }
    var fn = foo(); // 获取函数引用, 返回的函数功能是 获取 foo函数中 变量 num

    var num1 = fn();
    console.log( num1 );    // 0.4612285847198856
    var num2 = fn();
    console.log( num2 );    // 0.4612285847198856

fn 中存储的是 foo 里面定义的函数的 引用,可以使用 fn 来调用,根据词法作用域,返回了 foo 中的 num,间接的访问了闭包中的数据


闭包的应用

闭包的应用-利用闭包实现私有数据

    function foo(){
        var num1 = 123, num2 = 456;
        return {
            get_num1: function() {
                return num1;
            },
            set_num1: function( value ) {
                num1 = value;
            },
            get_num2: function() {
                return num2;
            }
        };
    }
    var o = foo();
    console.log( o.get_num1() );    // 获取 num1  123
    o.set_num1(789);    // 修改 num1
    console.log( o.get_num1() );    // 再次获取 num1    789
    console.log( o.get_num2() );    // 获取 num2 456

从上可以看到外部对 num1 可读可写,对 num2 可读不可写

函数允许返回一个对象, 那么该对象可以提供数据访问方法,但是数据存储在闭包中, 来达到私有的目的


    function createPerson ( name, age, gender ) {
        var hasChangeGender = false;
        return {
            get_Name: function () {
                return name;
            }, set_Name: function ( value ) {
                name = value;
            }, get_Age: function () {
                return age;
            }, get_Gender: function () {
                return gender;
            }, set_Gender: function ( value ) {
                if ( hasChangeGender == false ) {
                    gender = value;
                    hasChangeGender = true;
                } else {
                    throw new Error( '已经改变过一次性别了, 不能再修改了' );
                }
            }
        };
    }

    var p1 = createPerson( '张三', 19, '男' );

    console.log( 'p1.name = ' + p1.get_Name() );    // 张三
    console.log( 'p1.age = ' + p1.get_Age() );  // 19
    console.log( 'p1.gender = ' + p1.get_Gender() );    // 男

    p1.set_Name( '王二' );
    p1.set_Gender( '女' );

    console.log( 'p1.name = ' + p1.get_Name() );    // 王二
    console.log( 'p1.age = ' + p1.get_Age() );  // 19
    console.log( 'p1.gender = ' + p1.get_Gender() );    // 女

    p1.set_Name( '王三' );
    p1.set_Gender( '男' );   // Uncaught Error: 已经改变过一次性别了, 不能再修改了

总结:闭包实现各种特性, 其根本的核心内容只有两个

  1. 带有私有数据的 函数
        function foo () {
            var num = 123;
            return function () {
                // 可以访问 num
            }
        }
        var func = foo();
        // 称 func 是一个 带有私有数据的 函数
        // 称 func 带有缓存
**称 func 是一个 带有私有数据的 函数**

**称 func 带有缓存**
  1. 带有私有数据的 对象

闭包的应用-沙箱模式

沙箱就是一个隔离的执行环境。js 中沙箱就是一个自调用函数,写在这个函数中变量,不会被外面的内容所影响,这样的话,使得我们的数据更加的安全,代码也能正常使用和运行。

在 js 中 什么情况需要使用沙箱?

    function Person() {....}
    var p = Person. ...
    Person.prototype = ...
    ...

定义变量越多, 会怎样? -> 出现冲突的可能性越大

而有时代码中 为了使得代码更加简洁, 会引入很多变量, 我们通过沙箱就可以解决这样的问题。

    (function () {
        // 沙箱模式
        // 所有的代码写在这里
    })();

闭包的应用-模拟onload事件的追加和移除

onload 事件的追加 addEvent

    /* 新建 jepson 对象来接收 沙箱返回的对象 */
    var jepson = ( function() {
        /* 新建 私有变量 */
        var arr = [];
        /* window onload 以后,遍历执行 arr数组中的全部方法 */
        window.onload = function() {
            for( var i = 0; i < arr.length; i++ ) {
                if( typeof arr[ i ] == "function" )  arr[ i ]();
            }
        };
        /* 返回对象,对象中存放 addEvent方法,用以追加 onload执行事件 */
        return {
            addEvent: function( fn ) {
                arr.push( fn );
            }
        }
    })();
    jepson.addEvent( function() {
        console.log( " 我被追加载 onload 执行了 1 " );
    });

onload 事件的移除 removeEvent

    /* 新建 jepson 对象来接收 沙箱返回的对象 */
    var jepson = ( function() {
        /* 新建 私有变量 */
        var arr = [];

        window.onload = function() {
            for( var i = 0; i < arr.length; i++ ) {
                if( typeof arr[ i ] == "function" )  arr[ i ]();
            }
        };
        return {
            addEvent: function( fn ) {      /* 追加 */
                arr.push( fn );
            },
            removeEvent: function( fn ) {   /* 移除 */
                // 遍历 arr, 发现相同的就删除
                for( var i = 0; i < arr.length; i++ ) {
                    if ( fn == arr[ i ] )   arr.splice( i, 1 )
                }
            }
        }
    })();
    /* 要删除必须传引用,所以用变量存一下引用 */
    var f1 = function() {
        console.log( " 我被追加载 onload 执行了 1 " );
    };
    var f2 = function() {
        console.log( " 我被追加载 onload 执行了 2 " );
    };
    var f3 = function() {
        console.log( " 我被追加载 onload 执行了 3 " );
    };
    jepson.addEvent( f1 );
    jepson.addEvent( f2 );
    jepson.addEvent( f3 );
    jepson.removeEvent( f1 );   // f1 被移除了

这里只会输出,f1已被移除

我被追加载 onload 执行了 2

我被追加载 onload 执行了 3

补充: setInterval函数调用时,也尽量用变量保存引用,不然每隔一秒,都会新建一个不同的函数来调用,大大的占用了内存,消耗了性能。

    var f1 = function() { ... };
    setInterval( f1, 1000 );

闭包的应用-模拟一个缓存结构

cache 对象, 可以使用 cache[ key ] = value 存储数据, cache[ key ] 获得数据

当 cache 里面的数据达到 1024 条, 将最早放进去的数据移除。

而模拟时, cache = {} 可以存取数据, 但是不能限定数据的长度

我们需要限定数据,就是在加入数据的时候判断,是否已经超过尺寸,来决定是否移除

可以将 cache 做成函数,那么添加数据时,使用 cache( key, value ),而且函数本身也是对象

    function cache ( key, value ) {
        // 可以在这里加上限定长度的代码
        cache[ key ] = value;
    }

    cache( 'attr', function ( node, attr ) {
        return node.getAttribute( 'name' ) == attr;
    } );
    cache( '__name__', '张三' );

由于需要记录键的数量. 并且需要记录添加数据的先后顺序. 所有首先考虑有序的数组.因此需要让 cache 函数带有缓存功能

  1. 搭建结构

        var cache = (function () {
            var data = [];  // 新建 data数组,用来保存 键
    
            function cache ( key, value ) {
                // 准备做判断, 如果超出范围, 则, 将最开始加入的 移除
                cache[ key ] = value;
            }
            return cache;
        })();
        cache( 'age', 20 ); // 给 cache 添加 age 属性,值为 20
    
  2. 实现

        var cache = (function () {
            var data = [], max = 3;
            function cache ( key, value ) {
                // 做判断, 如果超出范围, 则, 将最开始加入的 移除
                if ( data.length >= 3 ) {
                    // 需要先移除
                    var temp = data.shift();    // 移除第一项并获取 key
                    delete cache[ temp ];   // 删除 cache对象中的 temp 属性
                }
    
                data.push( key );
                console.log( data );
    
                cache[ key ] = value;
            }
            return cache;
        })();
    
        cache( 'name1', '张三' );
        cache( 'name2', '李四' );
        cache( 'name3', '王五' );
        cache( 'name4', '找钱' );
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值