Волшебный прокси JavaScript

внешний интерфейс JavaScript
Волшебный прокси JavaScript

предисловие

Что такое прокси?

Когда вы были в начальной школе, Ли Сяохун пришел к вам домой и попросил вас пойти поиграть.Первым ответом были не вы, а ваша мама: «Ван Сяомин делает домашнее задание дома, я не пойду сегодня! "

Когда он был в средней школе, Чжао Эрху забрал своих младших братьев после школы и ждал, чтобы побить тебя у школьных ворот.Впереди шел не ты, а отец Эрху: «Я провалил экзамен и научился притворяться гангстер!" Ударь по жиру.

Иду в колледж, лежу на кровати в общежитии, очень голоден. Когда ты выходишь за едой и объясняешь, что не хочешь добавлять больше лука и чеснока, это не ты приносишь прямо в постель, а брат-курьер.

Это прокси.

Что такое прокси JavaScript?

На официальном иностранном языке этоProxy:

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

С помощью Proxy мы можем перехватывать и изменять почти все основные операции объекта, включая, помимо прочего, поиск свойств, присваивание, перечисление, вызовы функций и т. д.

В жизни через прокси мы можем автоматически блокировать приглашение Сяохуна, автоматически отгонять угрозу Эрху, автоматически покупать чистую еду и приносить ее в постель. В мире JavaScript прокси-серверы могут сделать что-то подобное для вас, так что давайте подумаем об этом вместе.

Первое знакомство с агентом: Hello World

Взяв в качестве примера опыт начальной школы, в моем сердце мне нравится Сяохун, поэтому мы определяем:

const me = { name: '小明', like: '小红' }

В это время, если вы позвонитеconsole.log(me.like), результат должен быть小红. Однако в жизни все не так, вокруг тебя, как несовершеннолетнего, всегда есть разные агенты, например:

const meWithProxy = new Proxy(me, {
  get(target, prop) {
    if (prop === 'like') {
      return '学习';
    }
    return target[prop];
  }
});

В это время, если вы позвонитеconsole.log(me.like)еще小红, потому что правда не лжет. но когда мы звонимconsole.log(meWithProxy.like), это будет позорный выход学习, скажи всем, что нам нравится学习.

Маленькое испытание: не останавливай мою музыку

Только что мы вкратце узнали, что прокси может перехватывать получение атрибутов объекта, скрывать реальное значение атрибута и возвращать результат, который хочет вернуть прокси, так что насчет присвоения атрибутов объекта? Давайте взглянем.

Предположим, вы слушаете музыку:

const me = { name: '小明', musicPlaying: true }

В этот момент, если мы выполнимme.musicPlaying = falseЭто легко останавливает вашу музыку, поэтому, если мы подключим прокси:

const meWithProxy = new Proxy(me, {
  set(target, prop, value) {
    if (prop === 'musicPlaying' && value !== true) {
      throw Error('任何妄图停止音乐的行为都是耍流氓!');
    }
    target[prop] = value;
  }
});

В этот момент, если мы выполнимme.musicPlaying = false, таблица будет безжалостно брошена:

> meWithProxy.musicPlaying = false
Error: 任何妄图停止音乐的行为都是耍流氓!
    at Object.set (repl:4:13)
>

Дайте волю волшебству: инкапсулируйте все RESTful API во вселенной

Теперь, когда мы знаем, что можем перехватывать операции чтения и записи свойств через прокси, что тогда? бесполезно?

Простой перехват операций чтения и записи атрибутов не дает много возможностей для игры, может быть, это удобно для выполнения какой-то работы по проверке присвоения атрибутов и так далее. Однако, возможно, вы не поняли удивительный секрет:Когда Proxy перехватывает операции чтения и записи свойства, ему все равно, существует ли свойство на самом деле!

То есть, используя Proxy, мы можем перехватить чтение свойств, которых не существует.

Подумайте дальше: используя прокси, мы можем динамически создавать возвращаемый результат в момент чтения свойства.

Однако свойства не ограничиваются строками, логическими значениями, свойства могут быть объектами, функциями, чем угодно.

До сих пор, что вы придумали?

не ожидал? Это не имеет значения! Основываясь на только что проведенном анализе, давайте инкапсулируем все RESTful API во вселенной с помощью следующих 17 строк кода!

