摘要:是中新增的特性。首先來說,的提出是為了整合之前中存在的一些不太合理的地方。表示當前對象是否可擴展,返回一個布爾值。更完美的枚舉很多代碼使用字符串普通或凍結的對象作為枚舉。通過記錄這些訪問和修改信息,能記錄下對這個對象的所有操作記錄。
Reflect
Reflect 是ES6中新增的特性。它是一個普通對象,下面有13個靜態方法(enumerate在最終的發布版中被移除),可以再全局下訪問。它不能當做函數調用,也不可以用new操作符生成一個實例。
首先來說,Reflect的提出是為了整合之前JS中存在的一些不太合理的地方。
1)更加有用的返回值
Object.getOwnPropertyNames(Reflect) // ["defineProperty", "deleteProperty", "apply", "construct", "get", "getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"]Reflect.apply
功能跟Function.prototype.apply類似。
var a = [1,2,3]; Math.max.apply(null, a);// 3 Reflect.apply(Math.max, null, a); // 3Reflect.isExtensible / Reflect.preventExtensions
Reflect.preventExtensions用于讓一個對象變為不可擴展。它返回一個布爾值,表示是否操作成功。
Reflect.isExtensible表示當前對象是否可擴展, 返回一個布爾值。
var o = {}; Reflect.isExtensible(o);// true Object.isExtensible(o);// true Object.freeze(o); Reflect.isExtensible(o);// false Object.isExtensible(o);// false // 這兩方法的區別 Object.isExtensible(1) // false Reflect.isExtensible(1) // 報錯 var newO = {}; Reflect.isExtensible(newO);// true Reflect.preventExtensions(newO); // true 返回true,表示該操作成功 Reflect.isExtensible(newO);// falseReflect.set / Reflect.get
設置和獲取對象屬性, 這兩個方法還允許接受一個reciever,用于重定義setter和getter方法的上下文。
var o = {}; Reflect.set(o, "key", "value"); console.log(o); // {key: "value"} Reflect.get(o, "key"); // "value" Reflect.get(o, "nokey"); // undefined
下面演示一下receiver的使用方法
var o = { name: "O" } Object.defineProperty(o, "sayHi", { get(){ return "hi, I am " + this.name } }) o.sayHi; // "hi, I am O" var receiver = { name: "receiver" } // 下面是關鍵, 看好咯~ Reflect.get(o, "sayHi", receiver); // "hi, I am receiver" // 下面試驗了下Proxy的用法,可以忽略 var p = new Proxy(o, { get(o, k, p){ return o[k] ? Reflect.get(o, k, receiver) : undefined; }, set(o, k, v, p){ v += +new Date(); Reflect.set(o, k, v, p) } }); p.sayHi; // "hi, I am receiver" p.t = "time:"; p.t; // "time:1530865528713"Reflect.ownKeys
Reflect.ownKeys方法用于返回對象的所有屬性數組。
這個數組的排序是根據: 先顯示數字, 數字根據大小排序,然后是 字符串根據插入的順序排序, 最后是symbol類型的key也根據插入插入順序排序。
出現這種排序是因為,你給一個對象屬性賦值時候, 對象的key的排序規則就是先數字, 在字符串, 最后是symbol類型的數據。
Reflect.ownKeys(JSON); // ["parse", "stringify", Symbol(Symbol.toStringTag)] Object.getOwnPropertyNames(JSON); // ["parse", "stringify"] Object.getOwnPropertySymbols(JSON); // [Symbol(Symbol.toStringTag)]Reflect.has
用于判斷對象是否具有某個屬性
Reflect.has(JSON, "parse"); // true Reflect.has(JSON, "nokeyxx"); // false Reflect.has(1, "parse");// Uncaught TypeError: Reflect.has called on non-objectReflect.construct
功能類似于new操作符
var a = new Array(3); console.log(a); // [empty × 3] var b = Reflect.construct(Array, [3]) console.log(b); // [empty × 3]Reflect.defineProperty / Reflect.deleteProperty
Reflect.defineProperty 對應于Object.DefineProperty;
Reflect.deleteProperfy 對應于 delete 語句;
var o = {}; Object.defineProperty(o, "sayHi", { configurable: true, get(k){ return this[k] ? "Hi, I am " + this[k] : "I have no name"; } }); o.sayHi;// "I have no name" Reflect.defineProperty(o, "sayHello", { configurable: true, get(k){ return this[k] ? "Hi, I am " + this[k] : "I have no name"; } }) o.sayHello; // "I have no name" // 演示屬性刪除方法 delete o.sayHi; // true Reflect.deleteProperty(o, "sayHello"); // trueReflect.setPrototypeOf / Reflect.getPrototypeOf
這兩個方法用于設置和訪問對象的原型__proto__
function A(){}; A.prototype.name= "A"; Reflect.getPrototypeOf(A) // ? () { [native code] } var a = new A(); Reflect.getPrototypeOf(a) // {name: "A", constructor: ?} a.name; // "A" var c = {}; Reflect.setPrototypeOf(c, A.prototype); c.name; // "A" A.prototype.newName = "C"; c.newName; // "C"Reflect.getOwnPropertyDescriptor
基本等同于Object.getOwnPropertyDescriptor,用于得到指定屬性的描述對象。
Reflect.getOwnPropertyDescriptor(JSON, "parse"); // {value: ?, writable: true, enumerable: false, configurable: true} var c = {}; Reflect.defineProperty(c, "name", { configurable: true, enumerable: true, value: "CCC", writable: false }); Reflect.getOwnPropertyDescriptor(c, "name"); // {value: "CCC", writable: false, enumerable: true, configurable: true} c.name; // "CCC" c.name = 111; c.name; // "CCC", 因為writable=== falseProxy
Proxy 對象用于定義JS對象的基本操作行為(如屬性查找,賦值,枚舉,函數調用等)。
下面看一個修改對象get行為的demo:
var obj = { n : 1}; var p = new Proxy(obj, { get(t, k){ if( Reflect.has(t, k) ){ return t[k] + 100; } else { throw Error("no the property") } } }); p.n; // 101 p.p; // Uncaught Error: no the property
關于Proxy具體可以重新定義哪些基本操作:
defineProperty
deleteProperty
apply
construct
get
getOwnPropertyDescriptor
getPrototypeOf
has
isExtensible
ownKeys
preventExtensions
set
setPrototypeOf
這個列表跟 Reflect 具有的靜態方法是一致的。更多信息可以參考Proxy可以處理的方法列表 的描述
不說上面這些值基本操作,我們列舉下一下Proxy的一些使用場景。
設置默認值如果訪問一個對象中還沒有初始化的值,Proxy可以通過代理get方法,返回一個默認值。
function setDefault(defaults) { const handler = { get(t, k) { return Reflect.get(t, k) || defaults[k]; } } return new Proxy({},handler); } const d = setDefault({ name: "name" }); const log = console.log; log(d.name); // "name" d.name = "new name"; log(d.name); // "new name" log(d);隱藏私有屬性
Proxy可以代理對象的基礎操作,我們通過代理 get 方法,控制外部對對象內部屬性的方法。這樣便可以達到隱藏某些特性(私有屬性)的目的。
const log = console.log; function hideProp(obj,filter) { const handler = { get(t, k) { if(!filter(k)){ let v = Reflect.get(t, k); if(typeof v === "function"){ v = v.bind(t); } return v; } } } return new Proxy(obj,handler); } function filter(key){ return key[0] === "_"; } const o = { _p : "no access", p: "access", f: function(){ log(this._p); } }; const p = hideProp(o, filter); log(p.p); // access log(p._p); // undefined log(p.f()); // no access更完美的枚舉
很多JavaScript代碼使用字符串、普通或凍結的對象作為枚舉。這些解決方案有其類型的安全問題,而且通常容易出錯。
Proxy可以提供一個可行的替代方案。我們采用一個簡單的鍵值對象,并通過保護它免受(甚至無意的)修改而使其更加健壯。甚至比Object.freeze更加完善。(Object.freeze不允許修改,但不會引發錯誤,而是靜默報錯)
const log = console.log; function enumF(obj) { const handler = { get(t, k) { if(Reflect.has(t, k)){ let v = Reflect.get(t, k); if(typeof v === "function"){ v = v.bind(t); } return v; } }, set(t){ throw new TypeError("Enum is read only"); } } return new Proxy(obj,handler); } const o = { p: "access", f: function(){ log(this._p); } }; const p = enumF(o); log(p.p); // access p.p = "modified"; // TypeError // 對比Object.freeze const o1 = { p: "1" } Object.freeze(o1); o1.p = "modified"; log(o1.p);跟蹤對象變化
因為Proxy可以截獲對象的大部分基礎操作,因此我們實際上是可以跟蹤的對 對象的訪問和修改。通過記錄這些訪問和修改信息,能記錄下對這個對象的所有操作記錄。
const log = console.log; function trackChange(obj, onchange) { const handler = { deleteProperty(t, k){ const oldVal = t[k]; Reflect.deleteProperty(t, k); onchange(t, k , undefined, oldVal) }, set(t,k, v){ const oldVal = t[k]; Reflect.set(t, k , v); onchange(t, k, v, oldVal); } } return new Proxy(obj,handler); } const o = { p: "access", f: function(){ log(this._p); } }; const p = trackChange(o, (t,k,newV, oldV)=>{ log(`the property: ${k} change from old value [${oldV}] to new value [${newV}]`) }); p.p = "modified"; delete p.f;Proxy實現單例模式
來看看通過Proxy如何實現 單例模式。
const log = console.log; function singleInstance(obj){ let instance, handler = { construct:function(t){ if(!instance){ instance = Reflect.construct(...arguments); } return instance; } } return new Proxy(obj, handler); } function A(){ this.v = 1; } const p = singleInstance(A); const p1 = new p(); const p2 = new p(); log(p1.v); log(p2.v); p1.v = "new value"; log(p1.v); log(p2.v);總結
總結一下,上面的幾個例子中,我們基本上代理的都是對象的get,set,deleteProperty方法。這些都是對象的常見操作。用好了Proxy可以解決很多問題,例如vue.js數據雙向綁定的特性實際上也可以使用Proxy實現。
說了這么多,但是并不極力推薦使用Proxy,并不是出于兼容性問題。主要是性能方面,可以肯定的是,使用了Proxy肯定不如原有對象訪問速度更快。Proxy相當于在對象前添加了一道墻,任何操作都要先經過Proxy處理。這肯定會增加成本。寫這篇文章的主要目的是 希望能夠了解更多的東西,為自己解決問題增加另外一種方案。
【參考資料】
ecma 262:Reflect
為什么要使用Reflect的原因
實例解析 ES6 Proxy 使用場景
ES6 Features - 10 Use Cases for Proxy
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108182.html
摘要:即必須有返回值其中可接受三個參數,為目標對象,為屬性名,為實際接受的對象,默認為本例中新建的,如果單獨指出一個對象,可使指出對象受到相同的方法作用。且中的必須有返回值,的不用,這也正是因為在他之后還會執行所以不需要。 ES6 Proxy/Reflect Proxy 攔截器 proxy是es6的新特性,簡單來講,即是對目標對象的屬性讀取、設置,亦或函數調用等操作進行攔截(處理)。 let...
摘要:攔截實例作為構造函數調用的操作,比如。方法等同于,這提供了一種不使用,來調用構造函數的方法。方法對應,返回一個布爾值,表示當前對象是否可擴展。這是的一個提案,目前轉碼器已經支持。別名或修飾器在控制臺顯示一條警告,表示該方法將廢除。 Proxy Proxy 這個詞的原意是代理,用在這里表示由它來代理某些操作,可以譯為代理器,即用自己的定義覆蓋了語言的原始定義。ES6 原生提供 Proxy...
摘要:下面是實現的函數為異步函數添加自動超時功能超時時間異步函數包裝后的異步函數測試一下,是可以正常調用與訪問其上的屬性的好了,這便是吾輩最常用的一種方式了封裝高階函數,為函數添加某些功能。 JavaScript 中的 ES6 Proxy 吾輩博客的原文地址: https://blog.rxliuli.com/p/c3... 場景 就算只是扮演,也會成為真實的自我的一部分。對人類的精神來說...
摘要:查找并返回對象的屬性例例屬性部署了讀取函數返回的是的參數對象注意如果的第一個參數不是對象,則會報錯。它返回一個布爾值,表示是否操作成功用于返回對象的所有屬性使用和實現觀察者模式請參考觀察者模式 1、什么是Reflect?為操作對象而提供的新API 2、為什么要設計Reflect?(1)將Object對象的屬于語言內部的方法放到Reflect對象上,即從Reflect對象上拿Object...
摘要:理解元編程和是屬于元編程范疇的,能介入的對象底層操作進行的過程中,并加以影響。元編程中的元的概念可以理解為程序本身。中,便是兩個可以用來進行元編程的特性。在之后,標準引入了,從而提供比較完善的元編程能力。 導讀 幾年前 ES6 剛出來的時候接觸過 元編程(Metaprogramming)的概念,不過當時還沒有深究。今天在應用和學習中不斷接觸到這概念,比如 mobx 5 中就用到了 Pr...
閱讀 2270·2021-09-28 09:36
閱讀 2023·2021-09-22 15:14
閱讀 3632·2019-08-30 12:47
閱讀 3041·2019-08-30 12:44
閱讀 1236·2019-08-29 17:06
閱讀 541·2019-08-29 14:12
閱讀 981·2019-08-29 14:01
閱讀 2584·2019-08-29 12:17