Heim  >  Artikel  >  Web-Frontend  >  Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

藏色散人
藏色散人nach vorne
2023-04-14 17:23:301687Durchsuche

Ich hatte die Idee, diesen Artikel vor ein paar Monaten zu schreiben, aber als ich über diesen Wissenspunkt nachdachte, wird VueJs häufig verwendet und ich bin ein React Entwickler. Wenn ich gebeten würde, ein anderes Framework auszuwählen, würde ich vielleicht angular bevorzugen, aber ich habe kürzlich NestJs gelernt und festgestellt, dass Reflect das ist unten weit verbreitet, wie folgt: VueJs 用得比较多,我本身是一个 React 开发者,如果让我选另外一个框架的话我可能更倾向 angular,但是最近在学 NestJs,发现底层大量使用的 Reflect,具体如下:

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

好了,到了这里我就是我今天为什么会写这篇文章的原因了,本篇文章将会按照 Object -> Proxy -> Reflect 的顺序进行讲解,赶紧学起来吧!

Object

ES5 中,我们可以定义一个对象,并且对其进行操作(添加或查找),如下代码所示:

const moment = {
  age: 18,
  address: "西安",
};

moment["hobby"] = "basketball";
moment.hight = 1.59;

console.log(moment.age); // 18

那如果我们要对该对象进行监听呢,我们希望可以监听到这个对象中的属性被设置或获取的过程,我们可以通过属性描述符中的存储属性来做到这个功能。

属性描述符

ES5 中,所有的属性都具备了属性描述符,具体使用如下图所示:

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

就像上图所展示的一样,这个普通的对象属性对应的属性描述符,可不仅仅是一个18,它还包含另外三个特性,它们分别是:

  • writable;
  • enumerable;
  • configurable;

在创建普通户型时,属性描述符会使用默认值,我们可以使用 Object.defineProperty(...) 来添加一个新属性或者修改一个已有属性(当该属性 Configurable 的值为 true 时)并对特性进行设置,具体如下代码所示:

const moment = {
  age: 18,
  address: "西安",
};

Object.defineProperty(moment, "address", {
  value: "肇庆",
  writable: true,
  configurable: true,
  enumerable: true,
});

console.log(moment.address); // 肇庆

该方法接收三个参数,它们分别为:

  • 要定义属性的对象;
  • 要定义或修改的属性的名称或 Symbol;
  • 要定义或修改的属性描述符;

Getter和Setter

前面说了这么多基础的东西,但是还没有讲解是怎么接收到属性的变化的,在这里,属性描述符 Object.defineProperty 提供了两个属性,它们分别是 setget,两个属性的使用如下所示:

const moment = {
  age: 18,
  address: "西安",
};

Object.keys(moment).forEach((key) => {
  let value = moment[key];
  Object.defineProperty(moment, key, {
    get: function () {
      console.log(`监听到了对象的 ${key} 属性被访问了`);
      return value;
    },

    set: function (params) {
      console.log(`监听到了对象的 ${key} 属性被修改了`);
      value = params;
    },
  });
});

moment.age = 22;
const foo = moment.address;

当我们对 moment.age 进行赋值的时候,会调用 set 属性上的方法,最终会输出 监听到了对象的 age 属性被修改了

当我们对 moment.address 进行取值的时候,会调用 get 属性上的方法,最终会输出 监听到了对象的 address 属性被访问了

虽然这两个方法是能做到,但是如果我们想监听更加丰富的操作的时候是做不到的,例如新增属性,淡出属性,使用 Object.defineProperty(...) 是做不到的

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

最终结果如上图所示,终端中的输出是对应上面画圈圈的代码执行。

那么 Proxy 的出现就很好的解决了这一痛点。

Proxy

ES6 中,新增了一个 Proxy 类,这个类从名字就可以看出来,是用于帮助我们创建一个代理的,它是一种由你创建的特殊对象,它封装另一个普通对象或者说挡在这个普通对象的前面,例如你要修改一个对象,它主要有以下的流程图:

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

它就像一个关卡对你的操作进行拦截。

这个类的基本使用如下所示:

const moment = {
  age: 18,
  address: "西安",
};

const proxy = new Proxy(moment, {});

console.log(proxy); // { age: 18, address: '西安' }

你会发现它返回的是同一个对象,但是内存地址不同,所以通过严格相等比较返回的值也就是 false

Peoxy的13种捕获器

Peoxy 总共有13个捕获器,但是常用的就那么几个,这里不低其全部讲解,具体可以查看 MDN官网

话不多说,直接上代码:

const moment = {
  age: 18,
  address: "西安",
};

function foo(x, y) {
  return x + y;
}

const proxy = new Proxy(moment, {
  has: function (target, prop) {
    console.log(`使用 in 访问了 ${prop} 是否存在于 moment 对象`);
  },
  get: function (target, property, receiver) {
    console.log(`通过读取 moment 对象中的 ${property} 属性`);
  },
  set: function (target, property, value, receiver) {
    console.log(`通过设置 moment 对象中的 ${property} 属性为 ${value}`);
  },
});

