js之代理模式

本文深入讲解代理模式在JavaScript中的应用,包括虚拟代理、缓存代理等,并通过实例演示如何利用代理模式解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式

小明追 MM 的故事


level01:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var Flower = function(){};
    var xiaoming = {
        sendFlower: function( target ){
            var flower = new Flower();
            target.receiveFlower( flower );
        }
    };
    var A = {
        receiveFlower: function( flower ){
            console.log( '收到花 ' + flower );
        }
    };
    xiaoming.sendFlower( A );
</script>
</body>
</html>

level02:引入代理 B

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var Flower = function(){};
    var xiaoming = {
        sendFlower: function( target){
            var flower = new Flower();
            target.receiveFlower( flower );
        }
    };
    var B = {
        receiveFlower: function( flower ){
            A.receiveFlower( flower );
        }
    };
    var A = {
        receiveFlower: function( flower ){
            console.log( '收到花 ' + flower );
        }
    };
    xiaoming.sendFlower( B );//先把花给B,可B立马给了A
</script>
</body>
</html>

level03:引入机智代理 B

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var Flower = function(){};
    var xiaoming = {
        sendFlower: function( target){
            var flower = new Flower();
            target.receiveFlower( flower );
        }
    };
    var B = {
        receiveFlower: function( flower ){
            A.listenGoodMood(function(){ // 监听 A 的好心情
                A.receiveFlower( flower );
            });
        }
    };
    var A = {
        receiveFlower: function( flower ){
            console.log( '收到花 ' + flower );
        },
        listenGoodMood: function( fn ){
            setTimeout(function(){ //假设10秒之后A的心情变好
                fn();
            }, 10000 );
        }
    };
    xiaoming.sendFlower( B );
    //B收到后立马监听A的心情。B起到保护作用。
</script>
</body>
</html>

保护代理:代理 B 可以帮助 A过滤掉一些请求,比如送花人中年龄太大的或者没有宝马的,这种请求就可以直接在代理 B处被拒绝掉。
虚拟代理:new Flower 也是一个代价昂贵的操作,那么我们可以把 new Flower 的操作交给代理 B 去执行,代理 B 会选择在 A 心情好时再执行 new Flower 。
保护代理用于控制不同权限的对象对目标对象的访问,但在 JavaScript并不容易实现保护代理,因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式

虚拟代理实现图片预加载


level01:

var myImage = (function(){
    var imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    return {
        setSrc: function( src ){
            imgNode.src = src;
        }
    }
})();//myImage是一个函数对象(似java中的类)
myImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

缺点:
我们把网速调至 5KB/s,然后通过 MyImage.setSrc 给该 img 节点设置 src ,可以看到,在图片被加载好之前,页面中有一段长长的空白时间。


level02:

var MyImage = (function(){
    var imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    var img = new Image;
    img.onload = function(){
        imgNode.src = img.src;
    };
    return {
        setSrc: function( src ){
            imgNode.src = 'file:// /C:/Users/svenzeng/Desktop/loading.gif';
            img.src = src;
        }
    }
})();
MyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

缺点:
1. 违反单一职责原则
上段代码中的 MyImage 对象除了负责给 img 节点设置 sr外还要负责预加载图片。我们在处理其中一个职责时,有可能因为其强耦合性影响另外一个职责的实现。


level03:引入代理对象 proxyImage

var myImage = (function(){
    var imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    return {
        setSrc: function( src ){
            imgNode.src = src;
        }
    }
})();
var proxyImage = (function(){
    var img = new Image;
    img.onload = function(){
        myImage.setSrc( this.src );
    }
    return {
        setSrc: function( src ){
            myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
            img.src = src;//??????????????????????????????????????
        }
    }
})();
proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

现在我们通过 proxyImage 间接地访问 MyImage proxyImage 控制了客户对 MyImage 的访问,并且在此过程中加入一些额外的操作,比如在真正的图片加载好之前,先把 img 节点的 src设置为一张本地的 loading图片。

优点:
1. 列表内容

虚拟代理合并 HTTP 请求


