This is the 26th day of my participation in Gwen Challenge

The article guide

The agency mode is long, which is divided into the first, middle and next three chapters.

Implementing the Proxy pattern in JavaScript (Part 1)

What is the proxy mode? The simple implementation of proxy mode

Links: juejin. Cn/post / 697850…

Implementing the Proxy Pattern in JavaScript (Part)

Overview: Analyze the various uses of the proxy pattern

Links: juejin. Cn/post / 697903…

Implementing the Proxy pattern in JavaScript (Part 2)

ES6 Proxy API to implement a more convenient Proxy mode

Links: juejin. Cn/post / 697924…

Use the protected agent in proxy mode

The protection proxy is used in scenarios where objects should have different access rights to control access to the original object.

Still use the example from the book above, because Xiaoming and xiaojie’s roommate are good friends, she knows xiaoming’s character, so she is willing to send flowers to xiaojie for Xiaoming.

If Xiao Ming had been replaced with an unrelated person, it would have been impossible for her roommate to agree to this strange request.

But it’s not easy to implement a protected proxy in JavaScript because you can’t tell who’s accessing an object. So let’s make a simple change to the following example, adding sources to the flower class to achieve a simple protection agent.

/ / flowers
class Flower {
    constructor(source) {
        this.source = source; }}/ / xiao Ming
let xiaoming = {
    sendFlower(target){
        let flower = new Flower('xiaoming'); target.receiveFlower( flower ); }};/ / passers-by
let passerby = {
    sendFlower(target){
        let flower = new Flower('passerby'); target.receiveFlower( flower ); }};// Little sister's best friend
