监听一个变量的变化,当变量变化时执行某些操作,这类似现在流行的前端框架(例如 React、Vue等)中的数据绑定功能,在数据更新时自动更新 DOM 渲染,那么如何实现数据绑定喃?
本文给出两种思路:
- ES5 的 Object.defineProperty
- ES6 的 Proxy
ES5 的 Object.defineProperty
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
——MDN
- Object.defineProperty(obj, prop, descriptor)
其中:
- obj :要定义属性的对象
- prop :要定义或修改的属性的名称或 Symbol
- descriptor :要定义或修改的属性描述符
- var user = {
- name: 'sisterAn'
- }
- Object.defineProperty(user, 'name', {
- enumerable: true,
- configurable:true,
- set: function(newVal) {
- this._name = newVal
- console.log('set: ' + this._name)
- },
- get: function() {
- console.log('get: ' + this._name)
- return this._name
- }
- })
- user.name = 'an' // set: an
- console.log(user.name) // get: an
如果是完整的对变量的每一个子属性进行监听:
- // 监视对象
- function observe(obj) {
- // 遍历对象,使用 get/set 重新定义对象的每个属性值
- Object.keys(obj).map(key => {
- defineReactive(obj, key, obj[key])
- })
- }
- function defineReactive(obj, k, v) {
- // 递归子属性
- if (typeof(v) === 'object') observe(v)
- // 重定义 get/set
- Object.defineProperty(obj, k, {
- enumerable: true,
- configurable: true,
- get: function reactiveGetter() {
- console.log('get: ' + v)
- return v
- },
- // 重新设置值时,触发收集器的通知机制
- set: function reactiveSetter(newV) {
- console.log('set: ' + newV)
- v = newV
- },
- })
- }
- let data = {a: 1}
- // 监视对象
- observe(data)
- data.a // get: 1
- data.a = 2 // set: 2
通过 map 遍历,通过深度递归监听子子属性
注意, Object.defineProperty 拥有以下缺陷:
- IE8 及更低版本 IE 是不支持的
- 无法检测到对象属性的新增或删除
- 如果修改数组的 length ( Object.defineProperty 不能监听数组的长度),以及数组的 push 等变异方法是无法触发 setter 的
对此,我们看一下 vue2.x 是如何解决这块的?