欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS?中Proxy代理和?Reflect反射方法示例詳解

 更新時(shí)間:2022年10月11日 14:47:24   作者:forwardXX  
這篇文章主要為大家介紹了JS?中Proxy代理和?Reflect反射方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

總所周知,Vue2 => Vue3 時(shí),數(shù)據(jù)響應(yīng)式方法從Object.defineProperty()方法變成了Proxy(),所以今天與大家 Proxy(代理)和 Reflect(反射)的知識(shí)。

講解 Proxy 和 Reflect 前,我們需要先了解屬性描述符的作用,所以我們線(xiàn)簡(jiǎn)單解釋一下屬性描述符的知識(shí)。

1.屬性描述符

屬性描述符(Property Descriptor) 本質(zhì)上是一個(gè) JavaScript 普通對(duì)象,用于描述一個(gè)屬性的相關(guān)信息,共有下面這幾種屬性。

  • value:屬性值
  • configurable:該屬性的描述符是否可以修改
  • enumerable:該屬性是否可以被枚舉
  • writable:該屬性是否可以被重新賦值
  • 存取器屬性:屬性描述符中如果配置了 get 和 set 中的任何一個(gè),則該屬性不再是一個(gè)普通屬性,而變成了存取器屬性。
    • get()讀值函數(shù):如果一個(gè)屬性是存取器屬性,則讀取該屬性時(shí),會(huì)運(yùn)行 get 方法,并將 get 方法得到的返回值作為屬性值
    • set(newVal)存值函數(shù):如果給該屬性賦值,則會(huì)運(yùn)行 set 方法,newVal 參數(shù)為賦值的值。

存取器屬性最大的意義,在于可以控制屬性的讀取和賦值,在函數(shù)里可以進(jìn)行各種操作。

Vue2 數(shù)據(jù)響應(yīng)式就是使用了這一點(diǎn),在 getter 和 setter 函數(shù)中進(jìn)行了數(shù)據(jù)綁定與派發(fā)更新。

注意點(diǎn):value 和 writable 屬性不能與 get 和 set 屬性二者不可共存,二者只能選其一。

查看某個(gè)對(duì)象的屬性描述符,使用以下這兩種方法:

Object.getOwnPropertyDescriptor(對(duì)象, 屬性名); //得到一個(gè)對(duì)象的某個(gè)屬性的屬性描述符
Object.getOwnPropertyDescriptors(對(duì)象); //得到某個(gè)對(duì)象的所有屬性描述符

為某個(gè)對(duì)象添加屬性時(shí) 或 修改屬性時(shí),配置其屬性描述符,使用以下這兩種方法:

Object.defineProperty(對(duì)象, 屬性名, 描述符); //設(shè)置一個(gè)對(duì)象的某個(gè)屬性
Object.defineProperties(對(duì)象, 多個(gè)屬性的描述符); //設(shè)置一個(gè)對(duì)象的多個(gè)屬性

2.Reflect

Reflect 是什么? Reflect 是一個(gè)內(nèi)置的 JS 對(duì)象,它提供了一系列方法,可以讓開(kāi)發(fā)者通過(guò)調(diào)用這些方法,訪(fǎng)問(wèn)一些 JS 底層功能。

由于它類(lèi)似于其他語(yǔ)言的反射,因此取名為 Reflect。

它可以做什么? 使用 Reflect 可以實(shí)現(xiàn)諸如:屬性的賦值與取值、調(diào)用普通函數(shù)、調(diào)用構(gòu)造函數(shù)、判斷屬性是否存在與對(duì)象中 等等功能。

