当前位置:网站首页>Proxy and reflection

Proxy and reflection

2022-06-12 20:12:00 Flying Behemoth

《JavaScript Advanced programming 》 Reading notes

Basis of agency

Create an empty proxy

const target = {
    id: 'target'
};
const handler = {};
const proxy = new Proxy(target, handler);

Revocable agent

revoke Undo function is idempotent .

const target = {
    id: 'target'
};
const handle = {
    get() {
        return 'iiiiiii';
    }
}
const {proxy, revoke} = Proxy.revocable(target, handle);
revoke();

Practical reflection API

Reflection method uses object control and operation with granularity

  1.   Reflection API Not limited to capture handlers
  2. Most reflections API Method in Object There are corresponding methods on the type

1. Status mark

Many reflection methods return Boolean values called status tags , Indicates whether the intended operation is successful .

const o = {}
try {
    Object.defineProperty(o, 'foo', 'bar');
    console.log('success');
} catch(e) {
    console.log('fail');
}

//  After refactoring 
if(Reflect.defineProperty(o, 'foo', {value:'bar'})) {
    console.log('success');
}else {
    console.log('fail');
}

The following reflection methods provide status markers :

  • Reflect.defineProperty()
  • Reflect.preventExtensions()
  • Reflect.setPrototypeOf()
  • Reflect.set()
  • Reflect.deleteProperty()

2. Replace the operator with a first-order function

The following reflection methods provide operations that can only be done by operators

  • Reflect.get() You can override the object property access operator
  • Reflect.set() Can replace = Assignment operator
  • Reflect.has() Can replace in Operator or with()
  • Reflect.deleteProperty() Can replace delete The operator
  • Reflect.constructor() Can replace new The operator

3. Safely apply functions

