溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ES6中的Proxy類(lèi)如何使用

發(fā)布時(shí)間:2023-04-20 09:35:38 來(lái)源:億速云 閱讀:198 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹了ES6中的Proxy類(lèi)如何使用的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇ES6中的Proxy類(lèi)如何使用文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

Object

ES5 中,我們可以定義一個(gè)對(duì)象,并且對(duì)其進(jìn)行操作(添加或查找),如下代碼所示:

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

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

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

那如果我們要對(duì)該對(duì)象進(jìn)行監(jiān)聽(tīng)呢,我們希望可以監(jiān)聽(tīng)到這個(gè)對(duì)象中的屬性被設(shè)置或獲取的過(guò)程,我們可以通過(guò)屬性描述符中的存儲(chǔ)屬性來(lái)做到這個(gè)功能。

屬性描述符

ES5 中,所有的屬性都具備了屬性描述符,具體使用如下圖所示:

ES6中的Proxy類(lèi)如何使用

就像上圖所展示的一樣,這個(gè)普通的對(duì)象屬性對(duì)應(yīng)的屬性描述符,可不僅僅是一個(gè)18,它還包含另外三個(gè)特性,它們分別是:

  • writable;

  • enumerable;

  • configurable;

在創(chuàng)建普通戶(hù)型時(shí),屬性描述符會(huì)使用默認(rèn)值,我們可以使用 Object.defineProperty(...) 來(lái)添加一個(gè)新屬性或者修改一個(gè)已有屬性(當(dāng)該屬性 Configurable 的值為 true 時(shí))并對(duì)特性進(jìn)行設(shè)置,具體如下代碼所示:

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

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

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

該方法接收三個(gè)參數(shù),它們分別為:

  • 要定義屬性的對(duì)象;

  • 要定義或修改的屬性的名稱(chēng)或 Symbol;

  • 要定義或修改的屬性描述符;

Getter和Setter

前面說(shuō)了這么多基礎(chǔ)的東西,但是還沒(méi)有講解是怎么接收到屬性的變化的,在這里,屬性描述符 Object.defineProperty 提供了兩個(gè)屬性,它們分別是 setget,兩個(gè)屬性的使用如下所示:

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

Object.keys(moment).forEach((key) => {
  let value = moment[key];
  Object.defineProperty(moment, key, {
    get: function () {
      console.log(`監(jiān)聽(tīng)到了對(duì)象的 ${key} 屬性被訪問(wèn)了`);
      return value;
    },

    set: function (params) {
      console.log(`監(jiān)聽(tīng)到了對(duì)象的 ${key} 屬性被修改了`);
      value = params;
    },
  });
});

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

當(dāng)我們對(duì) moment.age 進(jìn)行賦值的時(shí)候,會(huì)調(diào)用 set 屬性上的方法,最終會(huì)輸出 監(jiān)聽(tīng)到了對(duì)象的 age 屬性被修改了。

當(dāng)我們對(duì) moment.address 進(jìn)行取值的時(shí)候,會(huì)調(diào)用 get 屬性上的方法,最終會(huì)輸出 監(jiān)聽(tīng)到了對(duì)象的 address 屬性被訪問(wèn)了

雖然這兩個(gè)方法是能做到,但是如果我們想監(jiān)聽(tīng)更加豐富的操作的時(shí)候是做不到的,例如新增屬性,淡出屬性,使用 Object.defineProperty(...) 是做不到的

ES6中的Proxy類(lèi)如何使用

最終結(jié)果如上圖所示,終端中的輸出是對(duì)應(yīng)上面畫(huà)圈圈的代碼執(zhí)行。

那么 Proxy 的出現(xiàn)就很好的解決了這一痛點(diǎn)。

Proxy

ES6 中,新增了一個(gè) Proxy 類(lèi),這個(gè)類(lèi)從名字就可以看出來(lái),是用于幫助我們創(chuàng)建一個(gè)代理的,它是一種由你創(chuàng)建的特殊對(duì)象,它封裝另一個(gè)普通對(duì)象或者說(shuō)擋在這個(gè)普通對(duì)象的前面,例如你要修改一個(gè)對(duì)象,它主要有以下的流程圖:

ES6中的Proxy類(lèi)如何使用

它就像一個(gè)關(guān)卡對(duì)你的操作進(jìn)行攔截。

這個(gè)類(lèi)的基本使用如下所示:

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

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

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

你會(huì)發(fā)現(xiàn)它返回的是同一個(gè)對(duì)象,但是內(nèi)存地址不同,所以通過(guò)嚴(yán)格相等比較返回的值也就是 false。

Peoxy的13種捕獲器

Peoxy 總共有13個(gè)捕獲器,但是常用的就那么幾個(gè),這里不低其全部講解,具體可以查看 MDN官網(wǎng)

