前言
Object.defineProperty对于前端的同学来说熟悉不过,Vue2.X实现响应式原理核心依赖这个api。通过对data里边的对象属性进行数据劫持,访问和修改数据的时候通知订阅器更新的界面,达到数据驱动界面的理念。在最新Vue3.X,尤大大通过Proxy代理的方式实现数据监听,性能要比前者更加优秀。
Object.defineProperty
1.定义以及语法
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty(obj, prop, descriptor)
- obj => 目标对象
- prop => 要定义或修改的属性的名称或 Symbol
- descriptor => 要定义或修改的属性描述符。
descriptor: {
value: ‘’,
configurable: true
enumerable: true,
writable: true,
get: function() {
},
set: function() {
}
}
value => 属性对应值
configurable => 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认false
enumerale => 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认false
writable => 当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。默认false
get/set => getter ,setter 属性被方法或修改会调用该函数
2.知识扩展
- Object.defineProperties(obj, props)
----方法跟Object.defineProperty差不多,跟字面意思一样,代表支持多个新增或修改属性
var obj = {};
Object.defineProperties(obj, {
'property1': { value: true, writable: true },
'property2': { value: 'Hello', writable: false }
});
- Object.getOwnPropertyDescriptor(obj, prop)
----方法返回指定对象上一个自有属性对应的属性描述符
- Object.isExtensible(obj)
----方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)
Object.preventExtensions,Object.seal 或 Object.freeze方法都可以标记一个对象为不可扩展
- Object.create(proto, propertiesObject)
proto---新创建对象的原型对象
propertiesObject---新创建的对象添加指定的属性值和对应的属性描述符
Proxy
概念
Proxy是ES6新增的api。本质是代理,包装的意思。是一个构造函数,需要通过new Proxy()来实现实例。但跟普通的构造函数又有区别,因为我们知道只要是函数都会有prototype。但是Proxy却是没有的。这是为什么呢?
typeof Proxy // 'function'
Proxy.prototype // undefined
const p = new Proxy(target, handler)
原因在于首先构造方法的 prototype 不一定是为对象的,可以置为 null 的。由于 Proxy 构造出来的实例对象是对 target 的一个代理,所以 Proxy 在构造过程中是不需要 prototype 进行初始化的。
- target => 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
- handler => 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
除了get和set来拦截读取和赋值操作之外,Proxy还支持对其他多种行为的拦截。下面是一个简单介绍,想要深入了解的可以去MDN上看看。
handler.getPrototypeOf()
handler.setPrototypeOf()
handler.isExtensible()
handler.preventExtensions()
handler.getOwnPropertyDescriptor()
handler.defineProperty()
handler.has() //in 操作符的捕捉器。
handler.get(target, propKey, receiver)
handler.set(target, propKey, value, receiver)
handler.deleteProperty() //delete 操作符的捕捉器。
handler.ownKeys()
handler.apply()
handler.construct() //new 操作符的捕捉器。
handler对象中优先重点关注一下标红的几个方法。
get(target, key, receiver)
set(target, key, value, receiver)
- target => 目标对象
- key => 目标属性
- receiver => 可选。proxy 实例本身(严格地说,是操作行为所针对的对象)
基本操作
实例1 无操作转发代理
const person = {
name: '黄忠'
}
const p = new Proxy(person, {}) //纯粹代理没有做任何操作
console.log(p.name) //黄忠
console.log(person.name) //黄忠
p.name = '赵云';
console.log(p.name) //赵云
console.log(person.name) //赵云
实例2
const person = {
name: '黄忠'
}
const p = new Proxy(person, {
get(target, key){
return target[key]
},
set(target, key, value) {
target[key] = value
return true
}
})
实例3 Proxy也可以作为其他对象的原型对象使用
const obj = Object.create(p);
console.log(obj.name) //黄忠
两者区别
虽然这样能够实现数组的监听,但是相比于proxy来说,还是比较麻烦的。
- 实现对象的深度监听,需要一次性递归到底。对于层级比较深的数据来说,计算量比较大。
- 无法监听新增属性/删除属性(但是vue2.0提供了另外的api,分别是Vue.set和Vue.delete)