简介
本篇文章只是为了补充一些遗漏的技术点,望大家批评多多指教
1.Proxy
proxy的字面意思的理解就是代理
用于定义基本操作的自定义行为,就是我们可以自定义某些行为,比如属性的查找,赋值,枚举,函数调用等。
实际上我们利用这个Proxy实现对编程语言进行编程,就是把一些内部的方式,内置的方法改变了,
这种编程就叫做语言编程。属性代理就做拦截。关于Proxy需要注意的地方有:Proxy内部的this关键字的指向是Proxy代理本身;
它的构建方式需要借助一个Proxy的构造函数new Proxy(target,handler),
其中target叫做目标对象,Proxy构造函数返回的是一个包装过后的目标对象,handler是代理的行为的函数。
基本用法:
let pro = new Proxy(target,handler);
new Proxy()表示生成一个Proxy实例
target参数表示所要拦截的目标对象
handler参数也是一个对象,用来定制拦截行为。
Proxy支持13种拦截行为(handle),简单介绍下其中2种拦截行为,
get
get(target, propKey, receiver)
用于拦截某个属性的读取操作,可以接受三个参数:
target:目标对象
propKey:属性名
receiver(可选):proxy 实例本身(严格地说,是操作行为所针对的对象)
举例:
let target = {
x: 10,
y: 20
};
let hanler = {
get: (obj, prop) => 42
};
target = new Proxy(target, hanler);
target.x; //42
target.y; //42
target.x; // 42
解析:
结果是一个对象将为任何属性访问操作都返回“42”。
这包括target.x,target['x'],Reflect.get(target, 'x')等。
set
set(target, propKey, value, receiver)
用于拦截某个属性的赋值操作,可以接受四个参数:
target:目标对象
propKey:属性名
value:属性值
receiver(可选):Proxy 实例本身
let hero = {
name: "赵云",
age: 25
}
let handler = {
get: (hero, name, ) => {
const heroName =`英雄名是${hero.name}`;
return heroName;
},
set:(hero,name,value)=>{
console.log(`${hero.name} change to ${value}`);
hero[name] = value;
return true;
}
}
let heroProxy = new Proxy(hero, handler);
console.log(heroProxy.name);
heroProxy.name = '黄忠';
console.log(heroProxy.name);
// --> 英雄名是赵云
// --> 赵云 change to 黄忠
// --> 英雄名是黄忠
解析:
创建hero对象为所要拦截的对象;
handler对象为拦截对象后执行的操作,这里get方法为读取操作,
即用户想要读取heroProxy中的属性时执行的拦截操作。
最后创建一个Proxy实例,当读取heroProxy中的属性时,
结果打印出来的总是“黄忠”字符串。
下面是 Proxy 支持的拦截操作一览,一共 13 种。
1、get(target, prop, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
2、set(target, prop, value, receiver):拦截对象属性的设置,最后返回一个布尔值。
3、apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,
比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
4、construct(target, args):方法用于拦截 new 操作符,比如 new proxy()。
为了使 new操作符在生成的Proxy对象上生效,
用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target 必须是有效的)。
5、has(target, prop): 判断对象是否有该属性 例如 prop in proxy的操作,返回一个布尔值。
6、deleteProperty(target, prop):拦截 delete proxy[prop] 的操作,返回一个布尔值。
7、ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、
Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。
该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
8、getOwnPropertyDescriptor(target, prop):拦截 Object.getOwnPropertyDescriptor(proxy, propKey),
返回属性的描述对象。
9、defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、
Object.defineProperties(proxy, propDescs),返回一个布尔值。。
10、preventExtensions(target):拦截 Object.preventExtensions(proxy),返回一个布尔值。
11、getPrototypeOf(target):拦截 Object.getPrototypeOf(proxy),返回一个对象。
12、isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
13、setPrototypeOf(target, proto):拦截 Object.setPrototypeOf(proxy, proto),返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
Proxy vs Object.defineProperty
在 Proxy 出现之前,JavaScript 中就提供过 Object.defineProperty,
允许对对象的 getter/setter 进行拦截,那么两者的区别在哪里呢?
1、Object.defineProperty 不能监听所有属性
Object.defineProperty 无法一次性监听对象所有属性,必须遍历或者递归来实现。
2、Object.defineProperty 无法监听新增加的属性
Proxy 可以监听到新增加的属性,而 Object.defineProperty 不可以,
需要你手动再去做一次监听。因此,在 Vue 中想动态监听属性,
一般用 Vue.set(girl, "hobby", "game") 这种形式来添加。
3、Object.defineProperty 无法响应数组操作
Object.defineProperty 可以监听数组的变化,
Object.defineProperty 无法对 push、shift、pop、unshift 等方法进行响应
4、Proxy 拦截方式更多
Proxy 提供了13种拦截方法,包括拦截 constructor、apply、deleteProperty 等等,
而 Object.defineProperty 只有 get 和 set。
5. Object.defineProperty 兼容性更好
Proxy 是新出的 API,兼容性还不够好,不支持 IE 全系列。
ES6中的Reflect
前言
我们来学习一下ES6的Reflect这个全局对象;首先我们要了解一下,为什么会新添加这么一个全局对象?如果你看过Reflect的一些函数,
你就会发现,这个对象上的方法基本上都可以从Object上面找到,找不到的那些,
也是可以通过对对象命令式的操作去实现的;那么为什么还要新添加一个呢?
Reflect介绍
1:Reflect上面的一些方法并不是专门为对象设计的,比如Reflect.apply方法,
它的参数是一个函数,如果使用Object.apply(func)会让人感觉很奇怪。
2:用一个单一的全局对象去存储这些方法,能够保持其它的JavaScript代码的整洁、干净。不然的话,
这些方法可能是全局的,或者要通过原型来调用。
3:将一些命令式的操作如delete,in等使用函数来替代,
这样做的目的是为了让代码更加好维护,更容易向下兼容;也避免出现更多的保留字。
4:Reflect正是ES6 为了操作对象而提供的新 API。
Reflect的本质其实就是建立起一套统一的API,便于新手学习和理解
基本特点
只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在。这些方法能够执行默认行为,
无论Proxy怎么修改默认行为,总是可以通过Reflect对应的方法获取默认行为。
修改某些Object方法的返回结果,让其变得更合理。比如,
Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,
而Reflect.defineProperty(obj, name, desc)则会返回false。让Object操作都变成函数行为。
某些Object操作是命令式,比如name in obj和delete obj[name],
而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)
让它们变成了函数行为。
下面我们来学习一下Reflect这个全局的对象,Reflect对象有下面这些静态的方法:
Reflect.apply
Reflect.construct
Reflect.defineProperty
Reflect.deleteProperty
Reflect.enumerate // 已废弃
Reflect.get
Reflect.getOwnPropertyDescriptor
Reflect.getPrototypeOf
Reflect.has
Reflect.isExtensible
Reflect.ownKeys
Reflect.preventExtensions
Reflect.set
Reflect.setPrototypeOf
举例
Reflect.get(target, name, receiver)
const { log } = console;
let myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
}
log(Reflect.get(myObject, 'foo')) // 1
log(Reflect.get(myObject, 'bar')) // 2
log(Reflect.get(myObject, 'baz')) // 3
如果name属性部署了读取函数(getter),则读取函数的this绑定receiver。
let myObject = {
foo: 1,
bar: 2,
get baz() {
return this.foo + this.bar;
},
};
var myReceiverObject = {
foo: 4,
bar: 4,
};
// 8
log(Reflect.get(myObject, 'baz', myReceiverObject))
如果第一个参数不是对象,Reflect.get方法会报错。
Reflect.get(1, 'foo') // 报错
Reflect.get(false, 'foo') // 报错
Reflect.set(target, name, value, receiver)
Reflect.set方法设置target对象的name属性等于value。
let myObject = {
foo: 1,
set bar(value) {
return this.foo = value;
},
}
log(myObject.foo) // 1
Reflect.set(myObject, 'foo', 2);
log(myObject.foo) // 2
Reflect.set(myObject, 'bar', 3)
log(myObject.foo) // 3
Reflect.has(obj, name)
Reflect.has方法对应name in obj里面的in运算符。
let myObject = {
foo: 1,
};
// 旧写法
'foo' in myObject // true
// 新写法
log(Reflect.has(myObject, 'foo')) // true
如果Reflect.has()方法的第一个参数不是对象,会报错。
Reflect.deleteProperty(obj, name)
Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性。
const myObj = { foo: 'bar' };
// 旧写法
delete myObj.foo;
// 新写法
Reflect.deleteProperty(myObj, 'foo');
该方法返回一个布尔值。如果删除成功,或者被删除的属性不存在,
返回true;删除失败,被删除的属性依然存在,返回false。
如果Reflect.deleteProperty()方法的第一个参数不是对象,会报错。
以上仅仅是三个例子,希望可以帮助大家,谢谢观看!!!