話不多說(shuō),直接上代碼:

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

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

const proxy = new Proxy(moment, {
  has: function (target, prop) {
    console.log(`使用 in 訪問(wèn)了 ${prop} 是否存在于 moment 對(duì)象`);
  },
  get: function (target, property, receiver) {
    console.log(`通過(guò)讀取 moment 對(duì)象中的 ${property} 屬性`);
  },
  set: function (target, property, value, receiver) {
    console.log(`通過(guò)設(shè)置 moment 對(duì)象中的 ${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 訪問(wèn)了 age 是否存在于 moment 對(duì)象
proxy.age; // 通過(guò)讀取 moment 對(duì)象中的 age 屬性
proxy.address = "肇慶"; // 通過(guò)設(shè)置 moment 對(duì)象中的 address 屬性為 肇慶

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

new fProxy(1, 2);

在上面的代碼中,target === momentreceiver === proxy 都返回 true,也就是說(shuō) target 為對(duì)應(yīng)要修改的對(duì)象,而 receiverProxy 或者繼承 Proxy 的對(duì)象。

操作 Proxy 的同時(shí)會(huì)修改 moment 對(duì)象。

可取消代理

普通對(duì)象總是陷入到目標(biāo)對(duì)象,并且在創(chuàng)建之后不能改變,只要還保持著對(duì)這個(gè)代理的引用,代理的機(jī)制就將維持下去。

但是可能會(huì)存在這樣的情況,比如你想要?jiǎng)?chuàng)建一個(gè)在你想要停止它作為代理時(shí)便可被停用的代理,解決的方案是創(chuàng)建可取消代理,具體代碼如下所示:

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

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

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

最終的輸出如下圖所示:

ES6中的Proxy類(lèi)如何使用

一旦可取消代理被取消,任何對(duì)他的訪問(wèn)都會(huì)拋出 TypeError 錯(cuò)誤。

Proxy的問(wèn)題與不足

盡管現(xiàn)在 Proxy 已經(jīng)做得很好了,但是在某些情況下,代理也不能與現(xiàn)在的 ECMAScript 機(jī)制很好地協(xié)同。

Proxy中的this

Peoxy 潛在的一個(gè)問(wèn)題來(lái)源是 this 值,我們知道方法中的 this 通常執(zhí)行調(diào)用這個(gè)方法的對(duì)象,具體代碼如下所示:

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

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

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

按照正常的理解這是沒(méi)有問(wèn)題的調(diào)用 Proxy 上的任何方法,而這個(gè)方法進(jìn)而又會(huì)調(diào)用另一個(gè)方法,實(shí)際上都會(huì)調(diào)用 Proxy 內(nèi)部的方法,這是符合預(yù)期的行為,但是,如果目標(biāo)對(duì)象依賴(lài)于對(duì)象表示,那就可能碰到意料之外的問(wèn)題。

舉個(gè)例子:

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);
  }
}

由于這個(gè)實(shí)現(xiàn)依賴(lài) User 實(shí)例的對(duì)象標(biāo)識(shí),在這個(gè)實(shí)例被代理的情況下就會(huì)出現(xiàn)問(wèn)題:

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

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

這是因?yàn)?User 實(shí)例一開(kāi)始使用目標(biāo)對(duì)象作為 WeakMap 的鍵,代理對(duì)象卻嘗試從自身取得這個(gè)實(shí)例,要解決這個(gè)問(wèn)題,就需要重新設(shè)置代理,把代理 User 實(shí)例改為代理 User 類(lèi)本身,之后再創(chuàng)建代理的實(shí)例就會(huì)創(chuàng)建代理實(shí)例作為 WeakMap 的鍵了:

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

Proxy與內(nèi)部槽位

代理與內(nèi)置引用類(lèi)型的實(shí)例通??梢院芎玫貐f(xié)同,但有些 ECMAScript 內(nèi)置類(lèi)型可能會(huì)依賴(lài)代理無(wú)法控制的機(jī)制,結(jié)果導(dǎo)致在代理上調(diào)用某些方法會(huì)出錯(cuò),具體代碼如下所示:

const target = new Date();

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

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

在上面的代碼中,根據(jù) ECMAScript 規(guī)范,Date 類(lèi)型方法的執(zhí)行依賴(lài) this 值上的內(nèi)部曹偉 [[NumberDate]],代理對(duì)象上不存在這個(gè)內(nèi)部槽位,而且這個(gè)內(nèi)部槽位的值也不能通過(guò)普通的 get()set() 操作訪問(wèn)到,于是代理攔截后本應(yīng)該轉(zhuǎn)發(fā)給目標(biāo)對(duì)象的方法會(huì)拋出 TypeRrror 的錯(cuò)誤。

關(guān)于“ES6中的Proxy類(lèi)如何使用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“ES6中的Proxy類(lèi)如何使用”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI