Definition 1.

The proxy pattern provides a proxy or placeholder for an object to control access to it.

The key to the proxy pattern is to provide a proxy object to control access to an object when it is inconvenient or not necessary for the client to directly access it. After the proxy object does some processing of the request, it passes the request to the ontology object.

Example 2.

Xiao Hong gives Xiao Gang a present

  • Case without proxy mode
var Gift = function(){};

var xiaohong = {
    sendGift: function(target) {
        var gift = newGift(); target.receiveGift(gift); }}var xiaogang = {
    receiveGift: function(gift) {
        console.log('Receive a gift' + gift)
    }
}

xiaohong.sendGift(xiaogang);
Copy the code
  • Use proxy mode (Xiao Hong sends Gifts to Xiao Gang through Xiao Ming)
var Gift = function(){};

var xiaohong = {
    sendGift: function(target) {
        var gift = newGift(); target.receiveGift(gift); }}var xiaoming = {
    receiveGift: function(gift) {
        xiaogang.receiveGift(gift)
    }
}

var xiaogang = {
    receiveGift: function(gift) {
        console.log('Receive a gift' + gift)
    }
}

xiaohong.sendGift(xiaogang);
Copy the code

This is a simple proxy pattern. There seems to be no essential difference between the two, and introducing a proxy object seems to complicate things. The proxy pattern here is really useless, simply passing the request to the ontology.

To change the context of the above example: Xiaogang receives gifts according to his mood

var Gift = function(){};

var xiaohong = {
    sendGift: function(target) {
        var gift = newGift(); target.receiveGift(gift); }}var xiaoming = {
    receiveGift: function(gift) {
        xiaogang.listenGoodMood(function(){ // Listen to xiao Gang's good mood
            xiaogang.receiveGift(gift)
        })
    }
}

var xiaogang = {
    receiveGift: function(gift) {
        console.log('Receive a gift' + gift)
    },
    listenGoodMood: function(fn) {
        setTimeout(function(){ // Suppose that after 10 seconds A's mood improves
            fn()
        }, 10000)
    }
}

xiaohong.sendGift(xiaogang);
Copy the code

3. Protect the proxy and virtual proxy

3.1 Protection Agent

Acting xiao Ming can help Xiao Gang filter out requests, such as gifts from people who are too old, which can be rejected directly at the acting Xiao Ming. This proxy is called a “protection proxy”

3.2 Virtual Proxy

Assuming that new Gift() is an expensive operation, the operation of new Gift() can be handed over to agent Xiao Ming, who will choose to execute new Gift() when Xiao Gang is in a good mood. This is called a “virtual proxy”. Virtual proxies delay the creation of expensive objects until they are really needed.

var xiaoming = {
    receiveGift: function(gift) {
        xiaogang.listenGoodMood(function(){ // Listen to xiao Gang's good mood
           var gift = new Gift(); // Delay the creation of the gift object
           xiaogang.receiveGift(gift)
        })
    }
}
Copy the code

A protected proxy is used to control access to a target object by objects with different permissions, but it’s not easy to implement in JavaScript because you can’t tell who is accessing an object. Virtual proxy is the most commonly used proxy mode.

4. The virtual proxy implements image preloading

If you set the SRC attribute directly to an IMG tag node, the location of the image will be blank for a period of time because the image is too large or the network is poor. A common practice is to load the image asynchronously with a loading image, and then fill the image into the IMG node after the image is loaded. This scenario is suitable for using a virtual agent.

Examples are as follows:

This object is responsible for creating an img tag in the page, and provides an external setSrc interface. This interface can be called to set the SRC attribute to the img tag:

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    
    return {
        setSrc: function(src) { imgNode.src = src; }}}) (); myImage.setSrc('http:// xxx.png');
Copy the code

As you can see, there is a gap in the page before the image is loaded.

Introduce the proxy object proxyImage. Through this proxy object, before the picture is actually loaded, a placeholder chrysanthemum map loading. GIF will appear on the page to remind the user that the picture is loading.

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//xxx/loading.gif')
            img.src = src;
        }
    }
})()

proxyImage.setSrc('http:// xxx.png')
Copy the code

Indirectly accessing MyImage through proxyImage. ProxyImage controls the client’s access to MyImage, and some additional operations are added in this process, such as setting SRC of img node to a local loading image before the real image is loaded.

5. The meaning of agency

One might wonder, what is the advantage of introducing proxy mode when it is possible to implement a small image preloading feature without introducing any patterns? Let’s get rid of the proxy and write a more common image preloading function.

The preloading image function without a proxy is implemented as follows:

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//xxx/loading.gif';
            img.src = src
        }
    }
})();  

myImage.setSrc('http:// xxx.png')
Copy the code

To illustrate the meaning of agency, we introduce a principle of object-oriented design — the single responsibility principle

The single responsibility principle states that there should be only one cause for a class (usually objects, functions, etc.) to change. If an object takes on more than one responsibility, it means that the object becomes large and can change for more than one reason. Object-oriented design encourages the distribution of behavior among fine-grained objects, and if an object takes on too many responsibilities, those responsibilities are coupled together, which leads to fragile and poorly cohesive designs. When changes occur, the design can be broken by accident.

Responsibility is defined as “cause of change”. In addition to setting SRC for the IMG node, the MyImage object is also responsible for preloading the image. Working with one responsibility might affect the implementation of the other because of its strong coupling.

