May 08, 2021 ES6
Proxy
is used to modify the default behavior of certain operations, equivalent to making changes at the language
“元编程”(
that is, programming the programming language.
Proxy
can be understood to set up a
“拦截”
outside access to the object must first pass through this layer of interception, thus providing a mechanism to filter and rewrite outside access.
The word Proxy is meant to be a proxy, and here it is used to "proxy" certain operations, which can be translated as "agents".
var obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
});
The code above sets up a layer of interception on an empty object, redefining the
读取
(get) and set
设置
behavior of the property. H
ere for the time being, we will not explain the specific syntax, but only the results of the operation.
For the object obj, which has an intercept behavior set, read and write its properties, and you get the following results.
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2
As the code above explains, Proxy
重载
overloads the point operator, which overrides the original definition of the language with its own definition.
ES6 natively provides a Proxy constructor to generate a Proxy instance.
var proxy = new Proxy(target, handler);
All usages of the Proxy object are in this form above, but the difference is
handler
parameter is written.
Where new Proxy() means to generate a Proxy instance, the target parameter represents the target object to be intercepted, and the handler parameter is also an object to customize the blocking behavior.
Here is another example of blocking read property behavior.
var proxy = new Proxy({}, {
get: function(target, propKey) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
In the code above, as a constructor, Proxy accepts two parameters. T he first argument is the target object to be proxyd (the example above is an empty object), i.e. if there is no involvement of Proxy, the operation was originally to access this object, and the second argument is a configuration object, for each agent operation, need to provide a corresponding handler, the function will intercept the corresponding operation. F or example, in the code above, the configuration object has a get method that intercepts access requests to the target object properties. T he two parameters of the get method are the target object and the property to be accessed. As you can see, because the intercept function always returns 35, you get 35 to access any property.
Note that for Proxy to work, you must work on the Proxy instance (in the case of the proxy object above) rather than on the target object (the above example is an empty object).
If handler does not set any intercepts, it is equivalent to going directly to the original object.
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
In the code above, handler is an empty object with no blocking effect, and accessing proxy is equivalent to accessing target.
One trick is to set the Proxy object to the object.proxy property so that it can be called on the object object.
var object = { proxy: new Proxy(target, handler) };
The Proxy instance can also be used as a prototype object for other objects.
var proxy = new Proxy({}, {
get: function(target, propKey) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
In the code above, the proxy object is a prototype of the obj object, and the obj object itself does not have a time property, so according to the prototype chain, the property is read on the proxy object, resulting in interception.
The same interceptor function can be set to intercept multiple operations.
var handler = {
get: function(target, name) {
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
},
apply: function(target, thisBinding, args) {
return args[0];
},
construct: function(target, args) {
return {value: args[1]};
}
};
var fproxy = new Proxy(function(x, y) {
return x + y;
}, handler);
fproxy(1, 2) // 1
new fproxy(1, 2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo === "Hello, foo" // true
For operations that can be set, but not blocked, they fall directly on the target object, producing results the way they were.
Below is a list of 13 interceptions supported by Proxy.
Below is a detailed description of these interception methods above.
get
method is used to intercept the read
读取
and can accept three parameters, followed by the target object, the property name, and the proxy instance itself (strictly speaking, the object against which the operation behavior is directed), the last of which is optional.
The use of the get method, which already has an example above, is another example of intercepting read operations.
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
if (propKey in target) {
return target[propKey];
} else {
throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
}
}
});
proxy.name // "张三"
proxy.age // 抛出一个错误
The code above indicates that an error is thrown if the target object is accessed for a property that does not exist. Without this intercept function, access to properties that do not exist will only return undefined.
The get method can be inherited.
let proto = new Proxy({}, {
get(target, propertyKey, receiver) {
console.log('GET ' + propertyKey);
return target[propertyKey];
}
});
let obj = Object.create(proto);
obj.foo // "GET foo"
In the code above, the intercept operation is defined above the Prototype object, so if you read the properties inherited by the obj object, the intercept takes effect.
The following example uses get blocking to enable an array to read a negative index.
function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver);
}
};
let target = [];
target.push(...elements);
return new Proxy(target, handler);
}
let arr = createArray('a', 'b', 'c');
arr[-1] // c
In the code above, the position parameter of the array is -1, which outputs the penultimate member of the array.
Proxy
you to convert an operation (get) that reads a property into a function that enables a chained operation of the property.
var pipe = function (value) {
var funcStack = [];
var oproxy = new Proxy({} , {
get : function (pipeObject, fnName) {
if (fnName === 'get') {
return funcStack.reduce(function (val, fn) {
return fn(val);
},value);
}
funcStack.push(window[fnName]);
return oproxy;
}
});
return oproxy;
}
var double = n => n * 2;
var pow = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;
pipe(3).double.pow.reverseInt.get; // 63
After setting up Proxy in the code above, the effect of chaining function names is achieved.
The following example is using get blocking to implement a common function dom that generates various DOM nodes.
const dom = new Proxy({}, {
get(target, property) {
return function(attrs = {}, ...children) {
const el = document.createElement(property);
for (let prop of Object.keys(attrs)) {
el.setAttribute(prop, attrs[prop]);
}
for (let child of children) {
if (typeof child === 'string') {
child = document.createTextNode(child);
}
el.appendChild(child);
}
return el;
}
}
});
const el = dom.div({},
'Hello, my name is ',
dom.a({href: '//example.com'}, 'Mark'),
'. I like:',
dom.ul({},
dom.li({}, 'The web'),
dom.li({}, 'Food'),
dom.li({}, '…actually that\'s it')
)
);
document.body.appendChild(el);
The following is an example of a third parameter of the get method, which always points to the object where the original read operation is located, typically the Proxy instance.
const proxy = new Proxy({}, {
get: function(target, key, receiver) {
return receiver;
}
});
proxy.getReceiver === proxy // true
In the code above, the getReceiver property of the proxy object is provided by the proxy object, so receiver points to the proxy object.
const proxy = new Proxy({}, {
get: function(target, key, receiver) {
return receiver;
}
});
const d = Object.create(proxy);
d.a === d // true
In the code above, the d object itself does not have an a property, so when you read d.a, you look for the prototype proxy object of d. At this point, receiver points to d, which represents the object where the original read operation is located.
If a property is not configurable and cannot be written, Proxy cannot modify the property, otherwise accessing the property through the Proxy object will report an error.
const target = Object.defineProperties({}, {
foo: {
value: 123,
writable: false,
configurable: false
},
});
const handler = {
get(target, propKey) {
return 'abc';
}
};
const proxy = new Proxy(target, handler);
proxy.foo
// TypeError: Invariant check failed
set
method is used to intercept the assignment of a property and can accept four parameters, followed by the target object, property name, property value, and the Proxy instance itself, the last of which is optional.
Assuming that the Person object has an age property that should be an integer not greater than 200, you can use Proxy to ensure that the property value of age meets the requirements.
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错
In the above code, because the memory function set is set, any non-compliant age property assignment throws an error, which is an implementation of data validation. The set method also allows you to bind data, i.e. update the DOM automatically whenever an object changes.
Sometimes, we set internal properties on top of an object, and the first character of the property name begins with an underscore, indicating that these properties should not be used externally. By combining the get and set methods, you can prevent these internal properties from being read and written externally.
const handler = {
get (target, key) {
invariant(key, 'get');
return target[key];
},
set (target, key, value) {
invariant(key, 'set');
target[key] = value;
return true;
}
};
function invariant (key, action) {
if (key[0] === '_') {
throw new Error( Invalid attempt to ${action} private "${key}" property );
}
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property
In the above code, as long as the first character of the read-write property name is underlined, all thrown wrong, so as to achieve the purpose of prohibiting read-write internal properties.
The following is an example of the fourth parameter of the set method.
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
}
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
proxy.foo === proxy // true
In the above code, the fourth parameter of the set method, receiver, refers to the object where the original operation behavior is located, typically the proxy instance itself, see the following example.
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
}
};
const proxy = new Proxy({}, handler);
const myObj = {};
Object.setPrototypeOf(myObj, proxy);
myObj.foo = 'bar';
myObj.foo === myObj // true
In the code above, when setting the value of the myObj.foo property, myObj does not have a foo property, so the engine d'It doesn't look for the foo property in myObj's prototype chain. M yObj's prototype object proxy is a Proxy instance, and setting its foo property triggers the set method. At this point, the fourth parameter, receiver, points to the object myObj, where the original assignment behavior is located.
Note that if a property of the target object itself is not writeable and non-configurable, the set method will not work.
const obj = {};
Object.defineProperty(obj, 'foo', {
value: 'bar',
writable: false,
});
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = 'baz';
}
};
const proxy = new Proxy(obj, handler);
proxy.foo = 'baz';
proxy.foo // "bar"
In the code above, the obj.foo property is not writeable and Proxy's set proxy for this property will not take effect.
Note that in strict mode, the set agent will report an error if it does not return true.
'use strict';
const handler = {
set: function(obj, prop, value, receiver) {
obj[prop] = receiver;
// 无论有没有下面这一行,都会报错
return false;
}
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
// TypeError: 'set' on proxy: trap returned falsish for property 'foo'
In the above code, in strict mode, the set agent returns false or undefined, which will report an error.
apply
method intercepts the call, call, and apply operations of the function.
apply
method can accept three parameters, namely,
目标对象
the context object of
上下文对象
object (this), and the parameter array of the
参数数组
var handler = {
apply (target, ctx, args) {
return Reflect.apply(...arguments);
}
};
Here's an example.
var target = function () { return 'I am the target'; };
var handler = {
apply: function () {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
p()
// "I am the proxy"
In the above code, the variable p is an instance of Proxy, and when it is called as a function (p(), it is intercepted by the apply method, returning a string.
Here's another example.
var twice = {
apply (target, ctx, args) {
return Reflect.apply(...arguments) * 2;
}
};
function sum (left, right) {
return left + right;
};
var proxy = new Proxy(sum, twice);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30
In the code above, whenever a proxy function (called directly or call and apply) is executed, it is blocked by the apply method.
In addition, the Reflect.apply method is called directly and is blocked.
Reflect.apply(proxy, null, [9, 10]) // 38
has
method is used
HasProperty
operations, which take effect when determining whether an object has a property.
A typical operation is the in operator.
The has method can accept two parameters, the target
目标对象
需查询的属性名
The following example uses the has method to hide certain properties that are not discovered by the in operator.
var handler = {
has (target, key) {
if (key[0] === '_') {
return false;
}
return key in target;
}
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
'_prop' in proxy // false
In the above code, if the first character of the property name of the original object is underlined, proxy.has returns false so that it is not discovered by the in operator.
If the original object is not configurable or can't be extended, has blocking will report an error.
var obj = { a: 10 };
Object.preventExtensions(obj);
var p = new Proxy(obj, {
has: function(target, prop) {
return false;
}
});
'a' in p // TypeError is thrown
In the code above, the obj object prohibits extensions, and the result is an error by using has blocking. That is, if a property is not configurable (or the target object is not extensible), the has method must not "hide" (that is, return false) the property of the target object.
It is important to note that the has method intercepts the HasProperty operation, not the HasOwnProperty operation, which means that the has method does not determine whether a property is a property of the object itself or an inherited property.
Also, although for... T he in loop also uses the in operator, but has intercepts the pair for... The in loop does not take effect.
let stu1 = {name: '张三', score: 59};
let stu2 = {name: '李四', score: 99};
let handler = {
has(target, prop) {
if (prop === 'score' && target[prop] < 60) {
console.log( ${target.name} 不及格 );
return false;
}
return prop in target;
}
}
let oproxy1 = new Proxy(stu1, handler);
let oproxy2 = new Proxy(stu2, handler);
'score' in oproxy1
// 张三 不及格
// false
'score' in oproxy2
// true
for (let a in oproxy1) {
console.log(oproxy1[a]);
}
// 张三
// 59
for (let b in oproxy2) {
console.log(oproxy2[b]);
}
// 李四
// 99
In the above code, has blocking only takes effect on the in operator, on the for... T he in loop does not take effect, resulting in non-compliant properties not being for... The in loop is excluded.
construct
method is used to intercept
new
and here's how to intercept objects.
var handler = {
construct (target, args, newTarget) {
return new target(...args);
}
};
The construct method can accept three parameters.
var p = new Proxy(function () {}, {
construct: function(target, args) {
console.log('called: ' + args.join(', '));
return { value: args[0] * 10 };
}
});
(new p(1)).value
// "called: 1"
// 10
construct
method must return an object
对象
an error will be reported.
var p = new Proxy(function() {}, {
construct: function(target, argumentsList) {
return 1;
}
});
new p() // 报错
// Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')
deleteProperty
method is used to
delete
operations, and if this method throws an error or returns false, the current property cannot be deleted by the delete command.
var handler = {
deleteProperty (target, key) {
invariant(key, 'delete');
delete target[key];
return true;
}
};
function invariant (key, action) {
if (key[0] === '_') {
throw new Error( Invalid attempt to ${action} private "${key}" property );
}
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop
// Error: Invalid attempt to delete private "_prop" property
In the code above, the deleteProperty method intercepts the delete operator, and removing the underlined property of the first character will report an error.
Note that the target object's own non-configurable properties cannot be deleted by the deleteProperty method, otherwise an error is reported.
defineProperty()
method
Object.defineProperty()
var handler = {
defineProperty (target, key, descriptor) {
return false;
}
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar' // 不会生效
In the code above, there is nothing inside the defineProperty() method that returns only false, causing the addition of new properties to always be invalid. Note that the false here is only used to prompt that the operation has failed and does not in itself prevent the addition of new properties.
Note that if the target object is not extensible, defineProperty() cannot add properties that do not exist on the target object, otherwise an error will be reported. Also, if a property of the target object is not written or configurable, the defineProperty() method must not change either setting.
getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor()
and returns a property description object or undefined.
var handler = {
getOwnPropertyDescriptor (target, key) {
if (key[0] === '_') {
return;
}
return Object.getOwnPropertyDescriptor(target, key);
}
};
var target = { _foo: 'bar', baz: 'tar' };
var proxy = new Proxy(target, handler);
Object.getOwnPropertyDescriptor(proxy, 'wat')
// undefined
Object.getOwnPropertyDescriptor(proxy, '_foo')
// undefined
Object.getOwnPropertyDescriptor(proxy, 'baz')
// { value: 'tar', writable: true, enumerable: true, configurable: true }
In the above code, the handler.getOwnPropertyDescriptor() method returns an underfined property name for the first character that is underlined.
getPrototypeOf()
primarily used to intercept
获取对象原型
Specifically, block these operations below.
Here's an example.
var proto = {};
var p = new Proxy({}, {
getPrototypeOf(target) {
return proto;
}
});
Object.getPrototypeOf(p) === proto // true
In the code above, the getPrototypeOf() method intercepts Object.getPrototypeOf() and returns the proto object.
Note that
getPrototypeOf()
method must be
对象
or
null
otherwise an error is reported.
In addition, if the target object is not extensible, the getPrototypeOf() method must return the prototype object of the target object.
isExtensible()
method
Object.isExtensible()
var p = new Proxy({}, {
isExtensible: function(target) {
console.log("called");
return true;
}
});
Object.isExtensible(p)
// "called"
// true
The above code sets the isExtensible() method, which outputs called when Object.isExtensible is called.
Note that the method can only return boolean values, otherwise the return values are automatically converted to Boolean values.
This method has a strong limitation, and its return value must be consistent with the isExtensible property of the target object, or an error will be thrown.
Object.isExtensible(proxy) === Object.isExtensible(target)
Here's an example.
var p = new Proxy({}, {
isExtensible: function(target) {
return false;
}
});
Object.isExtensible(p)
// Uncaught TypeError: 'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is 'true')
The ownKeys() method is used to intercept read operations on the object's own properties. Specifically, block the following operations.
The following is an example of intercepting Object.keys().
let target = {
a: 1,
b: 2,
c: 3
};
let handler = {
ownKeys(target) {
return ['a'];
}
};
let proxy = new Proxy(target, handler);
Object.keys(proxy)
// [ 'a' ]
The above code intercepts the Object.keys() operation on the target object, returning only the a property of a, b, and c of the three properties.
The following example is intercepting a property name where the first character is underlined.
let target = {
_bar: 'foo',
_prop: 'bar',
prop: 'baz'
};
let handler = {
ownKeys (target) {
return Reflect.ownKeys(target).filter(key => key[0] !== '_');
}
};
let proxy = new Proxy(target, handler);
for (let key of Object.keys(proxy)) {
console.log(target[key]);
}
// "baz"
Note that when using the Object.keys() method, three types of properties are automatically filtered by the ownKeys() method and are not returned.
let target = {
a: 1,
b: 2,
c: 3,
[Symbol.for('secret')]: '4',
};
Object.defineProperty(target, 'key', {
enumerable: false,
configurable: true,
writable: true,
value: 'static'
});
let handler = {
ownKeys(target) {
return ['a', 'd', Symbol.for('secret'), 'key'];
}
};
let proxy = new Proxy(target, handler);
Object.keys(proxy)
// ['a']
In the above code, in the ownKeys() method, the return of non-existent properties (d), Symbol values (Symbol.for('secret'), and non-traversable properties (key) are displayed, and the results are automatically filtered out.
The ownKeys() method can also intercept Object.getOwnPropertyNames().
var p = new Proxy({}, {
ownKeys: function(target) {
return ['a', 'b', 'c'];
}
});
Object.getOwnPropertyNames(p)
// [ 'a', 'b', 'c' ]
for... The in loop is also blocked by the ownKeys() method.
const obj = { hello: 'world' };
const proxy = new Proxy(obj, {
ownKeys: function () {
return ['a', 'b'];
}
});
for (let key in proxy) {
console.log(key); // 没有任何输出
}
In the above code, ownkeys() specifies that only the a and b properties are returned, and because obj does not have these two properties, for... The in loop does not have any output.
The ownKeys() method returns array members that can only be strings or Symbol values. If there are other types of values, or if the return is not an array at all, an error is reported.
var obj = {};
var p = new Proxy(obj, {
ownKeys: function(target) {
return [123, true, undefined, null, {}, []];
}
});
Object.getOwnPropertyNames(p)
// Uncaught TypeError: 123 is not a valid property name
In the code above, the ownKeys() method returns an array, but each array member is not a string or Symbol value, so it is misaled.
If the target object itself contains an unconfigurable property, the property must be returned by the ownKeys() method or an error will be reported.
var obj = {};
Object.defineProperty(obj, 'a', {
configurable: false,
enumerable: true,
value: 10 }
);
var p = new Proxy(obj, {
ownKeys: function(target) {
return ['b'];
}
});
Object.getOwnPropertyNames(p)
// Uncaught TypeError: 'ownKeys' on proxy: trap result did not include 'a'
In the code above, the a property of the obj object is not configurable, and the array returned by the ownKeys() method must contain a or an error will be reported.
Also, if the target object is not extensible, the array returned by the ownKeys() method must contain all the properties of the original object and must not contain extra properties, otherwise an error is reported.
var obj = {
a: 1
};
Object.preventExtensions(obj);
var p = new Proxy(obj, {
ownKeys: function(target) {
return ['a', 'b'];
}
});
Object.getOwnPropertyNames(p)
// Uncaught TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible
In the above code, the obj object is not extensable, and the array returned by the ownKeys() method contains the excess properties of the obj object b, which results in an error.
preventExtensions()
method
Object.preventExtensions()
The method must return a Boolean value
布尔值
will automatically be converted to a Boolean value.
This method has a limitation that only when the target object is not scalable (i.e. Object.isExtensible (proxy) is false), proxy.preventExtensions can return true, otherwise an error will be reported.
var proxy = new Proxy({}, {
preventExtensions: function(target) {
return true;
}
});
Object.preventExtensions(proxy)
// Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible
In the code above, the proxy.preventExtensions() method returns true, but object.isExtensible (proxy) returns true and therefore errors are reported.
To prevent this problem, object.preventExtensions() is usually called once in the proxy.preventExtensions() method.
var proxy = new Proxy({}, {
preventExtensions: function(target) {
console.log('called');
Object.preventExtensions(target);
return true;
}
});
Object.preventExtensions(proxy)
// "called"
// Proxy {}
setPrototypeOf()
is mainly
Object.setPrototypeOf()
method.
Here's an example.
var handler = {
setPrototypeOf (target, proto) {
throw new Error('Changing the prototype is forbidden');
}
};
var proto = {};
var target = function () {};
var proxy = new Proxy(target, handler);
Object.setPrototypeOf(proxy, proto);
// Error: Changing the prototype is forbidden
In the code above, any modification of the target's prototype object will result in an error.
Note that the method can only return boolean values, otherwise they will be automatically converted to Boolean values. In addition, if the target object is not extensible, the setPrototypeOf() method must not change the prototype of the target object.
Proxy.revocable()
method returns an instance of the Proxy that can
Proxy
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
The Proxy.revocable() method returns an object, the proxy property of which is a Proxy instance, and the revoke property is a function that cancels the Proxy instance. In the code above, an error is thrown when the revoke function is executed and the Proxy instance is accessed.
One use scenario for Proxy.revocable() is that the target object is not allowed direct access, must be accessed through a proxy, and once the access is complete, the proxy is withdrawn and no further access is allowed.
Although Proxy can proxy access to the target object, it is not a transparent proxy for the target object, i.e. it cannot guarantee that it will behave consistently with the target object without any interception. The main reason is that in the case of the Proxy proxy, the this keyword inside the target object points to the Proxy proxy.
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
In the above code, once the proxy proxy target .m, the this inside the latter points to the proxy, not the target.
Here's an example of a change in this pointing that prevents Proxy from acting as a target object.
const _name = new WeakMap();
class Person {
constructor(name) {
_name.set(this, name);
}
get name() {
return _name.get(this);
}
}
const jane = new Person('Jane');
jane.name // 'Jane'
const proxy = new Proxy(jane, {});
proxy.name // undefined
In the code above, the name property of the target object jane is actually saved on top of the external WeakMap object _name, distinguished by the this key. Because this points proxy.name proxy when accessed through the data, the value cannot be obtained, so the undefined is returned.
In addition, some of the internal properties of native objects can only be obtained through the correct this, so Proxy cannot represent the properties of these native objects.
const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);
proxy.getDate();
// TypeError: this is not a Date object.
In the code above, the getDate() method can only be obtained on top of the Date object instance, and if this is not a Date object instance, an error will be reported. At this point, this binds the original object and solves the problem.
const target = new Date('2015-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.getDate() // 1
Proxy
object can intercept any property of the target object, which makes it a good fit for clients that write Web services.
const service = createWebService('http://example.com/data');
service.employees().then(json => {
const employees = JSON.parse(json);
// ···
});
The code above creates a new interface for a Web service that returns a variety of data. Proxy can intercept any property of this object, so you don't have to write an appropriate method for each type of data, just write a Proxy intercept.
function createWebService(baseUrl) {
return new Proxy({}, {
get(target, propKey, receiver) {
return () => httpGet(baseUrl + '/' + propKey);
}
});
}
Similarly, Proxy can also be used to implement the ORM layer of the database.