JS Proxy() API介紹

Proxy

攔截函數(shù)

get

  • target 代理的對象
  • propkeyName 讀取的屬性名稱
  • receiver Proxy 對象本身

如果一個屬性不可配置(configurable)且不可寫(writable)构眯,則 Proxy 不能修改該屬性伍掀,否則通過 Proxy 對象訪問該屬性會報錯括饶。


var proxy = new Proxy({a:1},{
    get:function(target,propkeyname,resiver){
        return {target,propkeyname,resiver}
    }
})

console.log(proxy.a)
/**
 * {
 *   target:{a:1},
 *   propkeyName:"a",
 *   resiver: Proxy({a:1}) // 這指的就是 proxy 這個對象
 * }
 */

dom 生成器

var dom = new Proxy(
  {},
  {
    get: function (target, elementType,receiver) {
      return function (attrs, ...children) {
        const ele = document.createElement(elementType);
        for (let attr of Object.keys(attrs)) {
          ele.setAttribute(attr, attrs[attr]);
        }
        for (let child of children) {
          if (typeof child === "string") {
            child = document.createTextNode(child);
          }
          ele.append(child);
        }
        return ele;
      };
    },
  }
);

var ele = dom.div(
  {},
  "Hello,world!",
  dom.a({ href: "www.baidu.com" }, "百度")
);
console.log(ele);

//  <div><a href="www.baidu.com">百度</a></div>

set

  • target 代理的對象
  • propkeyName 設(shè)置屬性的名稱
  • propkeyValue 設(shè)置屬性的值
  • receiver Proxy 對象本身

嚴(yán)格模式下:需要返回 true ,返回 false 或不返回會報錯

Object.defineProperty 不會觸發(fā)這個調(diào)用

如果一個屬性不可配置(configurable)且不可寫(writable)入录,則 Proxy 不能修改該屬性,否則通過 Proxy 對象不起作用晒夹。

  var handler = {
    set(target,propkeyName,propValue){
        console.log("-------> set");
        console.log("target",target);
        console.log("propkeyName",propkeyName);
        console.log("propValue",propValue);
        target[propkeyName] = propValue;
        console.log("<------- set");
      },

  };
  var target = {a:1};
  var proxy = new Proxy(target, handler);
  proxy.foo = 'bar'
  Object.defineProperty(proxy,'foo2',{ value: "bar2", writable: true, enumerable: true, configurable: true })

  console.log(proxy);

apply

  • target 代理的對象
  • ctx 函數(shù)上下文中的 this 指針
  • args 函數(shù)接收的參數(shù)

若代理對象是一個函數(shù)裆馒,那么可以攔截執(zhí)行調(diào)用

攔截調(diào)用

    var twice = {
    apply (target, ctx, args) {
        return Reflect.apply(...arguments) * 2;
    }
    };
    function sum (left, right) {
    return left + right;
    };
    var proxy = new Proxy(sum, twice);
    proxy(1, 2) // 6
    proxy.call(null, 5, 6) // 22
    proxy.apply(null, [7, 8]) // 30

    // 使用 Reflect 調(diào)用也會被攔截
    Reflect.apply(proxy, null, [9, 10]) // 38

has

  • target 代理的對象
  • propkeyName 查詢屬性的名稱

如果原對象不可配置或者禁止擴展,這時 has()攔截會報錯丐怯。

不檢查屬性來自自身還是原型喷好,只對 in 關(guān)鍵字生效,且對 for_in 無效

    var obj = { a: 10 };
    Object.preventExtensions(obj);

    var p = new Proxy(obj, {
    has: function(target, prop) {
        return false;
    }
    });

    'a' in p // TypeError is thrown

constructor

  • target 代理的函數(shù)
  • args 構(gòu)造函數(shù)參數(shù)
  • newTarget Proxy 對象

構(gòu)造函數(shù)攔截器必須返回一個對象

construct()方法中的 this 指向的是 handler读跷,而不是實例對象梗搅。

  const handler = {
    construct: function(target, args) {
      console.log(this === handler);
      return new target(...args);
    }
  }

  let p = new Proxy(function () {}, handler);
  new p() // true

deleteProperty

  • target 代理對象
  • propkeyName 屬性名稱

需要在該函數(shù)中將代理對象中對應(yīng)值刪除。需要返回 true 表示刪除成功,false/報錯表示刪除失敗

目標(biāo)對象自身的不可配置(configurable)的屬性无切,不能被 deleteProperty 方法刪除荡短,否則報錯。

  const handle = {
    deleteProperty:function(target,keyName){
        console.log("target",target);
          console.log("keyName",keyName);
          delete target[keyName];
          return true
      }
  }

  const proxy = new Proxy({a:1},handle);

  delete proxy.a;

  console.log(proxy);

defineProperty

  • target 代理對象
  • propkeyName 屬性值名稱
  • descriptor 屬性描述

當(dāng) set 與 defineProperty 同時存在的時候哆键,直接賦值只會調(diào)用 set掘托,但如果不存在 set 或者通過 Object.defineProperty 還是會被調(diào)用

返回值為 true,表示成功,如果無返回值會報錯

var handler = {
  set(target,propkeyName,propValue){
      console.log("-------> set");
      console.log("target",target);
      console.log("propkeyName",propkeyName);
      console.log("propValue",propValue);
      target[propkeyName] = propValue;
      console.log("<------- set");
    },
  defineProperty (target, key, descriptor) {
    console.log("-------> defineProperty");
    console.log("target",target);
    console.log("key",key);
    console.log("descriptor",descriptor);
    console.log("<------- defineProperty");
    Object.defineProperty(target,key,descriptor);
    return true;
  }
};
var target = {a:1};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar'
Object.defineProperty(proxy,'b',{ value: "bar", writable: true, enumerable: true, configurable: true })

console.log(proxy);

getOwnPropertyDescriptor

  • target 代理對象
  • propkeyName 屬性值名稱

必須返回一個屬性描述對象或者 undefinde籍嘹,否則會報錯

  var handler = {
    getOwnPropertyDescriptor (target, key) {
      if (key[0] === '_') {
        return;
      }
      return Object.getOwnPropertyDescriptor(target, key);
    }
  };
  var target = { _foo: 'bar', baz: 'tar' };
  var proxy = new Proxy(target, handler);
  Object.getOwnPropertyDescriptor(proxy, 'wat')
  // undefined
  Object.getOwnPropertyDescriptor(proxy, '_foo')
  // undefined
  Object.getOwnPropertyDescriptor(proxy, 'baz')
  // { value: 'tar', writable: true, enumerable: true, configurable: true }

getPrototypeOf()

  • Object.prototype.__proto__
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof

攔截以上方法,返回值必須是對象或是 null闪盔,否則會報錯。

另外辱士,如果目標(biāo)對象不可擴展(non-extensible)泪掀, getPrototypeOf()方法必須返回目標(biāo)對象的原型對象。


var proto = {};
var p = new Proxy({a:1}, {
  getPrototypeOf(target) {
    return proto;
  }
});
Object.getPrototypeOf(p) === proto // true

isExtensible

攔截 Object.isExtensible()操作识补。該函數(shù)必須返回布爾值族淮,其他值會被自動轉(zhuǎn)換辫红。

且返回值必須與目標(biāo)對象的 isExtensible 屬性保持一致凭涂,否則就會拋出錯誤

var target = {};
object.preventExtensions(target);
console.log(Object.isExtensions(target)); // false
var p = new Proxy(target, {
  isExtensible: function(target) {
    console.log("called");
    return true;
  }
});

Object.isExtensible(p) // 與 target.isExtensions 值不一致  會報錯

ownKeys

方法返回的數(shù)組成員,只能是字符串或 Symbol 值贴妻。如果有其他類型的值切油,或者返回的根本不是數(shù)組,就會報錯名惩。

如果目標(biāo)對象自身包含不可配置的屬性(configurable)澎胡,則該屬性必須被 ownKeys()方法返回,否則報錯娩鹉。

如果目標(biāo)對象是不可擴展的(non-extensible)攻谁,這時 ownKeys()方法返回的數(shù)組之中,必須包含原對象的所有屬性弯予,且不能包含多余的屬性戚宦,否則報錯。

攔截以下調(diào)用

  • Object.getOwnPropertyNames()

    • 會自動過濾 屬性名為 Symbol 的屬性锈嫩,即使攔截函數(shù)中返回
  • Object.getOwnPropertySymbols()

    • 會自動過濾 非屬性名為 Symbol 的屬性受楼,即使攔截函數(shù)中返回
  • Object.keys()

    • 會自動過濾 不存在、不可遍歷呼寸、屬性名為 Symbol 的屬性艳汽,即使攔截函數(shù)中返回
  • for...in 循環(huán)

    • 會自動過濾 不存在、不可遍歷对雪、屬性名為 Symbol 的屬性河狐,即使攔截函數(shù)中返回
  let target = {
    a: 1,
    b: 2,
    c: 3,
    [Symbol.for('secret')]: '4',
  };

  Object.defineProperty(target, 'key', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: 'static'
  });

  let handler = {
    ownKeys(target) {
      return ['a', 'd', Symbol.for('secret'), 'key'];
    }
  };

  let proxy = new Proxy(target, handler);

  console.log( Object.keys(proxy));
  // Array ["a"]
  console.log( Object.getOwnPropertyNames(proxy));
  // Array ["a", "d", "key"]
  console.log( Object.getOwnPropertySymbols(proxy));
  // Array ["a", "d", "key"]
  for(let key in proxy){
    console.log(key);
  }
  // “a”

preventExtensions

preventExtensions()方法攔截 Object.preventExtensions()。該方法必須返回一個布爾值,否則會被自動轉(zhuǎn)為布爾值甚牲。

只有目標(biāo)對象不可擴展時(即 Object.isExtensible(proxy)為 false)义郑,proxy.preventExtensions 才能返回 true,否則會報錯丈钙。

