оригинал:blog.bit SRC.IO/Ах-практичный…
Переводчик: Front-end Xiaozhi
Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статья
GitHub
GitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.
Все говорили, что нет проекта для написания резюме, поэтому я помог вам найти проект, и это было с бонусом.【Учебник по строительству】.
Введение в прокси
использоватьProxy
, вы можете замаскировать кота под тигра. Ниже приведены примерно 6 примеров, которые, я надеюсь, убедят вас в том, что Proxy обеспечивает мощное метапрограммирование Javascript.
Хотя это не так распространено, как другие функции ES6,Proxy
имеет множество применений, в том числеПерегрузка оператора,моделирование объектов,Простое и гибкое создание API,событие смены объекта,четноеРаботает на внутренней адаптивной системе позади Vue 3.
Proxy
Поведение по умолчанию, используемое для изменения определенных операций, также можно понимать как настройку уровня перехвата перед целевым объектом.Все внешние обращения должны сначала проходить через этот уровень перехвата, поэтому он обеспечивает механизм фильтрации и перехвата внешнего доступа. Принцип слова агент, что может быть выражено здесь как «агент» для некоторых операций, что переводится как «агент».
ES6 изначально предоставляетProxy
конструктор для генерацииProxy
пример.
var proxy = new Proxy(target, handler);
Proxy
Все виды использования объектов указаны в форме выше. разные толькоhandle
запись параметров. вnew Proxy
используется для созданияProxy
пример,target
объект, который нужно перехватить,handle
Объект, используемый для настройки поведения перехвата.
Ниже приведен простейший пример Proxy, который представляет собой прокси с ловушками,get
ловушка, всегда возвращайся42
.
let target = {
x: 10,
y: 20
}
let hanler = {
get: (obj, prop) => 42
}
target = new Proxy(target, hanler)
target.x //42
target.y //42
target.x // 42
В результате объект вернет «42» для любой операции доступа к свойству. Это включает в себяtarget.x
,target['x']
,Reflect.get(target, 'x')
Ждать.
Однако ловушка Proxy, конечно, не ограничивается свойствами чтения. Это всего лишь одна из дюжины разных ловушек:
- handler.get
- handler.set
- handler.has
- handler.apply
- handler.construct
- handler.ownKeys
- handler.deleteProperty
- handler.defineProperty
- handler.isExtensible
- handler.preventExtensions
- handler.getPrototypeOf
- handler.setPrototypeOf
- handler.getOwnPropertyDescriptor
Вариант использования прокси
По умолчанию/"нулевое значение"
В языке Go естьнулевое значениеПредставление о том, что нулевое значение является неявным значением структуры по умолчанию для конкретного типа. Идея состоит в том, чтобы предоставить типобезопасные примитивные значения по умолчанию, или, говоря языком гофера, дать структурам полезное нулевое значение.
Хотя разные режимы создания поддерживают схожие функции, Javascript не может оборачивать объекты неявными инициализаторами. Значение по умолчанию неустановленного свойства в Javascript равноundefined
. Но Proxy может это изменить.
const withZeroValue = (target, zeroValue) => new Proxy(target, {
get: (obj, prop) => (prop in obj) ? obj[prop] : zeroValue
})
функцияwithZeroValue
Используется для обертывания целевого объекта. Возвращает значение свойства, если свойство установлено. В противном случае возвращается значение по умолчанию **"нулевое значение"**.
Технически этот метод либо неявен, но если мы расширяемсяwithZeroValue
, с булевым (false
), Number (0
), String (""
), Object ({}
), Множество ([]
) и другие соответствующие нулевые значения могут быть неявными.
let pos = {
x: 4,
y: 19
}
console.log(pos.x, pos.y, pos.z) // 4, 19, undefined
pos = withZeroValue(pos, 0)
console.log(pos.z, pos.y, pos.z) // 4, 19, 0
Одно из мест, где эта функция может быть полезна, — это системы координат. Библиотеки чертежей могут автоматически поддерживать 2D- и 3D-рендеринг в зависимости от формы данных. Вместо создания двух отдельных моделей всегдаz
По умолчанию0
вместоundefined
, что может иметь смысл.
массив отрицательных индексов
Получение последнего элемента в массиве в JS — это многословный и повторяющийся процесс, который может привести к ошибкам. Вот почему существует предложение TC39, определяющее атрибут удобства.Array.lastItem
чтобы получить и установить последний элемент.
Другие языки, такие как Python и Ruby, используют отрицательные групповые индексы, чтобы упростить доступ к последнему элементу. Например, вы можете просто использоватьarr[-1]
заменятьarr[arr.length-1]
Доступ к последнему элементу.
Отрицательные индексы также можно использовать в Javascript с использованием прокси.
const negativeArray = (els) => new Proxy(els, {
get: (target, propKey, receiver) => Reflect.get(target,
(+propKey < 0) ? String(target.length + +propKey) : propKey, receiver)
});
Важным примечанием является включениеhandler.getЛовушка упорядочивает все свойства. Для доступа к массиву нам нужно привести имя свойства кNumbers
, что можно сделать кратко, используя унарный оператор плюс.
Сейчас[-1]
получить доступ к последнему элементу,[-2]
Доступ к предпоследнему элементу и т. д.
const unicorn = negativeArray(['🐴', '🎂', '🌈']);
unicorn[-1] // '🌈'
скрытый атрибут
Хорошо известно, что в JS нет приватных свойств.Symbol
первоначально дляВведено путем включения частных свойств, но затем используйте что-то вродеObject.getOwnPropertySymbols
Такие методы отражения разбавлены, что делает их общедоступными.
Давнее соглашение состоит в том, чтобы называть частные свойства с начальным символом подчеркивания._
, эффективно помечая их как «недоступные».Prox
Предоставляет немного лучший способ маскировать эти свойства.
const hide = (target, prefix = '_') => new Proxy(target, {
has: (obj, prop) => (!prop.startsWith(prefix) && prop in obj),
ownKeys: (obj) => Reflect.ownKeys(obj)
.filter(prop => (typeof prop !== "string" || !prop.startsWith(prefix))),
get: (obj, prop, rec) => (prop in rec) ? obj[prop] : undefined
})
hide
функция оборачивает целевой объект и делаетin
оператор иObject.getOwnPropertyNames
и другие методы не могут получить доступ к свойствам со знаком подчеркивания.
let userData = hide({
firstName: 'Tom',
mediumHandle: '@tbarrasso',
_favoriteRapper: 'Drake'
})
userData._favoriteRapper // undefined
('_favoriteRapper' in userData) // false
Более полная реализация также включает в себя такие вещи, какdeleteProperty
а такжеdefineProperty
такие ловушки. Помимо замыканий, это, вероятно, самое близкое, что вы можете получить к действительно частным свойствам, поскольку к ним нельзя получить доступ путем перечисления, клонирования, доступа или изменения.
Однако они видны в консоли развития. Только закрытие может быть свободно от этой судьбы.
тайник
Нередко возникают трудности с синхронизацией состояния между клиентом и сервером. Данные могут меняться со временем, и трудно точно знать, когда нужно повторно синхронизировать логику.
Proxy
Включен новый метод: обертывание объектов свойствами аннулирования (и повторной синхронизации) по мере необходимости. Все попытки доступа к свойствам сначала проверяют политику кэширования, которая принимает решение вернуть то, что в данный момент находится в памяти, или выполнить какое-то другое действие.
const ephemeral = (target, ttl = 60) => {
const CREATED_AT = Date.now()
const isExpired = () => (Date.now() - CREATED_AT) > (ttl * 1000)
return new Proxy(target, {
get: (obj, prop) => isExpired() ? undefined : Reflect.get(obj, prop)
})
}
Эта функция является чрезмерным упрощением: она делает все свойства объекта недоступными через определенный период времени. Однако этот подход несложно расширить, установив время жизни (TTL) для каждого ресурса и обновив его после определенной продолжительности или количества посещений.
let bankAccount = ephemeral({
balance: 14.93
}, 10)
console.log(bankAccount.balance) // 14.93
setTimeout(() => {
console.log(bankAccount.balance) // undefined
}, 10 * 1000)
Этот пример просто делает баланс банковского счета недоступным через 10 секунд.
Перечисления и представления только для чтения
Эти примеры приходят изCsaba Hellinge 关于[代理用例][23]和[Mozilla黑客][24]的文章。方法是包装一个对象以防止扩展或修改。虽然
object.freeze` теперь предоставляет возможность отображать объекты как доступные только для чтения, но этот подход можно расширить, чтобы доступ к объектам перечисления с несуществующими свойствами лучше обрабатывал ошибки генерирования.
вид только для чтения
const NOPE = () => {
throw new Error("Can't modify read-only view");
}
const NOPE_HANDLER = {
set: NOPE,
defineProperty: NOPE,
deleteProperty: NOPE,
preventExtensions: NOPE,
setPrototypeOf: NOPE
}
const readOnlyView = target =>
new Proxy(target, NOPE_HANDLER)
представление перечисления
const createEnum = (target) => readOnlyView(new Proxy(target, {
get: (obj, prop) => {
if (prop in obj) {
return Reflect.get(obj, prop)
}
throw new ReferenceError(`Unknown prop "${prop}"`)
}
}))
Теперь мы можем создатьObject
, если вы попытаетесь получить доступ к несуществующему свойству сейчас вместо возвратаundefined
, но выдает исключение. Это облегчает обнаружение и устранение проблем на ранней стадии.
нашenum
Этот пример также является первым примером прокси на прокси, который подтверждает, что прокси является допустимым целевым объектом для другого прокси. Это способствует повторному использованию кода за счет объединения функций прокси.
let SHIRT_SIZES = createEnum({
S: 10,
M: 15,
L: 20
})
SHIRT_SIZES.S // 10
SHIRT_SIZES.S = 15
// Uncaught Error: Can't modify read-only view
SHIRT_SIZES.XL
// Uncaught ReferenceError: Unknown prop "XL"
Этот подход можно расширить, включив фиктивные методы.nameOf
, который возвращает заданныйenum
Имя свойства значения, имитирующее поведение в таких языках, как Javascript.
В то время как другие фреймворки и надмножества языков (например, TypeScript) предоставляютenum
type, но это решение уникально тем, что в нем используется простой Javascript, а не специальные инструменты сборки или транспонеры.
перегрузка оператора
Возможно, грамматически наиболее привлекательнымProxy
Варианты использования — это возможность перегружать операторы, такие как использованиеhandler.hasизinоператор.
in
Оператор используется для проверки того, находится ли указанное свойство в указанном объекте или в цепочке его прототипов. Но это также самый синтаксически элегантный перегруженный оператор. Этот пример определяет непрерывныйrange
функция сравнения чисел.
const range = (min, max) => new Proxy(Object.create(null), {
has: (_, prop) => (+prop >= min && +prop <= max)
})
В отличие от Python, который использует генераторы для сравнения с конечной последовательностью целых чисел, этот подход поддерживает десятичные сравнения, которые можно расширить для поддержки других числовых диапазонов.
const X = 10.5
const nums = [1, 5, X, 50, 100]
if (X in range(1, 100)) { // true
// ...
}
nums.filter(n => n in range(1, 10)) // [1, 5]
Хотя этот вариант использования не решает сложных проблем, он обеспечивает чистый, читаемый и пригодный для повторного использования код.
Кромеin
оператор, мы также можем перегрузитьdelete
а такжеnew
.
объект cookie
если вы когда-либо были сcookie
взаимодействовать, то вы должны иметь дело сdocument.cookie. Это необычный API, потому что API представляет собойString
, он читает всеcookie
,кточка с запятой разделена.
document.cookie
это строка, которая выглядит так:
_octo=GH1.2.2591.47507; _ga=GA1.1.62208.4087; has_recent_activity=1
Короче говоря, обработкаdocument.cookie
Это громоздко и подвержено ошибкам. Один из способов - использоватьПростая структура файлов cookie, который можно адаптировать для использования прокси.
const getCookieObject = () => {
const cookies = document.cookie.split(';').reduce((cks, ck) =>
({[ck.substr(0, ck.indexOf('=')).trim()]: ck.substr(ck.indexOf('=') + 1), ...cks}), {});
const setCookie = (name, val) => document.cookie = `${name}=${val}`;
const deleteCookie = (name) => document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
return new Proxy(cookies, {
set: (obj, prop, val) => (setCookie(prop, val), Reflect.set(obj, prop, val)),
deleteProperty: (obj, prop) => (deleteCookie(prop), Reflect.deleteProperty(obj, prop))
})
}
Эта функция возвращает объект пары ключ-значение, но прокси-парыdocument.cookie
Внесите все изменения, которые являются постоянными.
let docCookies = getCookieObject()
docCookies.has_recent_activity // "1"
docCookies.has_recent_activity = "2" // "2"
delete docCookies2["has_recent_activity"] // true
В 11 строках кода изменитеcookie
Обеспечивает лучшее взаимодействие, хотя в производственных средах также требуются дополнительные функции, такие как нормализация строк.
Детали определяют успех или неудачу, и Proxy не является исключением.
Polyfill
На момент написания этой статьи (май 2019 г.) у Proxy не былоПолный полифилл. Однако есть один, написанный Googlepartial polyfill for Proxy, который поддерживаетget
,set
,apply
а такжеconstruct trap
и работает в IE9+.
Это прокси?
Невозможно определить, является ли объект прокси
Согласно спецификации языка Javascript, невозможно определить, является ли объект прокси. Однако на Node 10+ можно использоватьutil.types.isProxyметод.
Какова цель?
Учитывая прокси-объект, невозможно получить или изменить целевой объект. Также невозможно получить или изменить объект обработчика.
Ближайшее приближение - статья Бена Наделя.Using Proxy to Dynamically Change THIS Binding, который использует пустой объект какProxy
Цели и замыкания для незаметного перераспределения объектовProxy
работать.
Прокси-примитив
new Proxy("To be, or not to be...", { })
// TypeError: Cannot create proxy with a non-object as target or handler
К сожалению, ограничение Proxy заключается в том, что целью должен быть объект. Это означает, что мы не можем напрямую использовать такие примитивы, как String. 😞
представление
Основным недостатком прокси является производительность. Зависит от браузера и использования, но для кода, критичного к производительности, прокси — не лучший подход. Конечно, можно измерить влияние и определить, перевешивают ли преимущества агента влияние на производительность.
Зачем использовать прокси?
Прокси предоставляет виртуализированный интерфейс для управления поведением любого целевого объекта. Это обеспечивает баланс между простотой и практичностью без ущерба для совместимости.
может быть, использоватьProxy
Самая убедительная причина заключается в том, что многие из приведенных выше примеров состоят всего из нескольких строк и могут быть легко объединены для создания сложных функций. В качестве последнего примера мы можем объединить функции из нескольких вариантов использования, чтобы создать доступную только для чтенияcookie
Object, объект возвращает значение по умолчанию несуществующего или «частного» файла cookie.
// document.cookie = "_octo=GH1.2.2591.47507; _ga=GA1.1.62208.4087; has_recent_activity=1"
let docCookies = withZeroValue(hide(readOnlyView(getCookieObject())), "Cookie not found")
docCookies.has_recent_activity // "1"
docCookies.nonExistentCookie // "Cookie not found"
docCookies._ga // "Cookie not found"
docCookies.newCookie = "1" // Uncaught Error: Can't modify read-only view
Я надеюсь, что эти примеры показали, что прокси — это не просто эзотерическая функция нишевого метапрограммирования в Javascript.
Ошибки, которые могут существовать после развертывания кода, не могут быть известны в режиме реального времени.Чтобы решить эти ошибки впоследствии, много времени тратится на отладку журнала.Кстати, я рекомендую всем полезный инструмент мониторинга ошибок.Fundebug.
общаться с
Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.