前言
从事前端开发工作从实习开始算起到现在也一年多了,然后也只会三大流行框架之一的vue.js以及小程序开发,工作之余也只是偶尔看看书,补充补充知识。最近开始考虑跳槽的时候才发现自己还真的只是一个菜鸟,因此想好好静下心来好好学习,沉淀自己,写这个博客记录一下自己的学习历程。
背景
学习vue早期源码学习系列之一的时候看到了WatchJS这个库可以做到监听数据的变化,于是也想学着youngwind介绍vue早期源码学习的思路开扒WatchJS。
效果
我们需要实现下面的效果
// 定义一个对象
var ex = {
attr1: 'attr1',
attr2: 'attr2'
};
// 监听attr1属性
ex.onWatch('attr1', function () {
console.log('attr1 change')
})
// 改变attr1属性触发回调
ex.attr1 = 'new attr1'
复制代码
思路
- 首先要把
onWatch
方法添加到Object.prototype
上,这样任何一个对象都可以调用onWatch
方法了。 - 通过
Object.defineProperty
劫持对象的属性,在有变化的时候通知订阅者执行相应的函数。
简单实现
var watchjs = {
isArray: function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
},
// 用来添加修改对象的自有属性
defineGetAndSet: function(obj, propName, setter, getter) {
Object.defineProperty(obj, propName, {
enumerable: true, // 能被枚举,监听的对象属性应该可以被枚举,即hasOwnProperty访问到
configurable: true, // enumerable, writable等属性描述符可以被改变
set: setter,
get: getter
})
},
// 用来添加object.prototype上的方法
defineProp: function(obj, propName, value) {
Object.defineProperty(obj, propName, {
enumerable: false, // 不能被枚举,在Object.property上自定义的属性不应该被枚举
configurable: true, // enumerable, writable等属性描述符可以被改变
writable: false, // 不可被重写,防止被他人重写
value: value // 设置值,与setter, getter不共存
})
}
}
watchjs.defineProp(Object.prototype, 'onWatch', function(prop, handler) {
var obj = this // 被监听的对象
var val = obj[prop]
if (!obj.handlers) {
watchjs.defineProp(obj, 'handlers', {})
}
// 将监听的对象的属性和响应的回调函数以键值对的形式存在被监听的对象的handlers属性中
if (!obj.handlers.hasOwnProperty(prop)) {
obj.handlers[prop] = [handler]
} else {
obj.handlers[prop].push(handler)
}
var getter = function() {
return val
}
var setter = function(newVal) {
// 当属性值有变化时执行相应的回调函数
if (val !== newVal) {
val = newVal
obj.emitWatch(prop)
}
}
watchjs.defineGetAndSet(obj, prop, setter, getter)
})
watchjs.defineProp(Object.prototype, 'emitWatch', function(prop) {
var obj = this
if (obj.handlers.hasOwnProperty(prop)) {
for (let i = 0; i < obj.handlers[prop].length; i++) {
obj.handlers[prop][i]()
}
}
})
复制代码