這些功能不是已經(jīng)存在了嗎?為什么還需要用 Reflect 實(shí)現(xiàn)一次? 有一個(gè)重要的理念,在 ES5 就被提出:減少魔法、讓代碼更加純粹(語(yǔ)言的方法使用 API 實(shí)現(xiàn),而不使用特殊語(yǔ)法實(shí)現(xiàn)),這種理念很大程度上是受到函數(shù)式編程的影響。 ES6 進(jìn)一步貫徹了這種理念,它認(rèn)為,對(duì)屬性?xún)?nèi)存的控制、原型鏈的修改、函數(shù)的調(diào)用等等,這些都屬于底層實(shí)現(xiàn),屬于一種魔法,因此,需要將它們提取出來(lái),形成一個(gè)正常的 API,并高度聚合到某個(gè)對(duì)象中,于是就造就了 Reflect 對(duì)象。 因此,你可以看到 Reflect 對(duì)象中有很多的 API 都可以使用過(guò)去的某種語(yǔ)法或其他 API 實(shí)現(xiàn)。

Reflect 里面提供了哪些 API 呢?

Reflect API用處等同于
Reflect.get(target, propertyKey)讀取對(duì)象 target 的屬性 propertyKey對(duì)象的屬性值讀取操作
Reflect.set(target, propertyKey, value)設(shè)置對(duì)象 target 的屬性 propertyKey 的值為 value對(duì)象的屬性賦值操作
Reflect.has(target, propertyKey)判斷一個(gè)對(duì)象是否擁有一個(gè)屬性in 操作符
Reflect.defineProperty(target, propertyKey, attributes)類(lèi)似于 Object.defineProperty,不同的是如果配置出現(xiàn)問(wèn)題,返回 false 而不是報(bào)錯(cuò)Object.defineProperty
Reflect.deleteProperty(target, propertyKey)刪除一個(gè)對(duì)象的屬性delete 操作符
Reflect.apply(target, thisArgument, argumentsList)調(diào)用一個(gè)指定的函數(shù),并綁定 this 和參數(shù)列表函數(shù)調(diào)用操作
Reflect.construct(target, argumentsList)用構(gòu)造函數(shù)的方式創(chuàng)建一個(gè)對(duì)象new 操作符

其他更多的 Reflect API

3.Proxy

ECMAScript 6 新增的代理和反射為開(kāi)發(fā)者提供了攔截并向基本操作嵌入額外行為的能力。

具體地說(shuō),可以給目標(biāo)對(duì)象(target)定義一個(gè)關(guān)聯(lián)的代理對(duì)象,而這個(gè)代理對(duì)象可當(dāng)作一個(gè)抽象的目標(biāo)對(duì)象來(lái)使用

因此在對(duì)目標(biāo)對(duì)象的各種操作影響到目標(biāo)對(duì)象之前,我們可以在代理對(duì)象中對(duì)這些操作加以控制,并且最終也可能會(huì)改變操作返回的結(jié)果。

所以我的理解是:代理(Proxy)能使我們開(kāi)發(fā)者擁有一種間接修改底層方法的能力,從而控制用戶(hù)的操作。

3.1 創(chuàng)建空代理

最簡(jiǎn)單的代理是空代理,即除了作為一個(gè)抽象的目標(biāo)對(duì)象,什么也不做。 默認(rèn)情況下,在代理對(duì)象上執(zhí)行的所有操作都會(huì)無(wú)障礙地傳播到目標(biāo)對(duì)象。因此,在任何可以使用目標(biāo)對(duì)象的地方,都可以通過(guò)同樣的方式來(lái)使用與之關(guān)聯(lián)的代理對(duì)象。

代理是使用 Proxy 構(gòu)造函數(shù)創(chuàng)建的,這個(gè)構(gòu)造函數(shù)接收兩個(gè)參數(shù):目標(biāo)對(duì)象和處理程序?qū)ο?。?少其中任何一個(gè)參數(shù)都會(huì)拋出 TypeError。返回一個(gè)代理對(duì)象 如:new Proxy(target, handler);

要?jiǎng)?chuàng)建空代理,可以傳一個(gè)簡(jiǎn)單的對(duì)象字面量作為處理程序?qū)ο?,從而讓所有操作暢通無(wú)阻地抵達(dá)目標(biāo)對(duì)象。

