js之代理模式

代理模式

小明追 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開發中最常用的是虛擬代理和緩存代理。雖然代理模式非常有用,但我們在編寫業務代碼的時候,往往不需要去預先猜測是否需要使用代理模式。當真正發現不方便直接訪問某個對象的時候,再編寫代理也不遲。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章