const fProxy = new Proxy(foo, {
  apply: function (target, _, params) {
    return target(...params) * 10;
  },
  construct: function (target, argumentsList, newTarget) {
    console.log(target); // [Function: foo]
    console.log(argumentsList); // [ 1, 2 ]
    console.log(newTarget); // [Function: foo]
    return {};
  },
});

"age" in proxy; // 使用 in 访问了 age 是否存在于 moment 对象
proxy.age; // 通过读取 moment 对象中的 age 属性
proxy.address = "肇庆"; // 通过设置 moment 对象中的 address 属性为 肇庆

console.log(foo(1, 2)); // 3
console.log(fProxy(1, 2)); // 30

new fProxy(1, 2);

在上面的代码中,target === momentreceiver === proxy 都返回 true,也就是说 target 为对应要修改的对象,而 receiverProxy 或者继承 Proxy

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!🎜🎜Okay Okay, hier bin ich der Grund, warum ich diesen Artikel heute geschrieben habe. Dieser Artikel basiert auf Object -> ; Reflect wird der Reihe nach erklärt, 🎜

Object

🎜In ES5 können wir Definieren Sie ein Objekt und bearbeiten Sie es (hinzufügen oder suchen), wie im folgenden Code gezeigt: 🎜
const moment = {
  age: 18,
  address: "西安",
};

const { proxy, revoke } = Proxy.revocable(moment, {
  get: function (target, key, receiver) {
    console.log("get 捕获器");
  },
});

proxy.address;
revoke();
proxy.address;
🎜 Wenn wir also dieses Objekt überwachen möchten, hoffen wir, den Prozess des Festlegens oder Abrufens der Attribute in diesem Objekt überwachen zu können. Dazu können wir die gespeicherten Eigenschaften im Eigenschaftsdeskriptor übergeben. 🎜

Attributdeskriptor

🎜In ES5 haben alle Attribute Attributdeskriptoren. Die spezifische Verwendung ist in der folgenden Abbildung dargestellt: 🎜 🎜Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!🎜🎜Gefällt mir Wie im gezeigt Im Bild oben ist der Attributdeskriptor, der diesem gewöhnlichen Objektattribut entspricht, nicht nur eine 18, sondern enthält auch drei weitere Merkmale: 🎜
  • beschreibbar ;
  • aufzählbar;
  • konfigurierbar;
🎜 Beim Erstellen eines gewöhnlichen Haustyps wird der Der Eigenschaftsdeskriptor verwendet den Standardwert. Wir können Object.defineProperty(...) verwenden, um eine neue Eigenschaft hinzuzufügen oder eine vorhandene Eigenschaft zu ändern (wenn die Eigenschaft Wenn der Wert von Configurable ist. code> ist <code>true) und legen Sie die Eigenschaften fest, wie im folgenden Code gezeigt: 🎜
const target = {
  moment() {
    return this === proxy;
  },
};

const proxy = new Proxy(target, {});

console.log(target.moment()); // false
console.log(proxy.moment()); // true
🎜Diese Methode empfängt drei Parameter: 🎜
  • Das Objekt, dessen Eigenschaften sein sollen definiert;
  • Der Name oder das Symbol der Eigenschaft, die definiert oder geändert werden soll;
  • Der Eigenschaftsdeskriptor, der definiert oder geändert werden soll;
  • li >

Getter und Setter

🎜Ich habe schon über so viele grundlegende Dinge gesprochen, aber ich habe noch nicht erklärt, wie man Änderungen an Attributen hier erhält. Der Eigenschaftsdeskriptor Object.defineProperty stellt zwei Eigenschaften bereit: set und get. Die Verwendung der beiden Eigenschaften ist wie folgt: 🎜
const wm = new WeakMap();

class User {
  constructor(userId) {
    wm.set(this, userId);
  }

  set id(userId) {
    wm.set(this, userId);
  }
  get id() {
    return wm.get(this);
  }
}
🎜 Wenn wir moment.age einen Wert zuweisen, wird die Methode für das Attribut set aufgerufen und schließlich wird ausgegeben und das Altersattribut des Objekts hat wurde geändert. . 🎜🎜Wenn wir den Wert von moment.address erhalten, wird die Methode für das Attribut get aufgerufen und schließlich wird als Ausgabe das Adressattribut überwacht des Objekts wird zugegriffen . 🎜🎜Obwohl diese beiden Methoden ausgeführt werden können, können wir dies nicht tun, wenn wir umfangreichere Vorgänge überwachen möchten, z. B. das Hinzufügen von Attributen und das Ausblenden von Attributen. Verwenden Sie Object.defineProperty(...) Es ist möglich Das geht nicht🎜🎜<img src="https://img.php.cn/upload/article/000/000/020/8a74cdb641da858575cc289fd9ea79a1-2.png" alt="Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!" loading="lazy">🎜🎜Das Endergebnis ist wie im Bild oben dargestellt. Die Ausgabe im Terminal entspricht der Ausführung des oben eingekreisten Codes. 🎜🎜Dann löst das Aufkommen von <code>Proxy dieses Problem sehr gut. 🎜

Proxy