const target = {
 id: 'target'
}; //target:目標(biāo)對(duì)象
const handler = {}; //handler:是一個(gè)普通對(duì)象,其中可以重寫(xiě)底層實(shí)現(xiàn)
//創(chuàng)建空對(duì)象
const proxy = new Proxy(target, handler);
// id 屬性會(huì)訪(fǎng)問(wèn)同一個(gè)值
console.log(target.id); // target
console.log(proxy.id); // target
// 給目標(biāo)屬性賦值會(huì)反映在兩個(gè)對(duì)象上 因?yàn)閮蓚€(gè)對(duì)象訪(fǎng)問(wèn)的是同一個(gè)值
target.id = 'foo';
console.log(target.id); // foo
console.log(proxy.id); // foo
// 給代理屬性賦值會(huì)反映在兩個(gè)對(duì)象上 因?yàn)檫@個(gè)賦值會(huì)轉(zhuǎn)移到目標(biāo)對(duì)象
proxy.id = 'bar';
console.log(target.id); // bar
console.log(proxy.id); // bar
// hasOwnProperty()方法在兩個(gè)地方  也都會(huì)應(yīng)用到目標(biāo)對(duì)象
console.log(target.hasOwnProperty('id')); // true
console.log(proxy.hasOwnProperty('id')); // true
// Proxy.prototype 是 undefined   因此不能使用 instanceof 操作符
console.log(target instanceof Proxy); // TypeError: Function has non-object prototype
'undefined' in instanceof check
console.log(proxy instanceof Proxy); // TypeError: Function has non-object prototype
'undefined' in instanceof check
// 嚴(yán)格相等可以用來(lái)區(qū)分代理和目標(biāo)
console.log(target === proxy); // false

3.2 定義捕獲器

使用代理的主要目的是可以定義捕獲器(trap)。捕獲器就是在處理程序?qū)ο笾卸x的“基本操作的攔截器”。

每個(gè)處理程序?qū)ο罂梢园銈€(gè)或多個(gè)捕獲器,每個(gè)捕獲器都對(duì)應(yīng)一種基本操作,可以直接或間接在代理對(duì)象上調(diào)用。

每次在代理對(duì)象上調(diào)用這些基本操作時(shí),代理可以在這些操作傳播到目標(biāo)對(duì)象之前先調(diào)用捕獲器函數(shù),從而攔截并修改相應(yīng)的行為。

所有捕獲器都可以訪(fǎng)問(wèn)相應(yīng)的參數(shù),基于這些參數(shù)可以重建被捕獲方法的原始行為。比如,get()捕獲器會(huì)接收到目標(biāo)對(duì)象、要查詢(xún)的屬性代理對(duì)象三個(gè)參數(shù)。

const target = {
  foo: "bar",
};
const handler = {
  // 捕獲器在處理程序?qū)ο笾幸苑椒麨殒I
  get(trapTarget, property, receiver) {
    //trapTarget - 目標(biāo)對(duì)象
    //property   - 要查詢(xún)的屬性
    //receiver   - 代理對(duì)象
    return "handler override";
  },
};
const proxy = new Proxy(target, handler);
console.log(target.foo); // bar
console.log(proxy.foo); // handler override

3.3 捕獲器不變式

使用捕獲器幾乎可以改變所有基本方法的行為,但也不是沒(méi)有限制。

根據(jù) ECMAScript 規(guī)范,每個(gè)捕獲的方法都知道目標(biāo)對(duì)象上下文、捕獲函數(shù)簽名,而捕獲處理程序的行為必須遵循“捕獲器不變式”(trap invariant)。捕獲器不變式因方法不同而異,但通常都會(huì)防止捕獲器定義出現(xiàn)過(guò)于反常的行為。

比如,如果目標(biāo)對(duì)象有一個(gè)不可配置且不可寫(xiě)的數(shù)據(jù)屬性,那么在捕獲器返回一個(gè)與該屬性不同的值時(shí),會(huì)拋出 TypeError:

