本文是 重溫基礎 系列文章的第十四篇。
這是第一個基礎系列的最後一篇,後面會開始複習一些中級的知識了,歡迎持續關注呀!
接下來會統一整理到個人【Cute-JavaScript】的JavaScript基礎系列中。 html
今日感覺:獨樂樂不如衆樂樂。前端
系列目錄:git
本章節複習的是JS中的元編程,涉及的更多的是ES6的新特性。es6
元編程,其實我是這麼理解的:讓代碼自動寫代碼,能夠更改源碼底層的功能。
元,是指程序自己。
有理解不到位,還請指點,具體詳細的介紹,能夠查看維基百科 元編程 。
從ES6開始,JavaScrip添加了對Proxy
和Reflect
對象的支持,容許咱們鏈接並定義基本語言操做的自定義行爲(如屬性查找,賦值,枚舉和函數調用等),從而實現JavaScrip的元級別編程。github
Reflect
: 用於替換直接調用Object
的方法,並非一個函數對象,也沒有constructor
方法,因此不能用new
操做符。Proxy
: 用於自定義對象的行爲,如修改set
和get
方法,能夠說是ES5中Object.defineProperty()
方法的ES6升級版。Reflect
通常在Proxy
須要處理默認行爲的時候使用。參考資料:正則表達式
名稱 | 地址 |
---|---|
Reflect |
MDN Reflect |
Proxy |
MDN Proxy |
元編程 | MDN 元編程 |
本文主要從Proxy介紹,還會有幾個案例,實際看下怎麼使用。編程
proxy
用於修改某些操做的默認行爲,能夠理解爲一種攔截外界對目標對象訪問的一種機制,從而對外界的訪問進行過濾和修改,即代理某些操做,也稱「代理器」。json
基本語法:segmentfault
let p = new Proxy(target, handler);
proxy
實例化須要傳入兩個參數,target
參數表示所要攔截的目標對象,handler
參數也是一個對象,用來定製攔截行爲。數組
let p = new Proxy({}, { get: function (target, handler){ return 'leo'; } }) p.name; // leo p.age; // leo p.abcd; // leo
上述a
實例中,在第二個參數中定義了get
方法,來攔截外界訪問,而且get
方法接收兩個參數,分別是目標對象和所要訪問的屬性,因此無論外部訪問對象中任何屬性都會執行get
方法返回leo
。
注意:
Proxy
實例的對象才能使用這些操做。handler
沒有設置攔截,則直接返回原對象。let target = {}; let handler = {}; let p = new Proxy(target, handler); p.a = 'leo'; target.a; // 'leo'
同個攔截器函數,設置多個攔截操做:
let p = new Proxy(function(a, b){ return a + b; },{ get:function(){ return 'get方法'; }, apply:function(){ return 'apply方法'; } })
這裏還有一個簡單的案例:
let handler = { get : function (target, name){ return name in target ? target[name] : 16; } } let p = new Proxy ({}, handler); p.a = 1; console.log(p.a , p.b); // 1 16
這裏由於 p.a = 1
定義了p
中的a
屬性,值爲1
,而沒有定義b
屬性,因此p.a
會獲得1
,而p.b
會獲得undefined
從而使用name in target ? target[name] : 16;
返回的默認值16
;
Proxy
支持的13種攔截操做:
13種攔截操做的詳細介紹:打開阮一峯老師的連接。
get(target, propKey, receiver)
:攔截對象屬性的讀取,好比proxy.foo和proxy['foo']。
set(target, propKey, value, receiver)
:攔截對象屬性的設置,好比proxy.foo = v或proxy['foo'] = v,返回一個布爾值。
has(target, propKey)
:攔截propKey in proxy的操做,返回一個布爾值。
deleteProperty(target, propKey)
:攔截delete proxy[propKey]的操做,返回一個布爾值。
ownKeys(target)
:攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循環,返回一個數組。該方法返回目標對象全部自身的屬性的屬性名,而Object.keys()的返回結果僅包括目標對象自身的可遍歷屬性。
getOwnPropertyDescriptor(target, propKey)
:攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對象。
defineProperty(target, propKey, propDesc)
:攔截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一個布爾值。
preventExtensions(target)
:攔截Object.preventExtensions(proxy),返回一個布爾值。
getPrototypeOf(target)
:攔截Object.getPrototypeOf(proxy),返回一個對象。
isExtensible(target)
:攔截Object.isExtensible(proxy),返回一個布爾值。
setPrototypeOf(target, proto)
:攔截Object.setPrototypeOf(proxy, proto),返回一個布爾值。若是目標對象是函數,那麼還有兩種額外操做能夠攔截。
apply(target, object, args)
:攔截 Proxy 實例做爲函數調用的操做,好比proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args)
:攔截 Proxy 實例做爲構造函數調用的操做,好比new proxy(...args)。
使用Proxy.revocable
方法取消Proxy
實例。
let a = {}; let b = { get: function(target, name) { return "[[" + name + "]]"; } }; let revoke = Proxy.revocable(a, b); let proxy = revoke.proxy; proxy.age; // "[[age]]" revoke.revoke(); proxy.age; // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked proxy.age = 10; // Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked delete proxy.age; // Uncaught TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked typeof proxy; // "object"
const service = createWebService('http://le.com/data'); service.employees().than(json =>{ const employees = JSON.parse(json); }) function createWebService(url){ return new Proxy({}, { get(target, propKey, receiver{ return () => httpGet(url+'/'+propKey); }) }) }
經過Proxy
代理對象的set
和get
方法來進行攔截數據,像Vue
就是用數據攔截來實現數據綁定。
let handler = { // 攔截並處理get方法 // 能夠理解爲設置get方法返回的默認值 get : function (target, key){ return key in target ? target[key] : 30; }, // 攔截並處理set方法 // 能夠理解爲設置set方法的默認行爲 set : function (target, key, value){ if(key === "age"){ if (!Number.isInteger(value)){ throw new TypeError("age不是一個整數!"); } if (value > 200){ throw new TypeError("age不能大於200!"); } } // 保存默認行爲 target[key] = value; } } let p = new Proxy({}, handler); p.a = 10; // p.a => 10 p.b = undefined; // p.b => undefined p.c; // 默認值 30 p.age = 100; // p.age => 100 p.age = 300; // Uncaught TypeError: age不能大於200! p.age = "leo"; // Uncaught TypeError: age不是一個整數!
經過攔截handler.apply()
方法的調用,實現函數只能在1秒以後才能再次被調用,常常能夠用在防止重複事件的觸發。
let p = (fun, time) => { // 獲取最後點擊時間 let last = Date.now() - time; return new Proxy (fun, { apply(target, context, args){ if(Date.now() - last >= time){ fun.bind(target)(args); // 重複設置當前時間 last = Date.now(); } } }) } let p1 = () => { console.log("點擊觸發"); } let time = 1000; // 設置時間 let proxyObj = p(p1, time); // 監聽滾動事件 document.addEventListener('scroll', proxyObj);
經過攔截construct
方法,讓不一樣實例指向相同的constructer
,實現單例模式。
let p = function(fun){ let instance; let handler = { // 攔截construct方法 construct: function(targer, args){ if(!instance){ instance = new fun(); } return instance; } } return new Proxy(fun, handler); } // 建立一個construct案例 function Cons (){ this.value = 0; } // 建立實例 let p1 = new Cons(); let p2 = new Cons(); // 操做 p1.value = 100; // p1.value => 100 , p2.value => 0 // 由於不是相同實例 // 經過Proxy實現單例 let singleton = p(Cons); let p3 = new singleton(); let p4 = new singleton(); p3.value = 130; // p1.value => 130 , p2.value => 130 // 如今是相同實例
1. MDN 元編程
2. ES6中的元編程-Proxy & Reflect
本部份內容到這結束
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推薦 | https://github.com/pingan8787... |
JS小冊 | js.pingan8787.com |
歡迎關注微信公衆號【前端自習課】天天早晨,與您一塊兒學習一篇優秀的前端技術博文 .