你好。核介是。

最近(2022/10/20),梦美做了一个小测验。

在阅读 Yumemi 的文章时,我发现了一个名为 Proxy 的对象。
正如文章标题所说,“已分配但未分配”,它是一个神秘的物体。
Proxy到底是谁?

在 Yumemi 的测验示例中山元太郎断然拒绝改名隆史例如,
其实Proxy有将数千行代码变成几行代码的魔法我也有

这次想介绍一下JavaScript中Proxy的神奇力量。

本来Proxy是一个难点的内容,但是和setTimeout一样,我想让初学者尽可能容易理解。我认为。

什么是代理?

简单的说,对象可以改造事物。
我将引用 Yumemi 的文章的例子。

const user = new Proxy(obj, { set: (target) => { target.name = "やめ太郎"; return true; } })
// 元のオブジェクト
const obj = { name: "" }

// Proxyオブジェクトを生成
const user = new Proxy(obj, { set: (target) => { target.name = "やめ太郎"; return true; } })

user.name = "たかし"

console.log(user.name)
// -> "やめ太郎"

console.log(obj.name)
// -> "やめ太郎"

在此示例中,即使您尝试将用户名更改为“Takashi”,如user.name = "たかし",也会被强制更改为“Yametaro”。
确切地对象user 被Proxy 改造我被迫

然后究竟什么是魔法改造?这就是它的感觉。

【JavaScript】数千行のコードを数行にしてしまう Proxy の魔力

在那之前,“代理”到底是什么意思?是不是?

正确答案是“副“就是这个意思。

您可能经常听说过“代理服务器”这个词。
充当客户端和后端服务器(如果有)之间的中介的“代理”服务器。

