欧美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(反射)的知識。

講解 Proxy 和 Reflect 前,我們需要先了解屬性描述符的作用,所以我們線簡單解釋一下屬性描述符的知識。

1.屬性描述符

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

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

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

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

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

2.Reflect

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

由于它類似于其他語言的反射,因此取名為 Reflect。

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

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

Reflect 里面提供了哪些 API 呢?

Reflect API用處等同于
Reflect.get(target, propertyKey)讀取對象 target 的屬性 propertyKey對象的屬性值讀取操作
Reflect.set(target, propertyKey, value)設(shè)置對象 target 的屬性 propertyKey 的值為 value對象的屬性賦值操作
Reflect.has(target, propertyKey)判斷一個(gè)對象是否擁有一個(gè)屬性in 操作符
Reflect.defineProperty(target, propertyKey, attributes)類似于 Object.defineProperty,不同的是如果配置出現(xiàn)問題,返回 false 而不是報(bào)錯(cuò)Object.defineProperty
Reflect.deleteProperty(target, propertyKey)刪除一個(gè)對象的屬性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è)對象new 操作符

其他更多的 Reflect API

3.Proxy

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

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

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

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

3.1 創(chuàng)建空代理

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

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

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

const target = {
 id: 'target'
}; //target:目標(biāo)對象
const handler = {}; //handler:是一個(gè)普通對象,其中可以重寫底層實(shí)現(xiàn)
//創(chuàng)建空對象
const proxy = new Proxy(target, handler);
// id 屬性會訪問同一個(gè)值
console.log(target.id); // target
console.log(proxy.id); // target
// 給目標(biāo)屬性賦值會反映在兩個(gè)對象上 因?yàn)閮蓚€(gè)對象訪問的是同一個(gè)值
target.id = 'foo';
console.log(target.id); // foo
console.log(proxy.id); // foo
// 給代理屬性賦值會反映在兩個(gè)對象上 因?yàn)檫@個(gè)賦值會轉(zhuǎn)移到目標(biāo)對象
proxy.id = 'bar';
console.log(target.id); // bar
console.log(proxy.id); // bar
// hasOwnProperty()方法在兩個(gè)地方  也都會應(yīng)用到目標(biāo)對象
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)格相等可以用來區(qū)分代理和目標(biāo)
console.log(target === proxy); // false

3.2 定義捕獲器

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

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

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

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

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

3.3 捕獲器不變式

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

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

比如,如果目標(biāo)對象有一個(gè)不可配置且不可寫的數(shù)據(jù)屬性,那么在捕獲器返回一個(gè)與該屬性不同的值時(shí),會拋出 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 可撤銷代理

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

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

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