In fact, all we need to do is set SRC for the IMG node, and preloading images is just the icing on the cake. It’s a good idea to put this operation inside another object. This is where the agent comes in. The agent is responsible for preloading the image, and when the preloading operation is complete, the request is redirected to the ontology MyImage.

We didn’t change or add the interface to MyImage, but by proxy objects, we actually added new behavior to the system. This is in line with the open-closure principle. Setting SRC and image preloading on the IMG node is separated into two objects that can be changed separately without affecting the other. And even if one day you don’t need preloading any more, you just need to change to a request ontology instead of a request proxy object.

6. Consistency between the proxy and the local interface

In the previous example, if one day we no longer need preloading, then we no longer need proxy objects, we can choose to request ontology directly. The key point is that both the proxy object and ontology provide setSrc method externally. In the eyes of customers, the proxy object and ontology are consistent. The process of proxy taking over requests is transparent to users, who are not clear about the difference between the proxy and ontology.

  • The user can safely request the broker, and all he cares about is getting the result he wants
  • Anywhere an ontology is used it can be replaced with a proxy.

In languages such as Java, agent and ontology needs to be explicitly implement the same interface, on the one hand, the interface ensures that they will have the same way, on the other hand, the programming to an interface to cater to the dependency inversion principle, through the interface up transformation, thus to avoid the compiler type checking, agent and ontology can be replaced to use in the future.

If both the proxy object and the ontology object are a function (which is also an object), and both functions must be able to be executed, then they can also be considered to have a consistent “interface.”

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    
    return function(src) {
        imgNode.src = src;
    }
})();

var proxyImage = (function(){
    var img = new Image;
    
    img.onload = function(){
        myImage(this.src);
    }
    
    return function(src){
        myImage('file//xxx/loading.gif');
        img.src = src;
    }
})();

proxyImage('http:// xxx.png');
Copy the code

7. The virtual proxy merges HTTP requests

var synchronousFile = function(id) {
    console.log('Start file synchronization with id:' + id);
}

var proxySynchronousFile = (function(){
    var cache = [],
        timer;
    
    return function(id) {
        cache.push(id);
        if(timer) {
            return;
        }
        
        timer = setTimeout(function(){
            synchronousFile(cache.join(', '));
            clearTimeout(timer);
            timer = null;
            cache.length = 0;
        }, 2000)}}) ()var checkbox = document.getElementsByTagName('input');

for(var i=0,c; c=checkbox[i++];) { c.onclick =function(){
        if(this.checked === true) {
            proxySynchronousFile(this.id); }}}Copy the code

8. Application of virtual proxy in lazy loading

var miniConsole = (function(){
    var cache = [];
    var handler = function(ev){  
        if(ev.keyCode === 113) {// When the user presses F2, the real miniconsole.js starts to load
            var script = document.createElement('script');
            script.onload = function(){
                for(var i=0, fn; fn=cache[i++];) {
                    fn();
                }
            }
            script.src = 'miniConsole.js';
            document.getElementsByTagName('head') [0].appendChild(script);
            document.body.removeEventListener('keydown', handler); // Load miniconsole.js only once}}document.body.addEventListener('keydown',handler, false);
    
    return {
        log: function(){
            var args = arguments;
            cache.push(function(){
                return miniConsole.log.apply(miniConsole, args)
            })
        }
    }
    
})()

miniConsole.log(11)
Copy the code

9. Cache proxy

It can provide temporary storage for some expensive operation results. In the next operation, if the parameters passed in are the same as before, it can directly return the previously stored operation results

9.1 Calculating the Product

var mult = function(){
    var a = 1;
    for(var i=0, l = arguments.length; i < l; i++) {
        a = a * arguments[i];
    }
    return a;
}

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);
    }
})();

proxyMult(1.2.3.4)
proxyMult(1.2.3.4) // Returns the previously cached result directly
Copy the code

10. Create proxies dynamically with higher-order functions

Caching proxies can be created for various computation methods by passing in higher-order functions in a more flexible manner. Now that these calculations are passed as arguments into a factory dedicated to creating caching proxies, we can create caching proxies for multiplication, addition, subtraction, and so on

// Compute the product
var mult = function(){
    var a = 1;
    for(var i=0, l = arguments.length; i<l; i++) { a = a *arguments[i];
    }
    return a;
}

// Calculate the sum
var plus = function(){
    var a = 0;
    for(var i=0, l = arguments.length; i<l; i++) { a = a +arguments[i];
    }
    return a;
}

// Create a factory for the cache proxy
var createProxyFactory = function(fn) {
    var chache = {}
    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))
alert(proxyMult(1.2.3.4))
alert(proxyPlus(1.2.3.4))
alert(proxyPlus(1.2.3.4))
Copy the code

11. Other proxy modes

  • Firewall proxy: controls access to network resources
  • Remote proxy: Provides local representation of an object in different address Spaces
  • Protected proxy: Used when objects should have different access rights
  • Smart reference proxy: Instead of simple Pointers, it performs additional operations on accessing objects, such as counting the number of times an object is referenced
  • Copy-on-write agent: Typically used when copying a large object. The copy-on-write agent delays the copying process until the object is actually modified. Copy-on-write agent is a variant of virtual agent, and DLL(dynamic link library in operating system) is its typical application scenario.

The most commonly used in JavaScript development are virtual proxies and caching proxies.