import axios from 'axios';
const api = new Proxy({}, {
  get(target, prop) {
    const method = /^[a-z]+/.exec(prop)[0];
    const path = '/' + prop
          .substring(method.length)
          .replace(/([a-z])([A-Z])/g, '$1/$2')
          .replace(/\$/g, '/$/')
          .toLowerCase();
    return (...args) => { // <------ 返回动态构造的函数!
      const url = path.replace(/\$/g, () => args.shift());
      const options = args.shift() || {};
      console.log('Requesting: ', method, url, options);
      return axios({ method, url,  ...options });
    }
  }
});

После определения прокси-сервера API мы можем вызвать его следующим образом:

api.get()
// GET /

api.getUsers()
// 获取所有用户
// GET /users

api.getUsers$Books(42)
// 获取 ID 为 42 的用户的所有书籍
// GET /users/42/books

api.getUsers$Books(42, { params: { page: 2 } })
// 获取 ID 为 42 的用户的所有书籍的第二页
// GET /users/42/books?page=2

api.postUsers({ data: { name: '小明' } })
// 创建名字为 小明 的用户
// POST /users Payload { name: '小明' }

Все вышеперечисленные функции динамически генерируются волшебной рукой агента в момент вашего звонка, чтобы мы могли использовать их по своему усмотрению.

Просто, элегантно, вау~ это круто!

Ultimate Magic: прочитайте секреты магии агента

Пока что мы используем Proxy только для преобразования операций получения и присвоения свойств объектов, а для Proxy это лишь верхушка айсберга.

Основной синтаксис Proxy выглядит следующим образом:

new Proxy(target, handler)

вtargetЭто объект, который должен быть проксирован (например: кто-то, кто хочет выйти и поиграть с Сяохуном)me),handlerЭто волшебная рука агента, используемая для перехвата и преобразованияtargetповедение.

дляhandlerобъект, мы только что использовалиget,setна самом деле существует 13 делегируемых операций:

  • handler.getPrototypeOf()

    Запускается при чтении прототипа прокси-объекта, например, при выполнении Object.getPrototypeOf(proxy) .

  • handler.setPrototypeOf()

    Запускается при установке прототипа прокси-объекта, например, при выполнении Object.setPrototypeOf(proxy, null) .

  • handler.isExtensible()

    Запускается при определении того, является ли прокси-объект расширяемым, например, при выполнении Object.isExtensible(proxy) .

  • handler.preventExtensions()

    Запускается, когда прокси-объект становится нерасширяемым, например, при выполнении Object.preventExtensions(proxy) .

  • handler.getOwnPropertyDescriptor()

    Запускается при получении описания свойства для свойства прокси-объекта, например, при выполнении Object.getOwnPropertyDescriptor(proxy, "foo") .

  • handler.defineProperty()

    Запускается при определении описания свойства для свойства прокси-объекта, например, при выполнении Object.defineProperty(proxy, "foo", {}) .

  • handler.has()

    Запускается при определении того, имеет ли прокси-объект свойство, например, при выполнении «foo» в прокси.

  • handler.get()

    Запускается при чтении свойства прокси-объекта, например при выполнении proxy.foo.

  • handler.set()

    Запускается, когда свойству прокси-объекта присваивается значение, например, когда proxy.foo = 1 .

  • handler.deleteProperty()

    Запускается при удалении свойства прокси-объекта, например при выполнении удаления proxy.foo.

  • handler.ownKeys()

    Запускается при получении всех ключей свойств прокси-объекта, например, при выполнении Object.getOwnPropertyNames(proxy) .

  • handler.apply()

    Запускается при вызове прокси-объекта, целевым объектом которого является функция, например, при выполнении proxy().

  • handler.construct()

    Запускается при создании экземпляра прокси-объекта, целевым объектом которого является конструктор, например, при выполнении new proxy().

Вышеуказанные 13 прокси-операций читатели должны изучить и попрактиковаться самостоятельно, прежде чем отправиться в окончательное волшебное путешествие.

Одноклассники, я с оптимизмом смотрю на вас.


Ссылка на ссылку:


Обратите внимание на публичный аккаунт WeChat: KnownsecFED, получайте больше качественной галантереи по коду!