level01:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<input type="checkbox" id="1"></input>1
<input type="checkbox" id="2"></input>2
<input type="checkbox" id="3"></input>3
<input type="checkbox" id="4"></input>4
<input type="checkbox" id="5"></input>5
<input type="checkbox" id="6"></input>6
<input type="checkbox" id="7"></input>7
<input type="checkbox" id="8"></input>8
<input type="checkbox" id="9"></input>9
<script>
    var synchronousFile = function( id ){
        console.log( '开始同步文件,id 为: ' + id );
    };
    var checkbox = document.getElementsByTagName( 'input' );
    for ( var i = 0, c; c = checkbox[ i++ ]; ){
        c.onclick = function(){
            if ( this.checked === true ){
                synchronousFile( this.id );
            }
        }
    };
</script>
</body>
</html>

level02:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="checkbox" id="1"></input>1
    <input type="checkbox" id="2"></input>2
    <input type="checkbox" id="3"></input>3
    <input type="checkbox" id="4"></input>4
    <input type="checkbox" id="5"></input>5
    <input type="checkbox" id="6"></input>6
    <script>//虚拟代理合并 HTTP 请求
        var synchronousFile = function( id ){
            console.log( '开始同步文件,id 为: ' + id );
        };
        var proxySynchronousFile = (function(){
            var cache = [], // 保存一段时间内需要同步的 ID
                timer; // 定时器
            return function( id ){
                cache.push( id );
                if ( timer ){ // 保证不会覆盖已经启动的定时器
                    return;
                }
                timer = setTimeout(function(){
                    synchronousFile( cache.join( ',' ) ); // 2 秒后向本体发送需要同步的 ID 集合
                    clearTimeout( timer ); // 清空定时器
                    timer = null;
                    cache.length = 0; // 清空 ID 集合
                }, 2000 );
            }
        })();
        var checkbox = document.getElementsByTagName( 'input' );
        for ( var i = 0, c; c = checkbox[ i++ ]; ){
            c.onclick = function(){
                if ( this.checked === true ){
                    proxySynchronousFile( this.id );
                }
             }
        };

    </script>
</body>
</html>

缓存代理


level01:计算乘积

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
        var mult = function(){
            console.log( '开始计算乘积' );
            var a = 1;
            for ( var i = 0, l = arguments.length; i < l; i++ ){
                a = a * arguments[i];
            }
            return a;
        };
        console.log(mult( 2, 3 )); // 输出:6
        console.log(mult( 2, 3, 4 )); // 输出:24
    </script>
</body>
</html>

level02:缓存代理计算乘积

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
        var mult = function(){
            console.log( '开始计算乘积' );
            var a = 1;
            for ( var i = 0, l = arguments.length; i < l; i++ ){
                a = a * arguments[i];
            }
            return a;
        };
        mult( 2, 3 ); // 输出:6
        mult( 2, 3, 4 ); // 输出:24
        //现在加入缓存代理函数:
        var proxyMult = (function(){
            var cache = {};
            return function(){
                var args = Array.prototype.join.call( arguments, ',' );
                if ( args in cache ){
                    return cache[ args ];
                }
                return cache[ args ] = mult.apply( this, arguments );
            }
        })();
        console.log(proxyMult( 1, 2, 3, 4 )); // 输出:24
        console.log(proxyMult( 1, 2, 3, 4 )); // 输出:24
    </script>
</body>
</html>

用高阶函数动态创建代理


level01:计算乘积

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script>
        /**************** 计算乘积 *****************/
        var mult = function(){
            var a = 1;
            for ( var i = 0, l = arguments.length; i < l; i++ ){
                a = a * arguments[i];
            }
            return a;
        };
        /**************** 计算加和 *****************/
        var plus = function(){
            var a = 0;
            for ( var i = 0, l = arguments.length; i < l; i++ ){
                a = a + arguments[i];
            }
            return a;
        };
        /**************** 创建缓存代理的工厂 *****************/
        var createProxyFactory = function( fn ){
            var cache = {};
            return function(){
                var args = Array.prototype.join.call( arguments, ',' );
                if ( args in cache ){
                    return cache[ args ];
                }
                return cache[ args ] = fn.apply( this, arguments );
            }
        };
        var proxyMult = createProxyFactory( mult ),
            proxyPlus = createProxyFactory( plus );
        alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
        alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
        alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
        alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
    </script>
</body>
</html>

小结
代理模式包括许多小分类,在 JavaScript开发中最常用的是虚拟代理和缓存代理。虽然代理模式非常有用,但我们在编写业务代码的时候,往往不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值