🎜In ES6 wurde eine neue Proxy-Klasse hinzugefügt, die an dieser Klasse erkennbar ist Es ist ersichtlich, dass es uns dabei hilft, einen Proxy zu erstellen. Es handelt sich um ein von Ihnen erstelltes spezielles Objekt, das ein anderes gewöhnliches Objekt oder Blöcke vor diesem gewöhnlichen Objekt kapselt Es enthält hauptsächlich das folgende Flussdiagramm: 🎜🎜Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!🎜🎜Es ist wie eine Ebene, die Ihre Operationen abfängt. 🎜🎜Die grundlegende Verwendung dieser Klasse ist wie folgt: 🎜
const user = new User(1);
console.log(user.id); // 1

const proxy = new Proxy(user, {});
console.log(proxy.id); // undefined
🎜Sie werden feststellen, dass sie dasselbe Objekt zurückgibt, die Speicheradresse jedoch unterschiedlich ist, sodass der durch strikten Gleichheitsvergleich zurückgegebene Wert false ist. 🎜

Peoxys 13 Capturer

🎜Peoxy Insgesamt gibt es 13 Capturer, aber es gibt nur wenige häufig verwendete, die nicht niedrig sind Hier finden Sie alle Erklärungen unter Offizielle MDN-Website🎜🎜Es gibt nicht viel zu sagen, gehen wir direkt zum Code:🎜
const UserProcess = new Proxy(User, {});
const proxy = new UserProcess(1);
console.log(proxy.id); // 1
🎜Im obigen Code target == = moment und receiver === Proxy geben beide true zurück, was bedeutet, dass target dem Objekt entspricht geändert werden und receiver ist Proxy oder ein Objekt, das Proxy erbt. 🎜

操作 Proxy 的同时会修改 moment 对象。

可取消代理

普通对象总是陷入到目标对象,并且在创建之后不能改变,只要还保持着对这个代理的引用,代理的机制就将维持下去。

但是可能会存在这样的情况,比如你想要创建一个在你想要停止它作为代理时便可被停用的代理,解决的方案是创建可取消代理,具体代码如下所示:

const moment = {
  age: 18,
  address: "西安",
};

const { proxy, revoke } = Proxy.revocable(moment, {
  get: function (target, key, receiver) {
    console.log("get 捕获器");
  },
});

proxy.address;
revoke();
proxy.address;

最终的输出如下图所示:

Es ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!

一旦可取消代理被取消,任何对他的访问都会抛出 TypeError 错误。

Proxy的问题与不足

尽管现在 Proxy 已经做得很好了,但是在某些情况下,代理也不能与现在的 ECMAScript 机制很好地协同。

Proxy中的this

Peoxy 潜在的一个问题来源是 this 值,我们知道方法中的 this 通常执行调用这个方法的对象,具体代码如下所示:

const target = {
  moment() {
    return this === proxy;
  },
};

const proxy = new Proxy(target, {});

console.log(target.moment()); // false
console.log(proxy.moment()); // true

按照正常的理解这是没有问题的调用 Proxy 上的任何方法,而这个方法进而又会调用另一个方法,实际上都会调用 Proxy 内部的方法,这是符合预期的行为,但是,如果目标对象依赖于对象表示,那就可能碰到意料之外的问题。

举个例子:

const wm = new WeakMap();

class User {
  constructor(userId) {
    wm.set(this, userId);
  }

  set id(userId) {
    wm.set(this, userId);
  }
  get id() {
    return wm.get(this);
  }
}

由于这个实现依赖 User 实例的对象标识,在这个实例被代理的情况下就会出现问题:

const user = new User(1);
console.log(user.id); // 1

const proxy = new Proxy(user, {});
console.log(proxy.id); // undefined

这是因为 User 实例一开始使用目标对象作为 WeakMap 的键,代理对象却尝试从自身取得这个实例,要解决这个问题,就需要重新设置代理,把代理 User 实例改为代理 User 类本身,之后再创建代理的实例就会创建代理实例作为 WeakMap 的键了:

const UserProcess = new Proxy(User, {});
const proxy = new UserProcess(1);
console.log(proxy.id); // 1

Proxy与内部槽位

代理与内置引用类型的实例通常可以很好地协同,但有些 ECMAScript 内置类型可能会依赖代理无法控制的机制,结果导致在代理上调用某些方法会出错,具体代码如下所示:

const target = new Date();

const proxy = new Proxy(target, {});

console.log(proxy instanceof Date); // true
proxy.getDate(); // TypeError: this is not a Date object.

在上面的代码中,根据 ECMAScript 规范,Date 类型方法的执行依赖 this 值上的内部曹伟 [[NumberDate]],代理对象上不存在这个内部槽位,而且这个内部槽位的值也不能通过普通的 get()set() 操作访问到,于是代理拦截后本应该转发给目标对象的方法会抛出 TypeRrror 的错误。

这篇文章到此结束,下一篇将会详细讲解 Reflect,敬请期待。

Das obige ist der detaillierte Inhalt vonEs ist in Ordnung, wenn Sie Vue nicht kennen, aber Sie müssen wissen, wie man Proxy verwendet!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen