淺析 Proxy 和 Reflect

Proxy是ES6中提供的新的API,可以用來定義對象各種基本操作的自定義行為 (在文檔中被稱為traps,我覺得可以理解為一個(gè)針對對象各種行為的鉤子),拿它可以做很多有意思的事情,在我們需要對一些對象的行為進(jìn)行控制時(shí)可以使用 Reflect 將變得非常有效。

定義

ProxyReflect 都屬于 JavaScript 標(biāo)準(zhǔn)內(nèi)置對象 的 反射 分類,不同的是:

Proxy 對象用于定義基本操作的自定義行為(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。

Proxy 是 一個(gè) function

Reflect 是一個(gè)內(nèi)置的對象,它提供攔截 JavaScript 操作的方法。這些方法與proxy handlers的方法相同。Reflect不是一個(gè)函數(shù)對象,因此它是不可構(gòu)造的。

Reflect 是 一個(gè)對象

術(shù)語

包含捕捉器(trap)的占位符對象,可譯為處理器對象。

  • traps(各種行為的代理)

提供屬性訪問的方法。這類似于操作系統(tǒng)中捕獲器的概念。

  • target

被 Proxy 代理虛擬化的對象。它常被作為代理的存儲后端。根據(jù)目標(biāo)驗(yàn)證關(guān)于對象不可擴(kuò)展性或不可配置屬性的不變量(保持不變的語義)。

注:

被代理的對象, 都是淺拷貝(及傳址)。

語法

const p = new Proxy(target, handler)

參數(shù)

  • target
    要使用 Proxy 包裝的目標(biāo)對象(可以是任何類型的對象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)。

  • handler
    一個(gè)通常以函數(shù)作為屬性的對象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為。

適用場景

  • 代理getter
const handler = {
    get(obj, prop) {
        return prop in obj ? Reflect.get(obj, prop) : 37;  
    }
};

const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

console.log(p.a, p.b);      // 1, undefined
console.log('c' in p, p.c); // false, 37

通過 反射對象 來獲取屬性,如果沒有則返回 37

  • 無操作轉(zhuǎn)發(fā)代理
let target = {};
let p = new Proxy(target, {});

p.a = 37;   // 操作轉(zhuǎn)發(fā)到目標(biāo)

console.log(target.a);    // 37. 操作已經(jīng)被正確地轉(zhuǎn)發(fā)
  • 驗(yàn)證

通過代理,你可以輕松地驗(yàn)證向一個(gè)對象的傳值。下面的代碼借此展示了 set handler 的作用。

let validator = {
  set(obj, prop, value)  {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }
    // 利用反射類來設(shè)置屬性
    Reflect.set(obj, prop, value);
    // 表示成功
    return true;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

console.log(person.age); 
// 100

person.age = 'young'; 
// 拋出異常: Uncaught TypeError: The age is not an integer

person.age = 300; 
// 拋出異常: Uncaught RangeError: The age seems invalid
  • 擴(kuò)展構(gòu)造函數(shù)

方法代理可以輕松地通過一個(gè)新構(gòu)造函數(shù)來擴(kuò)展一個(gè)已有的構(gòu)造函數(shù)。這個(gè)例子使用了constructapply。

function extend(sup, base) {
  var descriptor = Object.getOwnPropertyDescriptor(
    base.prototype, "constructor"
  );
  base.prototype = Object.create(sup.prototype);
  var handler = {
    construct(target, args) {
      var obj = Object.create(base.prototype);
      this.apply(target, obj, args);
      return obj;
    },
    apply(target, that, args) {
      sup.apply(that, args);
      base.apply(that, args);
    }
  };
  var proxy = new Proxy(base, handler);
  descriptor.value = proxy;
  Object.defineProperty(base.prototype, "constructor", descriptor);
  return proxy;
}

var Person = function (name) {
  this.name = name
};

var Boy = extend(Person, function (name, age) {
  this.age = age;
});

Boy.prototype.sex = "M";

var Peter = new Boy("Peter", 13);
console.log(Peter.sex);  // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age);  // 13

解決對象屬性為undefined的問題

在實(shí)際應(yīng)用中,我們調(diào)用對象的多級屬性時(shí),當(dāng)屬性的父級不存在時(shí)經(jīng)常會報(bào) VM241:2 Uncaught TypeError: Cannot read property 'c' of undefined 的錯(cuò)誤,如下:

VM241:2 Uncaught TypeError: Cannot read property 'c' of undefined

那么,我們可以使用 遞歸的Proxy 來代理 traps 的 get 方法, 來解決這個(gè)問題:

let handlers = {
    get (target, property) {
        if (Reflect.has(target, property)) {
            return Reflect.get(target, property);
        } else {
            if (typeof target === 'undefined') {
                target = {};
            }
            if (typeof target[property] === 'undefined') {
                target[property] = {};
            }
            return new Proxy(target[property], handlers);
        }
    }
}
let a = {};
let proxy = new Proxy(a, handlers)
console.log(proxy.a.b.c);

結(jié)果如下:

proxy.a.b.c

可以看到,并未報(bào)錯(cuò),而是返回了一個(gè) Proxy 的實(shí)例對象。

他山之石

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 今天我們要來講一下Javascript中一個(gè)很有趣的類 -- Proxy類,類名翻譯成中文是代理的意思。在真...
    SF_1316閱讀 835評論 0 1
  • 一、Proxy1、概述Proxy取其英文意思即“代理”。所謂代理,是你要取得某樣?xùn)|西或?qū)ζ溥M(jìn)行某些操作的中間媒介,...
    貴在隨心閱讀 9,748評論 2 11
  • 一、Proxy 1.什么是proxy Proxy 可以理解成,在目標(biāo)對象之前架設(shè)一層“攔截”,外界對該對象的訪問,...
    錢羅羅_閱讀 855評論 0 0
  • mdn上的解釋: Proxy 對象用于定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數(shù)調(diào)用等)。 Refle...
    天驅(qū)丶閱讀 997評論 0 1
  • 久違的晴天,家長會。 家長大會開好到教室時(shí),離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,868評論 16 22

友情鏈接更多精彩內(nèi)容