const target = {};
Object.defineProperty(target, "foo", {
  configurable: false,
  writable: false,
  value: "bar",
});
const handler = {
  get() {
    return "qux";
  },
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo); // TypeError

3.4 可撤銷(xiāo)代理

有時(shí)候可能需要中斷代理對(duì)象與目標(biāo)對(duì)象之間的聯(lián)系。

Proxy 也暴露了 revocable()方法,這個(gè)方法支持撤銷(xiāo)代理對(duì)象與目標(biāo)對(duì)象的關(guān)聯(lián)。

后續(xù)可直接調(diào)用撤銷(xiāo)函數(shù) revoke() 來(lái)撤銷(xiāo)代理。

撤銷(xiāo)代理之后再調(diào)用代理會(huì)拋出 TypeError,撤銷(xiāo)函數(shù)和代理對(duì)象是在實(shí)例化時(shí)同時(shí)生成的:

const target = {
  foo: "bar",
};
const handler = {
  get() {
    return "intercepted";
  },
};
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.foo); // intercepted
console.log(target.foo); // bar
revoke();
console.log(proxy.foo); // TypeError

4.代理捕獲器與反射方法

4.1 get()

get()捕獲器會(huì)在獲取屬性值的操作中被調(diào)用。對(duì)應(yīng)的反射 API 方法為 Reflect.get()

const myTarget = {};
const proxy = new Proxy(myTarget, {
  get(target, property, receiver) {
    console.log("get()");
    return Reflect.get(...arguments);
  },
});
proxy.foo; // 觸發(fā)get()捕獲器

返回值 返回值無(wú)限制。

攔截的操作

  • proxy.property
  • proxy[property]
  • Object.create(proxy)[property]
  • Reflect.get(proxy, property, receiver)

捕獲器處理程序參數(shù)

  • target:目標(biāo)對(duì)象。
  • property:引用的目標(biāo)對(duì)象上的字符串鍵屬性。① - receiver:代理對(duì)象或繼承代理對(duì)象的對(duì)象。

捕獲器不變式 如果 target.property 不可寫(xiě)且不可配置,則處理程序返回的值必須與 target.property 匹配。 如果 target.property 不可配置且[[Get]]特性為 undefined,處理程序的返回值也必須是 undefined。

4.2 set()

set()捕獲器會(huì)在設(shè)置屬性值的操作中被調(diào)用。對(duì)應(yīng)的反射 API 方法為 Reflect.set()。

const myTarget = {};
const proxy = new Proxy(myTarget, {
  set(target, property, value, receiver) {
    console.log("set()");
    return Reflect.set(...arguments);
  },
});
proxy.foo = "bar"; // 觸發(fā)set()捕獲器

返回值 返回 true 表示成功;返回 false 表示失敗,嚴(yán)格模式下會(huì)拋出 TypeError。

攔截的操作

  • proxy.property = value
  • proxy[property] = value
  • Object.create(proxy)[property] = value
  • Reflect.set(proxy, property, value, receiver)

捕獲器處理程序參數(shù)

  • target:目標(biāo)對(duì)象。
  • property:引用的目標(biāo)對(duì)象上的字符串鍵屬性。
  • value:要賦給屬性的值。
  • receiver:接收最初賦值的對(duì)象。

捕獲器不變式 如果 target.property 不可寫(xiě)且不可配置,則不能修改目標(biāo)屬性的值。 如果 target.property 不可配置且[[Set]]特性為 undefined,則不能修改目標(biāo)屬性的值。 在嚴(yán)格模式下,處理程序中返回 false 會(huì)拋出 TypeError。

4.3 has()

has()捕獲器會(huì)在 in 操作符中被調(diào)用。對(duì)應(yīng)的反射 API 方法為 Reflect.has()。

const myTarget = {};
const proxy = new Proxy(myTarget, {
  has(target, property) {
    console.log("has()");
    return Reflect.has(...arguments);
  },
});
"foo" in proxy; //觸發(fā) has()捕獲器

