本文是 重温基础 系列文章的第十四篇。
这是第一个基础系列的最后一篇,后面会开始复习一些中级的知识了,欢迎持续关注呀!
接下来会统一整理到个人【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 |
欢迎关注微信公众号【前端自习课】天天早晨,与您一块儿学习一篇优秀的前端技术博文 .