let ladybro = {
    receiveFlower(flower){
        if (flower.source === 'xiaoming') {
            cuteGirl.listenGoodMood(() = > {    // Monitor A's good mood
              cuteGirl.receiveFlower( flower );
            });
        } else {
            throw new Error('Little sister's best friend refused to send you flowers! ')}}};/ / a little sister
let cuteGirl = {
    receiveFlower( flower ){
        console.log( 'Received flowers' + flower );
    },
    listenGoodMood( fn ){
        setTimeout(() = > {    // Suppose that after 10 seconds A's mood improves
            fn();
        }, 10000); }};// Xiao Ming will be flowers to a good friend, entrust a good friend in the little sister in a good mood will be flowers to the little sister
xiaoming.sendFlower( ladybro );
// Passers-by will flowers to little sister's bestie, entrust her to little sister in a good mood when the flowers to little sister
passerby.sendFlower( ladybro );
Copy the code

Use the virtual proxy in proxy mode

Again, using the example from the book above, there are many types of flowers, and the price of each flower is not nearly the same, and different flowers have different expiration dates.

Xiaoming in order to win the favor of the little sister, I hope the little sister’s bestie in the little sister in a good mood, and then to help buy a bunch of more expensive flowers transferred to the little sister, at this time the operation is called virtual agent. Virtual proxies delay the creation of expensive objects until they are really needed.

// Little sister's best friend
let ladybro = {
    receiveFlower(flower){
        if (flower.source === 'xiaoming') {
            cuteGirl.listenGoodMood(() = > {    // Monitor A's good mood
              let flower = new Flower('xiaoming'); // Delay the creation of flower objects
              cuteGirl.receiveFlower( flower );
            });
        } else {
            throw new Error('Little sister's best friend refused to send you flowers! ')}}};Copy the code

Common virtual proxy implementations

Image preloading

This is also one of the common development requirements, citing the example in the book. Before loading the image, you want to have a loading map to fill the space, and then fill the img node after loading the loading map.

Proxy mode is not used

let MyImage = (function(){
    let imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    // Create an Image object to load the Image to be set
    let img = new Image;

    img.onload = function(){
        // After the image is loaded, set SRC to the image after loading
        imgNode.src = img.src;
    };

    return {
        setSrc: function( src ){
            // Set the loading graph to the default
            imgNode.src = 'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif';
            // Pass the actual Image to the SRC property of the Image objectimg.src = src; }}}) (); MyImage.setSrc('https://img.zcool.cn/community/01b620577ccc8b0000012e7ede064f.jpg@1280w_1l_2o_100sh.jpg' );
Copy the code

This is often the easiest way to write code without using proxy mode. It doesn’t have any business problems, but the MyImage object is responsible for preloading images in addition to setting SRC for the IMG node, which violates the principle of object-oriented design — the single responsibility principle. When we deal with one responsibility, it may affect the implementation of the other because of its strong coupling.

It also violates the open-closure principle, according to which:

Software entities (classes, modules, functions), etc., should be extensible but not modifiable.

Loading in English is coupled to MyImage. If we do not need to show loading in the future, we can only change the code in MyImage. While MyImage can fix the problem with just a few lines of code, it would be risky to modify its source code for other JavaScript projects with even 100,000 lines of code.

Using proxy mode

// Image local object, responsible for creating an IMG tag in the page and providing an external setSrc interface
let myImage = (function(){
    let imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );

    return {
        // the setSrc interface is invoked to set the SRC attribute for the img tag
        setSrc: function( src ){ imgNode.src = src; }}}) ();// The proxy object is responsible for image preloading
let proxyImage = (function(){
    // Create an Image object to load the Image to be set
    let img = new Image;
    img.onload = function(){
        // When the image is loaded, set SRC to the local object of the proxied image
        myImage.setSrc( this.src );
    }
    return {
        setSrc: function( src ){
            // If the image is not loaded properly, this image is used as loading to prompt the user that the image is loading
            myImage.setSrc( 'https://img.zcool.cn/community/01deed576019060000018c1bd2352d.gif'); img.src = src; }}}) (); proxyImage.setSrc('https://img.zcool.cn/community/01b620577ccc8b0000012e7ede064f.jpg@1280w_1l_2o_100sh.jpg' );
Copy the code

After using proxy mode:

The image local object is responsible for creating an IMG tag in the page and providing an external setSrc interface.

The proxy object is responsible for introducing the loading diagram before the image is loaded, which is responsible for the function of image preloading.

At the same time, it also satisfies the basic idea of the open-closure principle:

The basic idea of the open-closed principle is that when it is necessary to change the functionality of a program or to add new features to the program, the code can be added, but the source code cannot be changed.

We didn’t change or add the interface to MyImage, but by proxy objects, we actually added a new behavior to the system (in this case, image preloading).

Merging HTTP requests

Here is also quoting an example from the book, for example, we need to do a file synchronization function, when selecting the corresponding file, it needs to be synchronized to its own OneDrive.

Replace the sync folder in OneDrive with the checkbox in the web page

<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="Seven"></input>7
    <input type="checkbox" id="8"></input>8
    <input type="checkbox" id="9"></input>9
</body>
Copy the code
Proxy mode is not used
// The network operation function that synchronizes file requests
let synchronousFile = function( id ){
     console.log( 'Start file synchronization with id:' + id );
};
// check all the checkbox selectors in the page.
let checkbox = document.getElementsByTagName( 'input' );
// Iterate over the checkbox selector
for ( let i = 0, c; c = checkbox[ i++ ]; ) {// Add click event, click if the state is selected, trigger file synchronization request
    c.onclick = function(){
        if ( this.checked === true ){
            synchronousFile( this.id ); }}};Copy the code

When the proxy mode is not used, the synchronousFile function can be proxyed without changing the synchronousFile function.

Using proxy mode
// The network operation function that synchronizes file requests
let synchronousFile = function( id ){
    console.log( 'Start file synchronization with id:' + id );
};
// Synchronize the file request network operation function - proxy function
let proxySynchronousFile = (function(){
    let cache = [],    // Save the ids to be synchronized over a period of time
        timer;    / / timer

     return function( id ){
        cache.push( id );
        if ( timer ){    // Ensure that the started timer is not overwritten
            return;
        }

        timer = setTimeout(function(){
            synchronousFile( cache.join( ', '));// Send the set of ids to be synchronized to the ontology after 2 seconds
            clearTimeout( timer );    // Empty the timer
            timer = null;
            cache.length = 0; // Clear the ID collection
        }, 2000 );
    }
})();
// check all the checkbox selectors in the page.
let checkbox = document.getElementsByTagName( 'input' );
// Iterate over the checkbox selector
for ( let i = 0, c; c = checkbox[ i++ ]; ) { c.onclick =function(){
        // Add a click event, click if the state is selected, trigger the synchronization file request proxy function
        if ( this.checked === true ){
            proxySynchronousFile( this.id ); }}};Copy the code

SynchronousFile is proxySynchronousFile, which adds an array to the cache. Any checkbox checked within two seconds is added to the cache check. Wait for 2 seconds before sending all the file ids that need to be synchronized within 2 seconds to the server (splicing multiple ids into comma-separated strings), which can greatly reduce the pressure on the server in systems with low real-time requirements.

Applications in lazy loading

From the example in the book, consider a miniConsole project — miniconsole.js — that has a log function for printing parameters.

/ / miniConsole. Js code

let miniConsole = {
    log: function(){
        // The real code is omitted
        console.log( Array.prototype.join.call( arguments)); }};export default miniConsole
Copy the code

Since the console project is only needed when the console is displayed, we want it to start loading it when it is necessary, such as loading Miniconsole.js by pressing F2, then lazily loading miniconsole.js in proxy mode.

The general steps are as follows:

  1. The miniconsole.js script tag is dynamically introduced when the user hits F2
  2. Any log command executed before the user hits F2 is cached in the proxy object’s internal cache array
  3. After dynamic introduction of Miniconsole.js is completed, remove and execute it one by one.

The detailed code is as follows:

/ / proxyMiniConsole. Js code

// miniConsole's proxy object
let proxyMiniConsole = (function(){
    // Store the callback function each time log is executed
    let cache = [];
    let handler = function( ev ){
        // If the user presses F2 to call the console
        if ( ev.keyCode === 113) {// Perform the operation to import miniconsole.js
            let script = document.createElement( 'script' );
            script.src = 'miniConsole.js';
            document.getElementsByTagName( 'head') [0].appendChild( script );
            document.body.removeEventListener( 'keydown', handler );// Load miniconsole.js only once
            script.onload = function(){
                // If the miniconsole.js script tag is imported and loaded
                for ( var i = 0, fn; fn = cache[ i++ ]; ) {// Iterate over all cached callback functions and executefn(); }}; }};// Listen for keyboard keystroke events
    document.body.addEventListener( 'keydown', handler, false );

    return {
        // Return the method after the proxy
        log: function(){
            // Get all the arguments passed in
            let args = arguments;
            // Add arguments to the cache list to print
                cache.push( function(){
                    returnminiConsole.log.apply( miniConsole, args ); }); }}}) (); miniConsole.log(11 );      // Start printing log
Copy the code

Use the cache proxy in proxy mode

The cache proxy can provide temporary storage for some expensive operation results, and can return the previously stored operation results directly on the next operation if the parameters passed in are the same as before.

Calculation of the product

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

/**************** calculates plus and *****************/
let plus = function(){
    let a = 0;
    for ( let i = 0, l = arguments.length; i < l; i++ ){
        a = a + arguments[i];
    }
    return a;
};

/**************** Create a factory for caching proxies *****************/
let createProxyFactory = function( fn ){
    // Cache the computed results
    let cache = {};
    return function(){
        // Concatenate all passed arguments with a string
        let args = Array.prototype.join.call( arguments.', ' );
        // If this parameter exists in the cache
        if ( args in cache ){
            // Returns the cached result directly
            return cache[args];
        }
        // Otherwise, evaluate the value
        return  cache[args] = fn.apply( this.arguments); }};let proxyMult = createProxyFactory( mult ),
proxyPlus = createProxyFactory( plus );

console.log ( proxyMult( 1.2.3.4));// Output: 24
console.log ( proxyMult( 1.2.3.4));// Output: 24
console.log ( proxyPlus( 1.2.3.4));// Output: 10
console.log ( proxyPlus( 1.2.3.4));// Output: 10

Copy the code

The resources

[cuG-gz] Advance of front-end knowledge — proxy mode

www.yuque.com/cuggz/feplu…

Proxy patterns for front-end design patterns

Juejin. Cn/post / 684490…

Comics: What is “agency mode”?

Mp.weixin.qq.com/s/O8_A2Ms9M…

JavaScript design patterns and development practices

www.ituring.com.cn/book/1632

Relearning JavaScript design patterns from ES6 (5): Proxy patterns and Proxies

Segmentfault.com/a/119000001…

Optimize the application using JavaScript native Proxy

Juejin. Cn/post / 684490…