返回值 has()必須返回布爾值,表示屬性是否存在。返回非布爾值會(huì)被轉(zhuǎn)型為布爾值。

攔截的操作

  • property in proxy
  • property in Object.create(proxy)
  • with(proxy) {(property);}
  • Reflect.has(proxy, property)

捕獲器處理程序參數(shù)

  • target:目標(biāo)對(duì)象。
  • property:引用的目標(biāo)對(duì)象上的字符串鍵屬性。

捕獲器不變式 如果 target.property 存在且不可配置,則處理程序必須返回 true。 如果 target.property 存在且目標(biāo)對(duì)象不可擴(kuò)展,則處理程序必須返回 true。

4.4 deleteProperty()

deleteProperty()捕獲器會(huì)在 delete 操作符中被調(diào)用。對(duì)應(yīng)的反射 API 方法為 Reflect.deleteProperty()。

const myTarget = {};
const proxy = new Proxy(myTarget, {
  deleteProperty(target, property) {
    console.log("deleteProperty()");
    return Reflect.deleteProperty(...arguments);
  },
});
delete proxy.foo; // 觸發(fā)deleteProperty()捕獲器
  • 返回值 deleteProperty()必須返回布爾值,表示刪除屬性是否成功。返回非布爾值會(huì)被轉(zhuǎn)型為布爾值。
  • 攔截的操作
    • delete proxy.property
    • delete proxy[property]
    • Reflect.deleteProperty(proxy, property)
  • 捕獲器處理程序參數(shù)
    • target:目標(biāo)對(duì)象。
    • property:引用的目標(biāo)對(duì)象上的字符串鍵屬性。
  • 捕獲器不變式 如果自有的 target.property 存在且不可配置,則處理程序不能刪除這個(gè)屬性。

4.5 apply()

apply()捕獲器會(huì)在調(diào)用函數(shù)時(shí)中被調(diào)用。對(duì)應(yīng)的反射 API 方法為 Reflect.apply()。

const myTarget = () => {};
const proxy = new Proxy(myTarget, {
  apply(target, thisArg, ...argumentsList) {
    console.log("apply()");
    return Reflect.apply(...arguments);
  },
});
proxy(); // 觸發(fā)apply()捕獲器
  • 返回值 返回值無(wú)限制。
  • 攔截的操作
    • proxy(...argumentsList)
    • Function.prototype.apply(thisArg, argumentsList)
    • Function.prototype.call(thisArg, ...argumentsList)
    • Reflect.apply(target, thisArgument, argumentsList)
  • 捕獲器處理程序參數(shù)
    • target:目標(biāo)對(duì)象。
    • thisArg:調(diào)用函數(shù)時(shí)的 this 參數(shù)。
    • argumentsList:調(diào)用函數(shù)時(shí)的參數(shù)列表
  • 捕獲器不變式 target 必須是一個(gè)函數(shù)對(duì)象。

4.6 construct()

construct()捕獲器會(huì)在 new 操作符中被調(diào)用。對(duì)應(yīng)的反射 API 方法為 Reflect.construct()。

const myTarget = function () {};
const proxy = new Proxy(myTarget, {
  construct(target, argumentsList, newTarget) {
    console.log("construct()");
    return Reflect.construct(...arguments);
  },
});
new proxy(); // 觸發(fā)construct()捕獲器
  • 返回值 construct()必須返回一個(gè)對(duì)象。
  • 攔截的操作
    • new proxy(...argumentsList)
    • Reflect.construct(target, argumentsList, newTarget)
  • 捕獲器處理程序參數(shù)
    • target:目標(biāo)構(gòu)造函數(shù)
    • argumentsList:傳給目標(biāo)構(gòu)造函數(shù)的參數(shù)列表。
    • newTarget:最初被調(diào)用的構(gòu)造函數(shù)。
  • 捕獲器不變式 target 必須可以用作構(gòu)造函數(shù)。

