- Оригинальный адрес:Using Proxy to Track Javascript Class
- Оригинальный автор:Amir Harel
- Перевод с:Команда облачных переводчиков Alibaba
- Ссылка на перевод:GitHub.com/рассветные команды/…
- Переводчик:пасти
- Корректор:также дерево
ProxyES6 объект в крутых и малоизвестных фичах. Хотя он существует уже довольно давно, я хотел написать эту статью и объяснить его функции, а также на реальном примере проиллюстрировать, как его использовать.
The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc). Proxy
Хотя это довольно много суммы очень всеобъемлющена, но всякий раз, когда прочитайте его, я не очень понял свою роль, это помогает.
Во-первых, прокси от концепции метапрограммирования (метапрограммирования) в мире. Проще говоря, это позволяет нам использовать метапрограммирование.我们编写的应用程序(或核心)代码код. Например, пресловутый код, позволяющий оценивать строковый код как исполняемый код.eval 函数Это относится к области метапрограммирования.
ProxyAPI позволяют нам создать своего рода слой между объектом и его потребляющей сущностью, что позволяет нам контролировать поведение этого объекта, например решать, как это делается.getа такжеsetповедение и даже решать, что делать, когда кто-то пытается получить доступ к неопределенному свойству в объекте.
Proxy API
var p = new Proxy(target, handler);
Proxyприобретение объектаtargetа такжеhandler, чтобы зафиксировать различные варианты поведения целевого объекта. Ниже перечислены некоторые параметры, которые вы можете установить.陷阱:
-
has- захватыватьinоператор.例如,这将允许您隐藏对象的某些属性。 -
get- захватывать获取对象中属性的值работать.例如,如果此属性不存在,这将允许您返回一些默认值。 -
set- Захватывать设置对象中属性的值работать.例如,这将允许您验证设置为属性的值,并在值无效时抛出异常。 -
applytry/catchБлокировать.
Давайте посмотрим на простой пример использования Proxy для аутентификации:
const Car = {
maker: 'BMW',
year: '2018,
};
const proxyCar = new Proxy(Car, {
set(obj, prop, value) {
if (prop === 'maker' && value.length < 1) {
throw new Error('Invalid maker');
}
if (prop === 'year' && typeof value !== 'number') {
throw new Error('Invalid year');
}
obj[prop] = value;
return true;
}
});
proxyCar.maker = ''; // throw exception
proxyCar.year = '1999'; // throw exception
Как видите, мы можем正在设置到代理对象中的值аутентификация.
Отладка с прокси
Чтобы показать мощность прокси, я создал простую библиотеку отслеживания, которая отслеживает данную对象/类из следующих:
- время выполнения функции
- вызывающая сторона каждого метода или свойства
- Количество вызовов на метод или свойство
Это делается путем вызова любого объекта, класса или даже функции,proxyTrackфункция реализована.
Это может быть полезно, если вы хотите знать, кто изменяет значение в объекте, или как долго и сколько раз была вызвана функция и кто ее вызвал. Я знаю, что, вероятно, есть лучшие инструменты для этого, но я создал эту библиотеку только для того, чтобы опробовать Proxy API.
Использование прокси-трека
Во-первых, давайте посмотрим, как его использовать:
function MyClass() {}
MyClass.prototype = {
isPrime: function() {
const num = this.num;
for(var i = 2; i < num; i++)
if(num % i === 0) return false;
return num !== 1 && num !== 0;
},
num: null,
};
MyClass.prototype.constructor = MyClass;
const trackedClass = proxyTrack(MyClass);
function start() {
const my = new trackedClass();
my.num = 573723653;
if (!my.isPrime()) {
return `${my.num} is not prime`;
}
}
function main() {
start();
}
main();
Если мы выполним этот код, вы увидите в консоли:
MyClass.num is being set by start for the 1 time
MyClass.num is being get by isPrime for the 1 time
MyClass.isPrime was called by start for the 1 time and took 0 mils.
MyClass.num is being get by start for the 2 time
proxyTrackПередайте 2 аргумента: первый — объект/класс для отслеживания, второй —option, если не передано, будет установлено значение по умолчаниюoption. Посмотрим на второй параметр:
const defaultOptions = {
trackFunctions: true,
trackProps: true,
trackTime: true,
trackCaller: true,
trackCount: true,
stdout: null,
filter: null,
};
Как видите, вы можете контролировать, что отслеживается, устанавливая соответствующие флаги. Если вы хотите управлять выходом, чтобы сначала перейти в другое место, а затемconsole.logstdout.
Если входящийfilterФункция обратного вызова, которая также может управлять выводами сообщениями трассировки. Вы получите объект, который содержит информацию о трассировке данных, должна вернутьсяtrueВ послании бронирования или возвратаfalseЕго можно игнорировать.
Использование proxyTrack в React
компоненты реакции на самом деле являются классами, поэтому вы можете отслеживать класс, чтобы проверять его в режиме реального времени. Например:
class MyComponent extends Component{...}
export default connect(mapStateToProps)(proxyTrack(MyComponent, {
trackFunctions: true,
trackProps: true,
trackTime: true,
trackCaller: true,
trackCount: true,
filter: (data) => {
if( data.type === 'get' && data.prop === 'componentDidUpdate') return false;
return true;
}
}));
Как видите, вы можете отфильтровать сообщения, которые могут не иметь к вам отношения или которые могут загромождать консоль.
Реализация ProxyTrack
Давайте взглянемproxyTrackКак реализован метод.
Во-первых, сама функция:
export function proxyTrack(entity, options = defaultOptions) {
if (typeof entity === 'function') return trackClass(entity, options);
return trackObject(entity, options);
}
Здесь ничего особенного, мы просто вызываем соответствующую функцию.
посмотриtrackObject:
function trackObject(obj, options = {}) {
const { trackFunctions, trackProps } = options;
let resultObj = obj;
if (trackFunctions) {
proxyFunctions(resultObj, options);
}
if (trackProps) {
resultObj = new Proxy(resultObj, {
get: trackPropertyGet(options),
set: trackPropertySet(options),
});
}
return resultObj;
}
function proxyFunctions(trackedEntity, options) {
if (typeof trackedEntity === 'function') return;
Object.getOwnPropertyNames(trackedEntity).forEach((name) => {
if (typeof trackedEntity[name] === 'function') {
trackedEntity[name] = new Proxy(trackedEntity[name], {
apply: trackFunctionCall(options),
});
}
});
}
Как видите, если нам нужно отслеживать свойства объекта, мы используемgetа такжеsetЛовушка создает прокси-объект. Вот код, который устанавливает ловушку:
function trackPropertySet(options = {}) {
return function set(target, prop, value, receiver) {
const { trackCaller, trackCount, stdout, filter } = options;
const error = trackCaller && new Error();
const caller = getCaller(error);
const contextName = target.constructor.name === 'Object' ? '' : `${target.constructor.name}.`;
const name = `${contextName}${prop}`;
const hashKey = `set_${name}`;
if (trackCount) {
if (!callerMap[hashKey]) {
callerMap[hashKey] = 1;
} else {
callerMap[hashKey]++;
}
}
let output = `${name} is being set`;
if (trackCaller) {
output += ` by ${caller.name}`;
}
if (trackCount) {
output += ` for the ${callerMap[hashKey]} time`;
}
let canReport = true;
if (filter) {
canReport = filter({
type: 'get',
prop,
name,
caller,
count: callerMap[hashKey],
value,
});
}
if (canReport) {
if (stdout) {
stdout(output);
} else {
console.log(output);
}
}
return Reflect.set(target, prop, value, receiver);
};
}
trackClassМне больше интересны функции:
function trackClass(cls, options = {}) {
cls.prototype = trackObject(cls.prototype, options);
cls.prototype.constructor = cls;
return new Proxy(cls, {
construct(target, args) {
const obj = new target(...args);
return new Proxy(obj, {
get: trackPropertyGet(options),
set: trackPropertySet(options),
});
},
apply: trackFunctionCall(options),
});
}
函数原型构造函数Создайте ловушку, потому что мы хотим иметь возможность захватывать в классе свойства, которые не исходят из прототипа.
Не забывайте, что даже если вы определяете свойство на уровне прототипа, как только вы установите для него значение, JavaScript создаст локальную копию свойства, поэтому все остальные экземпляры этого класса не изменятся вместе с ним. Вот почему прокси-архетипов недостаточно.
ты сможешьздесьСм. полный код.