Proxy and Its Advantages

  Front end, html5, javascript, node.js, Programmer

Translation: Liu Xiaoxi

Original link:https://devinduct.com/blogpos …

What is Proxy

Usually, when talking about JavaScript language, we are talking about the new features provided by ES6 standard, and this article is no exception. We will discuss JavaScript proxies and their functions, but before we go into further research, let’s first look at what Proxy is defined.

The definition on MDN is: proxy object is a custom behavior used to define basic operations (e.g., attribute lookup, assignment, enumeration, function call, etc.).

In other words, we can say that the proxy object is the wrapper of our target object, in which we can manipulate its attributes and prevent direct access to it. You may find it difficult to apply them to actual code. I encourage you to read this concept carefully. It may change your point of view.

term

handler

Placeholder object containing traps.

traps

Provides methods for accessing properties. This is similar to the concept of a trap in an operating system.

target

The object of proxy virtualization. (Actual Objects Wrapped and Operated by Proxy Objects)

In this article, I will begetAndsetTraps provide simple use cases. Considering the end, we will see how to use them and include more complex functions such as API.

Grammar and Use Cases

let p = new Proxy(target, handler);

Pass targets and handlers toProxyConstructor, thus creating a proxy object. Now, let’s see how to use it. In order to see the benefits of Proxy more clearly, first of all, we need to write some code without it.

Imagine that we have a user object with several attributes. If the attribute exists, we want to print the user information. If it does not exist, we throw an exception. When the proxy object is not used, the code for judging whether the attribute value exists is also placed in the function for printing user information, i.e.printUser(This is not what we want), as shown in demo below:

let user = {
    name: 'John',
    surname: 'Doe'
};

let printUser = (property) => {
    let value = user[property];
    if (!value) {
        throw new Error(`The property [${property}] does not exist`);
    } else {
        console.log(`The user ${property} is ${value}`);
    }
}

printUser('name'); // 输出: 'The user name is John'
printUser('email'); // 抛出错误: The property [email] does not exist

get

By looking at the above code, you will find: move the conditions and exceptions to other places, whileprintUserIt would be better to focus only on the actual logic of displaying user information. This is where we can use proxy objects. Let’s update this example.

let user = {
    name: 'John',
    surname: 'Doe'
};

let proxy = new Proxy(user, {
    get(target, property) {
        let value = target[property];
        if (!value) {
            throw new Error(`The property [${property}] does not exist`);
        }
        return value;
    }
});

let printUser = (property) => {
    console.log(`The user ${property} is ${proxy[property]}`);
};

printUser('name'); // 输出: 'The user name is John'
printUser('email'); // 抛出错误: The property [email] does not exist

In the above example, we packaged theuserObject and sets agetMethods. This method acts as an interceptor. Before returning a value, it first checks the attribute value and throws an exception if it does not exist.

The output is the same as in the first case, but at this timeprintUserFunction focuses on logic and only processes messages.

set

Another example where agents may be useful is attribute value validation. In this case, we need to usesetMethod and verify it in it. For example, this is a very useful hook when we need to ensure the target type. Let’s look at the actual use:

let user = new Proxy({}, {
    set(target, property, value) {
        if (property === 'name' && Object.prototype.toString.call(value) !== '[object String]') { // 确保是 string 类型
            throw new Error(`The value for [${property}] must be a string`);
        };
        target[property] = value;
    }
});

user.name = 1; // 抛出错误: The value for [name] must be a string

These are fairly simple use cases. proxy can be used in the following scenarios:

  • Format
  • Value and Type Correction
  • Data binding
  • Debugging

It is time to create a more complex use case.

API with Proxy-More Complex Example

By using knowledge from simple use cases, we can create an API wrapper for use in our applications. Currently only supportedgetAndpostRequest, but it can be easily extended. The code is as follows.

const api = new Proxy({}, {
    get(target, key, context) {
        return target[key] || ['get', 'post'].reduce((acc, key) => {
            acc[key] = (config, data) => {

                if (!config && !config.url || config.url === '') throw new Error('Url cannot be empty.');
                let isPost = key === 'post';

                if (isPost && !data) throw new Error('Please provide data in JSON format when using POST request.');

                config.headers = isPost ? Object.assign(config.headers || {}, { 'content-type': 'application/json;chartset=utf8' }) :
                    config.headers;

                return new Promise((resolve, reject) => {
                    let xhr = new XMLHttpRequest();
                    xhr.open(key, config.url);
                    if (config.headers) {
                        Object.keys(config.headers).forEach((header) => {
                            xhr.setRequestHeader(header, config.headers[header]);
                        });
                    }
                    xhr.onload = () => (xhr.status === 200 ? resolve : reject)(xhr);
                    xhr.onerror = () => reject(xhr);
                    xhr.send(isPost ? JSON.stringify(data) : null);
                });
            };
            return acc;
        }, target)[key];
    },
    set() {
        throw new Error('API methods are readonly');
    },
    deleteProperty() {
        throw new Error('API methods cannot be deleted!');
    }
});

Let’s explain a simple implementation.setAnddeleteProperty. We added a level of protection and made sure that whenever someone accidentally or unintentionally tried to set a new value for any API attribute, an exception would be thrown.

Called each time an attempt is made to delete an attributedeletePropertyMethods. It can be ensured that no one can delete any attribute from our proxy (that is, api here), because we usually don’t want to lose API methods.

getIt is very interesting here. It has done several things.targetIs an empty object,getThe method will be used for the first timeapiTo create all methods such as the currentgetAndpostRequest)reduceIn the callback, we perform the verification and inspection required by the API specification according to the provided configuration. In this example, we do not allow empty URL and publish request without providing data. These checks can be expanded and modified, but the important thing is that we can only deal with them in one place.

reduceIt is only completed on the first API call, and the whole is skipped afterwards.reduceThe process,getOnly the default behavior is executed and the attribute value, the API handler, is returned. Each handler returns a Promise object, which is responsible for creating the request and calling the service.

Use:

api.get({
    url: 'my-url'
}).then((xhr) => {
    alert('Success');
}, (xhr) => {
    alert('Fail');
});
delete api.get; //throw new Error('API methods cannot be deleted!'); 

Conclusion

Agents can be useful when you need more control over your data. You can expand or deny access to raw data according to controlled rules, thus monitoring objects and ensuring correct behavior.

If you like this article, please pay attention to my public number. You can express your thoughts in the comments section below.

Thank you for your friends’ willingness to spend precious time reading this article. If this article gives you some help or inspiration, please don’t be stingy with your praise and Star. Your affirmation is my greatest motivation to move forward.https://github.com/YvetteLau/ …

I recommend paying attention to my public number.

clipboard.png