撤銷代理之后再調(diào)用代理會拋出 TypeError,撤銷函數(shù)和代理對象是在實(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()捕獲器會在獲取屬性值的操作中被調(diào)用。對應(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()捕獲器

返回值 返回值無限制。

攔截的操作

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

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

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

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

4.2 set()

set()捕獲器會在設(shè)置屬性值的操作中被調(diào)用。對應(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)格模式下會拋出 TypeError。

攔截的操作

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

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

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

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

4.3 has()

has()捕獲器會在 in 操作符中被調(diào)用。對應(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()必須返回布爾值,表示屬性是否存在。返回非布爾值會被轉(zhuǎn)型為布爾值。

攔截的操作

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

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

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

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

4.4 deleteProperty()

deleteProperty()捕獲器會在 delete 操作符中被調(diào)用。對應(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()必須返回布爾值,表示刪除屬性是否成功。返回非布爾值會被轉(zhuǎn)型為布爾值。
  • 攔截的操作
    • delete proxy.property
    • delete proxy[property]
    • Reflect.deleteProperty(proxy, property)
  • 捕獲器處理程序參數(shù)
    • target:目標(biāo)對象。
    • property:引用的目標(biāo)對象上的字符串鍵屬性。
  • 捕獲器不變式 如果自有的 target.property 存在且不可配置,則處理程序不能刪除這個(gè)屬性。

4.5 apply()

apply()捕獲器會在調(diào)用函數(shù)時(shí)中被調(diào)用。對應(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()捕獲器
  • 返回值 返回值無限制。
  • 攔截的操作
    • proxy(...argumentsList)
    • Function.prototype.apply(thisArg, argumentsList)
    • Function.prototype.call(thisArg, ...argumentsList)
    • Reflect.apply(target, thisArgument, argumentsList)
  • 捕獲器處理程序參數(shù)
    • target:目標(biāo)對象。
    • thisArg:調(diào)用函數(shù)時(shí)的 this 參數(shù)。
    • argumentsList:調(diào)用函數(shù)時(shí)的參數(shù)列表
  • 捕獲器不變式 target 必須是一個(gè)函數(shù)對象。

4.6 construct()

construct()捕獲器會在 new 操作符中被調(diào)用。對應(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è)對象。
  • 攔截的操作
    • 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()捕獲器會在 Object.defineProperty()中被調(diào)用。
  • getOwnPropertyDescriptor()捕獲器會在 Object.getOwnPropertyDescriptor()中被調(diào) 用。
  • ownKeys()捕獲器會在 Object.keys()及類似方法中被調(diào)用。
  • getPrototypeOf()捕獲器會在 Object.getPrototypeOf()中被調(diào)用。
  • setPrototypeOf()捕獲器會在 Object.setPrototypeOf()中被調(diào)用。
  • isExtensible()捕獲器會在 Object.isExtensible()中被調(diào)用。
  • preventExtensions()捕獲器會在 Object.preventExtensions()中被調(diào)用。

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

參考博客

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

MDN - Proxy

MDN - Reflect

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

相關(guān)文章

  • 查找Oracle高消耗語句的方法

    查找Oracle高消耗語句的方法

    這篇文章主要介紹了查找Oracle高消耗語句的方法,需要的朋友可以參考下
    2014-03-03
  • JavaScript編碼小技巧分享

    JavaScript編碼小技巧分享

    這篇文章適合任何一位基于JavaScript開發(fā)的開發(fā)者。我寫這篇文章主要涉及JavaScript中一些簡寫的代碼,幫助大家更好理解一些JavaScript的基礎(chǔ)。希望這些代碼能從不同的角度幫助你更好的理解JavaScript。
    2020-09-09
  • 如何利用javascript接收json信息并進(jìn)行處理

    如何利用javascript接收json信息并進(jìn)行處理

    這篇文章主要介紹了如何利用javascript接收json信息并進(jìn)行處理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Js+Flash實(shí)現(xiàn)訪問剪切板操作

    Js+Flash實(shí)現(xiàn)訪問剪切板操作

    最近需要遇到了這個(gè)問題點(diǎn)擊按鈕復(fù)制鏈接的功能果斷度娘谷哥,最后找到得解決方案ZeroClipBoard一款開源得js+Flash實(shí)現(xiàn)得剪切板操作
    2012-11-11
  • JavaScript oncopy事件用法實(shí)例解析

    JavaScript oncopy事件用法實(shí)例解析

    這篇文章主要介紹了JavaScript oncopy事件用法實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • js實(shí)現(xiàn)用戶注冊協(xié)議倒計(jì)時(shí)的方法

    js實(shí)現(xiàn)用戶注冊協(xié)議倒計(jì)時(shí)的方法

    這篇文章主要介紹了js實(shí)現(xiàn)用戶注冊協(xié)議倒計(jì)時(shí)的方法,是開發(fā)會員注冊功能時(shí)非常實(shí)用的技巧,需要的朋友可以參考下
    2015-01-01
  • JavaScript 循環(huán)結(jié)構(gòu)注意事項(xiàng)示例詳解

    JavaScript 循環(huán)結(jié)構(gòu)注意事項(xiàng)示例詳解

    循環(huán)作為算法與數(shù)據(jù)結(jié)構(gòu)中的基石,JS與其他編程語言一樣,都提供了多種循環(huán)結(jié)構(gòu)用于處理數(shù)據(jù), 本文給大家介紹JavaScript 循環(huán)結(jié)構(gòu)注意事項(xiàng),結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-06-06
  • JS實(shí)現(xiàn)拖拽的方法分析

    JS實(shí)現(xiàn)拖拽的方法分析

    這篇文章主要介紹了JS實(shí)現(xiàn)拖拽的方法,結(jié)合實(shí)例形式分析了JS拖拽的實(shí)現(xiàn)原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2016-12-12
  • bootstrap Validator 模態(tài)框、jsp、表單驗(yàn)證 Ajax提交功能

    bootstrap Validator 模態(tài)框、jsp、表單驗(yàn)證 Ajax提交功能

    這篇文章主要介紹了bootstrap Validator 模態(tài)框、jsp、表單驗(yàn)證 Ajax提交功能的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下
    2017-02-02
  • JS實(shí)現(xiàn)閃動的title消息提醒效果

    JS實(shí)現(xiàn)閃動的title消息提醒效果

    這篇文章主要介紹了JS實(shí)現(xiàn)閃動的title消息提醒效果,考慮并兼容了大部份的瀏覽器,需要的朋友可以參考下
    2014-06-06

最新評論