為解決這個問題非驮,可以在攔截函數(shù)中,將目標(biāo)對象禁止掉擴展

  var target = {};
  var proxy = new Proxy(target, {
    preventExtensions: function(target) {
      return true;
    }
  });

  Object.preventExtensions(proxy)
  // Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible

  // proxy 的攔截 與 Object.isExtensions(proxy)[值為 false] 不一致 會報錯

setPrototypeOf

setPrototypeOf()方法主要用來攔截 Object.setPrototypeOf()方法雏赦。

該方法只能返回布爾值劫笙,否則會被自動轉(zhuǎn)為布爾值。另外星岗,如果目標(biāo)對象不可擴展(non-extensible)填大,setPrototypeOf()方法不得改變目標(biāo)對象的原型。

  var handler = {
    setPrototypeOf (target, proto) {
      throw new Error('Changing the prototype is forbidden');
    }
  };
  var proto = {};
  var target = function () {}; // 函數(shù)或?qū)ο蠖际强梢缘?  var proxy = new Proxy(target, handler);
  Object.setPrototypeOf(proxy, proto);
  // Error: Changing the prototype is forbidden

Proxy.revocable

返回一個可取消的 Proxy 實例

Proxy.revocable()方法返回一個對象俏橘,該對象的 proxy 屬性是 Proxy 實例允华,revoke 屬性是一個函數(shù),可以取消 Proxy 實例寥掐。上面代碼中靴寂,當(dāng)執(zhí)行 revoke 函數(shù)之后,再訪問 Proxy 實例召耘,就會拋出一個錯誤百炬。

Proxy.revocable()的一個使用場景是,目標(biāo)對象不允許直接訪問污它,必須通過代理訪問剖踊,一旦訪問結(jié)束,就收回代理權(quán)衫贬,不允許再次訪問德澈。

  let target = {};
  let handler = {};

  let {proxy, revoke} = Proxy.revocable(target, handler);

  proxy.foo = 123;
  proxy.foo // 123

  revoke();
  proxy.foo // TypeError: Revoked

this 問題

在 Proxy 代理的情況下,目標(biāo)對象內(nèi)部的 this 關(guān)鍵字會指向 Proxy 代理固惯,也就是 handle梆造。

  const target = {
    m: function () {
      console.log(this === proxy);
    }
  };
  const handler = {};

  const proxy = new Proxy(target, handler);

  target.m() // false
  proxy.m()  // true
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缝呕,隨后出現(xiàn)的幾起案子澳窑,更是在濱河造成了極大的恐慌,老刑警劉巖供常,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊聋,死亡現(xiàn)場離奇詭異,居然都是意外死亡栈暇,警方通過查閱死者的電腦和手機麻裁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人煎源,你說我怎么就攤上這事色迂。” “怎么了手销?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵歇僧,是天一觀的道長。 經(jīng)常有香客問我锋拖,道長诈悍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任兽埃,我火速辦了婚禮侥钳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柄错。我一直安慰自己舷夺,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布售貌。 她就那樣靜靜地躺著给猾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趁矾。 梳的紋絲不亂的頭發(fā)上耙册,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天给僵,我揣著相機與錄音毫捣,去河邊找鬼。 笑死帝际,一個胖子當(dāng)著我的面吹牛蔓同,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹲诀,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼斑粱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脯爪?” 一聲冷哼從身側(cè)響起则北,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痕慢,沒想到半個月后尚揣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡掖举,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年快骗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡方篮,死狀恐怖名秀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情藕溅,我是刑警寧澤匕得,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站巾表,受9級特大地震影響耗跛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜攒发,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一调塌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惠猿,春花似錦、人聲如沸偶妖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趾访。三九已至态秧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扼鞋,已是汗流浹背申鱼。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留云头,地道東北人捐友。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像溃槐,于是被迫代替她去往敵國和親匣砖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一昏滴、Proxy概述 Proxy 用于修改某些操作的默認(rèn)行為猴鲫,等同于在語言層面做出修改,所以屬于一種“元編程”(me...
    傑仔閱讀 8,382評論 0 8
  • Proxy 概述 Proxy 用于修改某些操作的默認(rèn)行為谣殊,等同于在語言層面做出修改拂共,所以屬于一種“元編程”(met...
    pauljun閱讀 3,251評論 0 1
  • 一、概述 Proxy 用于修改某些操作的默認(rèn)行為蟹倾,等同于在語言層面做出修改匣缘,所以屬于一種“元編程”(meta pr...
    了凡和纖風(fēng)閱讀 265評論 0 1
  • 筆記猖闪,完全摘錄自阮一峰 proxy的概念 proxy英文原意是代理的意思,在ES6中肌厨,可以翻譯為"代理器"培慌。它主要...
    布蕾布蕾閱讀 22,497評論 0 8
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)、焦點柑爸、注意力吵护、語言聯(lián)想、情景聯(lián)想 觀點: 1.統(tǒng)計學(xué)現(xiàn)在叫數(shù)據(jù)分析表鳍,社會...
    Jenaral閱讀 5,721評論 0 5