【JavaScript】数千行のコードを数行にしてしまう Proxy の魔力
(https://cybersecurity-jp.com/column/32171引用自)

Proxy 也代表.
看看上面 Yumemi 的代码示例。

// 元のオブジェクト
const obj = { name: "" }

// Proxyオブジェクトを生成
const user = new Proxy(obj, { set: (target) => { target.name = "やめ太郎"; return true; } })

在此示例中,Proxy 充当 target.name = "やめ太郎";,而 setter 为 obj 工作。

然后究竟什么是魔法改造?这就是它的感觉。

分辨率不是在逐渐提高吗?
现在回到“代理服务器”主题。

更详细地考虑代理服务器的角色。

在客户端和后端服务器之间中继的“代理”服务器

代理服务器中继。
即使你说接力,你是根据什么来做的?是吗?
例如,当客户端发送“给我页面信息”的请求时,它会向后端服务器发送请求以获取页面信息。
同样,当后端返回页面信息时,它会将页面信息返回给客户端。

Proxy 也是如此.

在此示例中,Proxy 充当 target.name = "やめ太郎";,而 setter 为 obj 工作。

就像“当你做〇〇时,做〇〇”这样的表达。
这是魔法改造的真相是。

Proxy定义为“当〇〇时做〇〇”,作为目标对象的代理这是。
在之前 Yumemi 的代码示例中,它是一个 setter,你甚至可以在 getter 和函数执行中定义任何你想要的东西!

const user = new Proxy({ name: "" }, { get: () => "やめ太郎" })

这也是 Yumemi 文章中的一个代码示例。
在这个例子中,getter 被神奇地改造了,当getter 工作时,它是返回"やめ太郎" 的代理。

代理魔法

我将向您展示Proxy 的力量。

您的站点具有以下实现来向后端 API 发出请求。

// 各 API へアクセスするためのパスを定義
const apiPathList = {
  articles: '/api/articles',
  users: '/api/users',
  // ...いっぱい
}

// 各 API へアクセスするためのオブジェクトを定義
const myClient = {
  fetchArticles(queryParams) {
    return new Promise((resolve, reject) => {
      // { page: 1 } などのオブジェクトを page=1 のようなクエリパラメータに変換
      const queryStr = (new URLSearchParams(queryParams)).toString();
      fetch(`${apiPathList.articles}?${queryStr}`)
        .then(res => res.json())
        .then(resolve)
        .catch(reject);
    })
  }
  fetchUsers(queryParams) {
    // ...
  }
  // API のパスごとに関数を用意
  // ...
}

// API へアクセスする時
const artcles = await myClient.fetchArticles({ page: 1 });

后端有几个API,为每个API创建一个请求函数。
但是随着服务的扩展,后端变成了微服务,API的数量稳步增加。
随着 API 数量的增加,访问每个 API 的 URL 定义和请求函数也必须增加。

在这里使用Proxy 来做一些魔术。

const apiClient = {};

// キャメルケースを小文字にしてスラッシュに変更する関数
// 例: fetchApiArticles -> fetch/api/articles
const convertCamelCaseToLowerCaseSlash = str => {
  return str
    .split(/(?=[A-Z])/)
    .join('/')
    .toLowerCase();
}

// Proxy でゲッターを魔改造して、 fetch する関数を返すようにします
const handler = {
  // prop には、関数名が入ります
  // myClient.fetchApiArticles だったら prop は 'fetchApiArticles' になります
  // target と receiver は使いません
  get(_target, prop, _receiver) {
    return queryParams =>
      new Promise((resolve, reject) => {
        if (prop.indexOf('fetch') !== 0) {
          reject('プロパティ名の先頭は「fetch」をつけてください');
          return;
        }
        // { page: 1 } などのオブジェクトを page=1 のようなクエリパラメータに変換
        const queryStr = queryParams
          ? `?${new URLSearchParams(queryParams).toString()}`
          : '';
        // convertCamelCaseToSlash の結果が fetch/api/articles のようになるので、先頭の fetch は削除
        const requestUrl = convertCamelCaseToLowerCaseSlash(prop).replace('fetch', '');
        fetch(`${requestUrl}${queryStr}`)
          .then(data => data.json())
          .then(resolve)
          .catch(reject);
      });
  },
};

const myClient = new Proxy(apiClient, handler);

// 関数は定義されていないが /api/articles?page=1 でリクエストされる!!
const articles = await myClient.fetchApiArticles({ page: 1 });

// 関数は定義されていないが /api/users?page=1 でリクエストされる!!
const users = await myClient.fetchApiUsers({ page: 1 });

// 「プロパティ名の先頭は「fetch」をつけてください」 のエラーが出る
const response = await myClient.hoge();

即使没有定义像 myClient.fetchApiArticles 和 myClient.fetchApiUsers 这样的函数,预期的请求仍然会飞! !

和,即使后端API数量增加也无需修改.
例如,假设添加了一个新的 API,如 /api/search。
只需使用myClient.fetchApiSearch 即可完成是。

此代码根据某些规则构造请求的 URL。
规则如下。

  • 将“fetch”添加到属性名称的开头。
  • 属性名称的驼峰式大小写照原样成为请求路径。
    • 例如fetchHogeFuga被转换为/hoge/fuga的请求路径。

虽然在代码示例中是Proxy,但您可以通过修改handler来进一步扩展功能。
例如,如果要更改请求目的地的域名,或者更改请求方式。

// GET でリクエスト
myClient.fetchGetApiArticles();

// hoge.com のような別ドメインにリクエスト
myClient.fetchHogeDomainApiArticles();

如果您为每个 API 的每个函数创建了一个测试用例,您将需要测试每个函数,但是在这个 Proxy 示例中,预期的请求 URL 是基于纯属性名称发出的。既然可以得到确认,测试用例的数量也可以减少。

这样Proxy 可用于减少相当数量的代码.

何时使用代理

老实说,我什么都能做,所以有一些地方我可以提出想法,但关键是对象扩展我猜。

诸如Vue 和MobX 之类的库扩展了您通过Proxy 传递给组件的数据。
检测对象更改并仅呈现具有数据依赖关系的组件,使用Proxy 检测。
它可能用于“当对某个对象进行更改时我想做〇〇!”的情况下使用它。

另外,对不起我的例子,但是我已经使用Proxy对WebWorker进行线程管理和处理访问WebWorker中的主线程的DOM。

WebWorker的线程管理例如是将WebWorker要处理的任务放入队列中,当某个线程中的处理完成后,如果队列中有任务,则继续被执行,如果没有,线程将被终止。就像。 Proxy当管理Proxy包裹的线程状态的对象发生变化时,会进行管理线程的处理,如“队列中有任务则继续执行,没有则终止线程” .

后者,“处理访问WebWorker内的主线程的DOM”,在下面的文章中简要提及。

Ruby 在这方面特别强,但是Proxy元编程这也是一个叫
作为一个完全题外话,如果有人正在研究 Ruby,我推荐这本书“Metaprogramming Ruby”。
我读了这本书,并在 Ruby 工程师认证考试中获得金牌!微笑

(如果你从这里买一本书,我会高兴地哭泣!!)

让我们一直使用代理?

“Proxy方便!尽快用吧!”

【JavaScript】数千行のコードを数行にしてしまう Proxy の魔力

不好是。

好吧,说不可能太多了,但我不建议这样做。

不推荐它有几个原因。

第一个原因没有人可以阅读代码这是为了
代理魔法这是一个使用Proxy 的代码示例,但老实说,它一定很难阅读代码。
Proxy本身就有它的特殊性,还有一个潜规则就是“这个属性名一定要写”。
我想我自己写了Proxy的处理,几个月后就看不懂了?

其他,与 TypeScript 不兼容是。
正如您在Proxy 实现中看到的那样,通常存在难以定义类型的情况。

首先,Proxy 动态寻求自由,而 TypeScript 静态寻求严格。
这不是狗和猴子的关系,但我认为这是一个哲学矛盾。

获得强大的力量是有代价的。
在认识到缺点之后,让我们使用Proxy。

在最后

怎么样?如果您能以某种方式理解Proxy,我会很高兴!

如果你更深入地研究 JavaScript,你会发现新的东西。
例如,setTimeout 不仅仅是一个在指定时间运行的函数。

如果描述有任何错误,请告诉我!

以后我会继续写文章,所以如果你愿意推特如果你关注我,我会通知你文章发布!

谢谢你读到这里!


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308632231.html

相关文章:

  • 2021-06-02
  • 2021-07-21
  • 2021-09-15
  • 2022-01-10
  • 2022-02-27
  • 2022-02-02
  • 2021-08-08
猜你喜欢
  • 2021-05-24
  • 2021-07-10
  • 2022-12-23
  • 2021-08-11
  • 2021-04-16
  • 2021-11-19
相关资源
相似解决方案