還有另外七種捕獲器:

  • defineProperty()捕獲器會(huì)在 Object.defineProperty()中被調(diào)用。
  • getOwnPropertyDescriptor()捕獲器會(huì)在 Object.getOwnPropertyDescriptor()中被調(diào) 用。
  • ownKeys()捕獲器會(huì)在 Object.keys()及類(lèi)似方法中被調(diào)用。
  • getPrototypeOf()捕獲器會(huì)在 Object.getPrototypeOf()中被調(diào)用。
  • setPrototypeOf()捕獲器會(huì)在 Object.setPrototypeOf()中被調(diào)用。
  • isExtensible()捕獲器會(huì)在 Object.isExtensible()中被調(diào)用。
  • preventExtensions()捕獲器會(huì)在 Object.preventExtensions()中被調(diào)用。

這七種捕獲器詳細(xì)介紹可參考MDN - Proxy

參考博客

《Javascript 高級(jí)程序設(shè)計(jì)(第 4 版)》(JS 紅寶書(shū))

MDN - Proxy

MDN - Reflect

以上就是JS 中Proxy代理和 Reflect反射方法示例詳解的詳細(xì)內(nèi)容,更多關(guān)于JS Proxy代理Reflect反射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript事件詳細(xì)講解

    JavaScript事件詳細(xì)講解

    本文給大家介紹js事件詳解,涉及到事件流,事件處理,事件對(duì)象等方面的知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧
    2016-06-06
  • Typescript3.9 常用新特性一覽(推薦)

    Typescript3.9 常用新特性一覽(推薦)

    這篇文章主要介紹了Typescript3.9 常用新特性一覽,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • javascript 對(duì)象比較實(shí)現(xiàn)代碼

    javascript 對(duì)象比較實(shí)現(xiàn)代碼

    js對(duì)象比較實(shí)現(xiàn)代碼。
    2009-04-04
  • js對(duì)象淺拷貝和深拷貝詳解

    js對(duì)象淺拷貝和深拷貝詳解

    這篇文章主要為大家詳細(xì)介紹了JavaScript對(duì)象的淺拷貝和深拷貝代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • 三分鐘帶你快速學(xué)會(huì)微信小程序的條件渲染

    三分鐘帶你快速學(xué)會(huì)微信小程序的條件渲染

    所謂的條件渲染就是判斷是否需要把代碼渲染到展示頁(yè)面上,下面這篇文章主要給大家介紹了關(guān)于微信小程序條件渲染的相關(guān)資料,文中通過(guò)實(shí)例代碼和圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Javascript讀取json文件方法實(shí)例總結(jié)

    Javascript讀取json文件方法實(shí)例總結(jié)

    json文件是一種輕量級(jí)的數(shù)據(jù)交互格式,下面這篇文章主要給大家介紹了關(guān)于Javascript讀取json文件方法的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • javascript 函數(shù)聲明與函數(shù)表達(dá)式的區(qū)別介紹

    javascript 函數(shù)聲明與函數(shù)表達(dá)式的區(qū)別介紹

    javascript中的函數(shù)聲明與函數(shù)表達(dá)式使用比較頻繁,可能很多的朋友都不知道他們之間的區(qū)別,在此為大家詳細(xì)介紹下,希望對(duì)大家有所幫助
    2013-10-10
  • 深入理解Javascript作用域與變量提升

    深入理解Javascript作用域與變量提升

    這篇文章主要是對(duì)Javascript作用域與變量提升進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助
    2013-12-12
  • js獲取光標(biāo)位置的最新方法

    js獲取光標(biāo)位置的最新方法

    這篇文章主要給大家介紹了關(guān)于js獲取光標(biāo)位置的最新方法,獲取光標(biāo)位置,最常用的方法就是使用Selection對(duì)象和Range對(duì)象,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • Code:loadScript( )加載js的功能函數(shù)

    Code:loadScript( )加載js的功能函數(shù)

    Code:loadScript( )加載js的功能函數(shù)...
    2007-02-02

最新評(píng)論