前言
技术从来不会受限于语言,受限你的,永远只是思想。(博主发现了一个有意思的机器学习库,TensorFlow的js版本,有兴趣的同学可以看一下https://www.tensorflow.org/api_docs)
=> 这里以Proxy
为例来进行编码,当然,我们需要事先了解一下Proxy的使用方法.
Proxy
就像一个代理器,当有人对目标对象进行处理(set、has、get 等等操作)的时候它会首先经过它,这时我们可以使用代码进行处理,此时Proxy
相当于一个中介或者叫代理人,它经常被用于代理模式中,可以做字段验证、缓存代理、访问控制等等。
1.Object.defineProperty
众所周知,vue
使用了Object.defineProperty
来做数据劫持,它是利用劫持对象的访问器,在属性值发生变化时我们可以获取变化,从而进行进一步操作。
// 例子:对对象属性a的拦截
const obj = { a: 1 }
Object.defineProperty(obj, 'a', {
get: function() {
console.log('get val')
},
set: function(newVal) {
console.log('set val:' + newVal)
}
})
2.与Object.defineProperty
相比,Proxy
的优势
-
数组作为特殊的对象,但
Object.defineProperty
无法监听数组变化。 -
Object.defineProperty
只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择。 -
Proxy 有多达 13 种拦截方法,不限于
apply、ownKeys、deleteProperty、has
等等是Object.defineProperty
不具备的。 -
Proxy
返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty
只能遍历对象属性直接修改 -
Proxy
作为新标准将受到浏览器厂商重点持续的性能优化
/*
* Vue主要有两个方法
* $proxy: 把输入对象属性代理到vue实例上
* $watch: 监听对象的变化
*/
class Vue {
constructor(data) {
this.$data = data
Object.keys(data).forEach(key => this.$proxy(key))
}
$watch(key, cb) {
new Watcher(this, key, cb)
}
$proxy(key) {
Reflect.defineProperty(this, key, {
configurable: true,
enumerable: true,
get: () => this.$data[key],
set: val => {
this._data[key] = val
}
})
}
}
/*
* Watcher
* 监听数据变化,并通知变化
*/
class Watcher {
constructor(vm, key, callback) {
this.vm = vm // vue实例
this.callback = callback // 回调:返回更新
this.key = key // 被订阅的数据
this.val = this.get() // 维护更新之前的数据
vm.$data = this.createProxy(vm.$data) // 为数据做代理
}
update(newVal) {
this.callback(newVal)
}
get() {
const val = this.vm.$data[this.key]
return val
}
createProxy(data) {
let _this = this
let handler = {
get(target, property) {
return Reflect.get(target, property)
},
set(target, property, value) {
let res = null
if (target[property] != value) {
const isOk = Reflect.set(target, property, value)
if (_this.key === property) {
// 同一层级
res = value
} else {
res = _this.get()
console.log(res)
}
_this.callback(res)
return isOk
}
}
}
return toDeepProxy(data, handler)
function toDeepProxy(object, handler) {
if (!isPureObject(object)) addSubProxy(object, handler)
return new Proxy(object, handler)
function addSubProxy(object, handler) {
for (let prop in object) {
if (typeof object[prop] == 'object') {
if (!isPureObject(object[prop])) addSubProxy(object[prop], handler)
object[prop] = new Proxy(object[prop], handler)
}
}
object = new Proxy(object, handler)
}
function isPureObject(object) {
if (typeof object !== 'object') {
return false
} else {
for (let prop in object) {
if (typeof object[prop] == 'object') {
return false
}
}
}
return true
}
}
}
}
双向绑定的完整例子已放在 codepen 上