- Оригинальный адрес:Using Proxy to Track Javascript Class
- Оригинальный автор:Amir Harel
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:SHERlocked93
- Корректор:salomezhang, cyuamber
Photo by Fabian Grohs on Unsplash
Прокси-объект (Proxy) — очень крутая, но малоизвестная функция ES6. Хотя эта функция существует уже давно, я хотел бы немного объяснить ее в этой статье и проиллюстрировать ее использование на примере.
Что такое прокси
Как простое и скучное определение на MDN:
ProxyОбъекты используются для определения пользовательского поведения для основных операций (таких как поиск свойств, присвоение, перечисление, вызовы функций и т. д.).
Хотя это хорошее резюме, я действительно не понимаю, что Proxy может сделать и что он может сделать для нас.
Во-первых, концепция прокси исходит из метапрограммирования. Проще говоря, метапрограммирование — это код, который позволяет нам запускать написанный нами код приложения (или ядра). Например, пресловутыйevalФункции позволяют нам выполнять строковый код как исполняемый код, что является областью метапрограммирования.
ProxyAPI позволяет нам создать промежуточный слой между объектом и его объектами-потребителями, эта функция дает нам возможность контролировать объект, например решать, как с ним поступить.getа такжеsetДаже когда нет обычаев времени для доступа к объектам свойства того, что мы можем сделать.
var p = new Proxy(target, handler);
ProxytargettargetОбъекты ведут себя по-разномуhandlerобъект. Вы можете установить следующие элементы перехвата:
-
has— Перехватinработать. Например, вы можете использовать его, чтобы скрыть некоторые свойства объекта. -
get— Используется для перехватачитатьработать. Вы можете использовать его для возврата значений по умолчанию, например, при попытке прочитать несуществующее свойство. -
set- используется для перехватаназначатьработать. Например, при присвоении значения свойству можно добавить логику проверки.Если проверка не пройдена, можно выдать ошибку. -
apply- используется для перехватаФункция Call.работать. Например, вы можете поместить все вызовы функций вtry/catchв блоке операторов.
Это только часть перехвата, вы можете найти его на MDNполный список.
Вот простой пример использования 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
Как видите, мы можем использовать Proxy для проверки значения, присвоенного проксируемому объекту.
Отладка с прокси
Чтобы продемонстрировать возможности Proxy на практике, я создал простую библиотеку мониторинга для мониторинга данного объекта или класса.Элементы мониторинга следующие:
- время выполнения функции
- Вызывающий функцию или посетитель свойства
- Подсчитайте количество обращений к каждой функции или свойству.
Это достигается путем вызова метода, вызываемого при доступе к любому объекту, классу или даже функции.proxyTrackфункция для завершения.
Если вы хотите следить за тем, кому присваивается значение свойства объекта, или выполнять функцию, как долго, сколько раз выполнять, эта библиотека полезна, кем выполняется. Я знаю, что могут быть другие лучшие инструменты для достижения вышеуказанных функций, но здесь я создал эту библиотеку, чтобы использовать этот 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 параметра: первый — это объект/класс для мониторинга, а второй — объект конфигурации, для которого будет установлено значение по умолчанию, если оно не будет передано. Давайте посмотрим, как выглядит значение по умолчанию для этого элемента конфигурации:
const defaultOptions = {
trackFunctions: true,
trackProps: true,
trackTime: true,
trackCaller: true,
trackCount: true,
stdout: null,
filter: null,
};
Как видите, вы можете отслеживать свои цели, настроив нужные вам элементы мониторинга. Например, если вы хотите вывести результат, вы можетеconsole.logназначатьstdout.
Также возможно назначениеfilterФункция обратного вызова из определения того, какую информацию контролировать на выходе. Вы получите информацию о мониторинге, включая объект, и если вы хотите сохранить эту информацию,true, иначе возвратfalse.
Использование proxyTrack в React
Поскольку компоненты React на самом деле тоже являются классами, вы можете передатьproxyTrackчтобы следить за ним в режиме реального времени. Например:
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Объект наблюдения перехватчика. Ниже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 создаст локальную копию свойства, поэтому изменение назначения не повлияет на другие экземпляры класса. , поведение. Вот почему просто проксировать прототип недостаточно.
кликните сюдаСм. полный код.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.
PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~
Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~