Through apply Method calls the function , The called function may also define its own apply attribute ( It's a long shot ). To get around this problem , as follows :

//  Method 1 : Use definition in Function  Archetype apply  Method 
Function.prototype.apply.call(myFunc, thisVal, argumentList);
//  Method 2 
Reflect.apply(myFunc, thisVal, argumentList);

Acting for another agent

Build a multi-layer interception network on a target object

const target = {
    id: 'target'
};
const firstProxy = new Proxy(target, {
    get() {
        console.log('first Proxy');
        return Reflect.get(...arguments);
    }
});
const secondProxy = new Proxy(firstProxy, {
    get() {
        console.log('second Proxy');
        return Reflect.get(...arguments);
    }
});
console.log(secondProxy.id);
// second Proxy
// first Proxy
// target

Problems and shortcomings of agency

  • In the agency this
  • Agent and internal slot

Proxy traps and reflection methods

get()

get() The catcher is called in the operation to get the property value . Corresponding reflection API by Reflect.get()

const myTarget = {};
const proxy = new Proxy(myTarget, {
    get(target, property, receiver) {
        console.log('get()');
        return Reflect.get(...arguments);
    }
});
proxy.foo;    // get()
  1. Return value : The return value is unlimited
  2. Intercept operation
    1. proxy.property
    2. proxy[property]
    3. Object.create(proxy)[property]
    4. Reflect.get(proxy, property, receiver)
  3. Catcher handler parameters
    1. target: Target audience
    2. property: String key property on the referenced target object
    3. receiver: Proxy objects or objects that inherit proxy objects
  4. The catcher invariant
    If target.property Not writable and not configurable , The value returned by the handler must be the same as target.property matching .
    If target.property Not configurable and [[Get]] Features for undefined, The return value of the handler must be undefined

set()

set() The catcher will be called in the operation of setting the property value . Corresponding reflection API by Reflect.set()

const myTarget = {};
const proxy = new Proxy(myTarget, {
    set(target, property, value, receiver) {
        console.log('set()');
        return Reflect.set(...arguments)
    }
});
proxy.foo = 'bar';
// set()
  1. Return value : return true It means success ; return false It means failure , Throw in strict mode TypeError.
  2. Intercept operation
    1. proxy.property = value
    2. proxy[property] = value
    3. Object.create(proxy)[property] = value
    4. Reflect.set(proxy, property, receiver)
  3. Catcher handler parameters
    1. target: Target audience
    2. property: String key property on the referenced target object
    3. value: The value to assign to the attribute
    4. receiver: Receive the object originally assigned
  4. The catcher invariant
    If target.property Not writable and not configurable , The value of the target attribute cannot be modified .
    If target.property Not configurable and [[Set]] Features for undefined, The value of the target attribute cannot be modified .

has()

If target.property Not configurable and [[Set]] Features for undefined, The value of the target attribute cannot be modified .

const myTarget = {};
const proxy = new Proxy(myTarget, {
    has(target, property) {
        console.log('has()');
        return Reflect.has(...arguments)
    }
});
'foo' in proxy;
// has()
  1. Return value : Must return a Boolean value , Indicates whether the attribute exists . Returning non Boolean values will be transformed into Boolean values
  2. Intercept operation
    1. property in proxy
    2. property in Object.create(proxy)
    3. with(proxy) {(property);}
    4. Reflect.has(proxy, property)
  3. Catcher handler parameters
    1. target: Target audience
    2. property: String key property on the referenced target object
  4. The catcher invariant
    If target.property Exists and is not configurable , The handler must return true.
    If target.property Exists and the target object is not extensible , The handler must return true.

defineProperty()

defineProperty() The catcher will be in Object.defineProperty() In the called . Corresponding reflection API Method is
Reflect.defineProperty().

const myTarget = {};
const proxy = new Proxy(myTarget, {
    defineProperty(target, property, descriptor) {
        console.log('defineProperty()');
        return Reflect.defineProperty(...arguments)
    }
});
Object.defineProperty(proxy, 'foo', { value: 'bar' });
// defineProperty()
  1. Return value : Must return a Boolean value , Indicates whether the attribute was successfully defined . Returning non Boolean values will be transformed into Boolean values .
  2. Intercept operation
    1. Object.defineProperty(proxy, property, descriptor)
    2. Reflect.defineProperty(proxy, property, descriptor)
  3. Catcher handler parameters
    1. target: Target audience
    2. property: String key property on the referenced target object
    3. descriptor: Contains optional enumerable、 configurable、 writable、 value、 get and set
      Defined objects .
  4. The catcher invariant
    If the target object is not extensible , You cannot define the attribute .
    If the target object has a configurable property , You cannot add a non configurable property with the same name .
    If the target object has a non configurable property , You cannot add a configurable property with the same name .

getOwnPropertyDescriptor()

getOwnPropertyDescriptor() The catcher will be in Object.getOwnPropertyDescriptor() In the called . Corresponding reflection API Method is Reflect.getOwnPropertyDescriptor()

const myTarget = {};
const proxy = new Proxy(myTarget, {
    getOwnPropertyDescriptor(target, property) {
        console.log('getOwnPropertyDescriptor()');
        return Reflect.getOwnPropertyDescriptor(...arguments)
    }
});
Object.getOwnPropertyDescriptor(proxy, 'foo');
// getOwnPropertyDescriptor()
  1. Return value : Must return object , Or return... When the property does not exist undefined.
  2. Intercept operation
    1. Object.getOwnPropertyDescriptor(proxy, property)
    2. Reflect.getOwnPropertyDescriptor(proxy, property)
  3. Catcher handler parameters
    1. target: Target audience
    2. property: String key property on the referenced target object
  4. The catcher invariant
    If you have your own target.property Exists and is not configurable , The handler must return a that indicates the existence of the property
    object .
    If you have your own target.property Existing and configurable , Then the handler must return an object that represents the configurable property .
    If you have your own target.property Exist and target Non scalable , Then the handler must return a value indicating that the property exists
    The object in .
    If target.property Does not exist and target Non scalable , The handler must return undefined Denotes the genus
    Sex does not exist .
    If target.property non-existent , The handler cannot return an object that represents that the property is configurable .

deleteProperty()

deleteProperty() The catcher will be in delete Called in the operator . Corresponding reflection API Method is Reflect.
deleteProperty().
 

const myTarget = {};
const proxy = new Proxy(myTarget, {
    deleteProperty(target, property) {
        console.log('deleteProperty()');
        return Reflect.deleteProperty(...arguments)
    }
});
delete proxy.foo
// deleteProperty()
  1. Return value : Must return a Boolean value , Indicates whether the attribute is deleted successfully . Returning non Boolean values will be transformed into Boolean values .
  2. Intercept operation
    1. delete proxy.property
    2. delete proxy[property]
    3. Reflect.deleteProperty(proxy, property)
  3. Catcher handler parameters
    1. target: Target audience
    2. property: String key property on the referenced target object
  4. The catcher invariant
    If you have your own target.property Exists and is not configurable , Then the handler cannot delete this property .

ownKeys()

ownKeys() The catcher will be in Object.keys() And similar methods . Corresponding reflection API Method is Reflect.
ownKeys().

const myTarget = {};
const proxy = new Proxy(myTarget, {
    ownKeys(target) {
        console.log('ownKeys()');
        return Reflect.ownKeys(...arguments)
    }
});
Object.keys(proxy);
// ownKeys()
  1. Return value : An enumerable object containing a string or symbol must be returned .
  2. Intercept operation
    1. Object.getOwnPropertyNames(proxy)
    2. Object.getOwnPropertySymbols(proxy)
    3. Object.keys(proxy)
    4. Reflect.ownKeys(proxy)
  3. Catcher handler parameters
    1. target: Target audience
  4. The catcher invariant
    The returned enumerable object must contain target All non configurable self owned properties of .
    If target Non scalable , The returned enumerable object must accurately contain its own property key

getPrototypeOf()

getPrototypeOf() The catcher will be in Object.getPrototypeOf() In the called . Corresponding reflection API Method is
Reflect.getPrototypeOf()

const myTarget = {};
const proxy = new Proxy(myTarget, {
    getPrototypeOf(target) {
        console.log('getPrototypeOf()');
        return Reflect.getPrototypeOf(...arguments)
    }
});
Object.getPrototypeOf(proxy);
// getPrototypeOf()
  1. Return value : You must return an object or null
  2. Intercept operation
    1. Object.getPrototypeOf(proxy)
    2. Reflect.getPrototypeOf(proxy)
    3. proxy.__proto__
    4. Object.prototype.isPrototypeOf(proxy)
    5. proxy instanceof Object
  3. Catcher handler parameters
    1. target: Target audience
  4. The catcher invariant
    If target Non scalable , be Object.getPrototypeOf(proxy) The only valid return value is Object.
    getPrototypeOf(target) The return value of .

setPrototypeOf()

setPrototypeOf() The catcher will be in Object.setPrototypeOf() In the called . Corresponding reflection API Method is
Reflect.setPrototypeOf().

const myTarget = {};
const proxy = new Proxy(myTarget, {
    setPrototypeOf(target, prototype) {
        console.log('setPrototypeOf()');
        return Reflect.setPrototypeOf(...arguments)
    }
});
Object.setPrototypeOf(proxy, Object);
// setPrototypeOf()
  1. Return value : Must return a Boolean value , Indicates whether the prototype assignment is successful . Returning non Boolean values will be transformed into Boolean values .
  2. Intercept operation
    1. Object.setPrototypeOf(proxy)
    2. Reflect.setPrototypeOf(proxy)
  3. Catcher handler parameters
    1. target: Target audience
    2. prototype: target Alternative prototypes , If it is a top-level prototype, it is null.
  4. The catcher invariant
    If target Non scalable , Only valid prototype The parameter is Object.getPrototypeOf(target)
    The return value of .

isExtensible()

isExtensible() The catcher will be in Object.isExtensible() In the called . Corresponding reflection API Method is
Reflect.isExtensible().

const proxy = new Proxy(myTarget, {
    isExtensible(target) {
        console.log('isExtensible()');
        return Reflect.isExtensible(...arguments)
    }
});
Object.isExtensible(proxy);
// isExtensible()
  1. Return value : Must return a Boolean value , Express target Is it extensible . Returning non Boolean values will be transformed into Boolean values .
  2. Intercept operation
    1. Object.isExtensible(proxy)
    2. Reflect.isExtensible(proxy)
  3. Catcher handler parameters
    1. target: Target audience
  4. The catcher invariant
    If target Scalable , The handler must return true.
    If target Non scalable , The handler must return false.

preventExtensions()

preventExtensions() The catcher will be in Object.preventExtensions() In the called . Corresponding reflection API
Method is Reflect.preventExtensions().

const myTarget = {};
const proxy = new Proxy(myTarget, {
    preventExtensions(target) {
        console.log('preventExtensions()');
        return Reflect.preventExtensions(...arguments)
    }
});
Object.preventExtensions(proxy);
// preventExtensions()
  1. Return value : Must return a Boolean value , Express target Is it already non extensible . Returning non Boolean values will be transformed into Boolean values .
  2. Intercept operation
    1. Object.preventExtensions(proxy)
    2. Reflect.preventExtensions(proxy)
  3. Catcher handler parameters
    1. target: Target audience
  4. The catcher invariant
    If Object.isExtensible(proxy) yes false, The handler must return true.

apply()

apply() The catcher is called when the function is called . Corresponding reflection API Method is Reflect.apply().

const myTarget = () => {};
const proxy = new Proxy(myTarget, {
    apply(target, thisArg, ...argumentsList) {
        console.log('apply()');
        return Reflect.apply(...arguments)
    }
});
proxy();
// apply()
  1. Return value : The return value is unlimited .
  2. Intercept operation
    1. proxy(...argumentsList)
    2. Function.prototype.apply(thisArg, argumentsList)
    3. Function.prototype.call(thisArg, ...argumentsList)
    4. Reflect.apply(target, thisArgument, argumentsList
  3. Catcher handler parameters
    1. target: Target audience .
    2. thisArg: When calling a function this Parameters .
    3. argumentsList: List of parameters when calling a function
  4. The catcher invariant
    target: Must be a function object .

construct()

construct() The catcher will be in new Called in the operator . Corresponding reflection API Method is Reflect.construct().

const myTarget = function() {};
const proxy = new Proxy(myTarget, {
    construct(target, argumentsList, newTarget) {
        console.log('construct()');
        return Reflect.construct(...arguments)
    }
});
new proxy;
// construct()
  1. Return value : An object must be returned .
  2. Intercept operation
    1. new proxy(...argumentsList)
    2. Reflect.construct(target, argumentsList, newTarget)
  3. Catcher handler parameters
    1. target: Target constructor .
    2. argumentsList: The list of parameters passed to the target constructor .
    3. newTarget: Constructor originally called .
  4. The catcher invariant
    target Must be used as a constructor .

The proxy pattern

Trace attribute mode

Monitor when objects are accessed

const user = {
    name: 'Jake'
};
const proxy = new Proxy(user, {
    get(target, property, receiver) {
        console.log(`Getting ${property}`);
        return Reflect.get(...arguments);
    },
    set(target, property, value, receiver) {
        console.log(`Setting ${property}=${value}`);
        return Reflect.set(...arguments);
    }
});
proxy.name; // Getting name
proxy.age = 27; // Setting age=27

Hidden attribute

const hiddenProperties = ['foo', 'bar'];
const targetObject = {
    foo: 1,
    bar: 2,
    baz: 3
};
const proxy = new Proxy(targetObject, {
    get(target, property) {
        if (hiddenProperties.includes(property)) {
            return undefined;
        } else {
            return Reflect.get(...arguments);
        }
    },
    has(target, property) {
        if (hiddenProperties.includes(property)) {
            return false;
        } else {
            return Reflect.has(...arguments);
        }
    }
});
// get()
console.log(proxy.foo); // undefined
console.log(proxy.bar); // undefined
console.log(proxy.baz); // 3
// has()
console.log('foo' in proxy); // false
console.log('bar' in proxy); // false
console.log('baz' in proxy); // true

Attribute validation

const target = {
    onlyNumbersGoHere: 0
};
const proxy = new Proxy(target, {
    set(target, property, value) {
        if (typeof value !== 'number') {
            return false;
        } else {
            return Reflect.set(...arguments);
        }
    }
});
proxy.onlyNumbersGoHere = 1;
console.log(proxy.onlyNumbersGoHere); // 1
proxy.onlyNumbersGoHere = '2';
console.log(proxy.onlyNumbersGoHere); // 1

Function and constructor parameter validation

function median(...nums) {
    return nums.sort()[Math.floor(nums.length / 2)];
}
const proxy = new Proxy(median, {
    apply(target, thisArg, argumentsList) {
        for (const arg of argumentsList) {
            if (typeof arg !== 'number') {
                throw 'Non-number argument provided';
            }
        }
        return Reflect.apply(...arguments);
    }
});
console.log(proxy(4, 7, 1)); // 4
console.log(proxy(4, '7', 1));
// Error: Non-number argument provided
 Similarly , You can require that you must pass arguments to the constructor when instantiating :
class User {
    constructor(id) {
        this.id_ = id;
    }
}
const proxy = new Proxy(User, {
    construct(target, argumentsList, newTarget) {
        if (argumentsList[0] === undefined) {
            throw 'User cannot be instantiated without id';
        } else {
            return Reflect.construct(...arguments);
        }
    }
});
new proxy(1);
new proxy();
// Error: User cannot be instantiated without id

Data binding to objects

const userList = [];
class User {
    constructor(name) {
        this.name_ = name;
    }
}
const proxy = new Proxy(User, {
    construct() {
        const newUser = Reflect.construct(...arguments);
        userList.push(newUser);
        return newUser;
    }
});
new proxy('John');
new proxy('Jacob');
new proxy('Jingleheimerschmidt');
console.log(userList); // [User {}, User {}, User{}]
const userList = [];
function emit(newValue) {
    console.log(newValue);
}
const proxy = new Proxy(userList, {
    set(target, property, value, receiver) {
        const result = Reflect.set(...arguments);
        if (result) {
            emit(Reflect.get(target, property, receiver));
        }
        return result;
    }
});
proxy.push('John');
// John
proxy.push('Jacob');
// Jacob
原网站

版权声明
本文为[Flying Behemoth]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206122008596545.html