实现监听数组方法

var ArrayProxy = Object.create(Array.prototype),
methods = ['push','pop','shift','unshift','splice','sort','reverse'];
function defProtected(obj, key, val, enumerable, configurable) {
// 如果是用户添加的方法则不监听
if (obj.hasOwnProperty(key)) return
Object.defineProperty(obj, key, {
value : val,
// 不可枚举
enumerable : !!enumerable,
// 不可配置
configurable : !!configurable
})
}
// 监听原生数组方法
methods.forEach(function (method) {
// ArrayProxy监听的对象 ,method监听的方法,第三个返回一个value
defProtected(ArrayProxy, method, function () {
// 这里面的this表示当前调用的数组
var result = Array.prototype[method].apply(this, arguments)
// 调用数组的__observer__里面的emit方法,触发更新。
this.__observer__.emit('mutate', this.__observer__.path, this, {
method: method,
args: slice.call(arguments),
result: result
})
return result
}, !hasProto)
});

我们可以看到在这段代码中并没有对数组进行get和set监听,这也是为什么在vue中给数组直接赋值不会触发更新的主要原因。

数组remove和replace方法

var hasProto = ({}).__proto__;

function def(obj, key, val, enumerable, configurable) {
if (obj.hasOwnProperty(key)) return
Object.defineProperty(obj, key, {
value : val,
enumerable : !!enumerable,
configurable : !!configurable
})
} var ArrayProxy = Object.create(Array.prototype); // 给数组添加remove和replace方法
var extensions = {
remove: function (index) {
/*
如果index是一个函数,则调用这个函数并且判断返回值,如果返回值为true则删除,false不删除 比如下面这个,删除index大于5的项
remove(function(index){
return index > 5;
}); */
if (typeof index === 'function') {
var i = this.length,
removed = []
while (i--) {
if (index(this[i])) {
removed.push(this.splice(i, 1)[0])
}
}
// 将删除的项返回,返回后为新数组
return removed.reverse()
} else {
// 这个判断是为了实现如果数组项是字符串也能删除
if (typeof index !== 'number') {
index = this.indexOf(index)
}
if (index > -1) {
return this.splice(index, 1)[0]
}
}
},
replace: function (index, data) {
if (typeof index === 'function') {
var i = this.length,
replaced = [],
replacer
while (i--) {
replacer = index(this[i])
/* 这里之所以不是直接判断if(replacer)是因为这里的目的就是实现替换功能,而如果值不是undefined说明用户有返回值而只要有返回值就应该给它替换。 */
if (replacer !== undefined) {
replaced.push(this.splice(i, 1, replacer)[0])
}
}
return replaced.reverse()
} else {
if (typeof index !== 'number') {
index = this.indexOf(index)
}
if (index > -1) {
return this.splice(index, 1, data)[0]
}
}
}
} for (var method in extensions) {
// 给ArrayProxy原型添加remove,replace方法,并且监听
def(ArrayProxy, method, extensions[method], !hasProto)
}

实现监听对象方法

/**
* 根据类型观察对象,入口
*/
function watch (obj, path, observer) {
var type = typeOf(obj)
if (type === 'Object') {
watchObject(obj, path, observer)
} else if (type === 'Array') {
watchArray(obj, path, observer)
}
} /**
* 监听对象变化,但不监听对象中开头为$和_的属性和方法,入口
*/
function watchObject (obj, path, observer) {
for (var key in obj) {
var keyPrefix = key.charAt(0)
if (keyPrefix !== '$' && keyPrefix !== '_') {
bind(obj, key, path, observer)
}
}
} /**
* 监听数组方法,并将其原型挂载到ArrayProxy上,入口
* ArrayProxy方法实现了一些变异的数组方法以及扩展,这是实现对数组方法监听的基础
*/
function watchArray (arr, path, observer) {
def(arr, '__observer__', observer)
observer.path = path
if (hasProto) {
arr.__proto__ = ArrayProxy
} else {
for (var key in ArrayProxy) {
def(arr, key, ArrayProxy[key])
}
}
} /*
* 具体实现对象监听的方法
*/
function bind (obj, key, path, observer) {
var val = obj[key],
watchable = isWatchable(val),
values = observer.values,
fullKey = (path ? path + '.' : '') + key
values[fullKey] = val
// 触发set事件
observer.emit('set', fullKey, val)
Object.defineProperty(obj, key, {
enumerable: true,
get: function () {
// only emit get on tip values
if (depsOb.active && !watchable) {
observer.emit('get', fullKey)
}
return values[fullKey]
},
set: function (newVal) {
values[fullKey] = newVal
ensurePaths(key, newVal, values)
observer.emit('set', fullKey, newVal)
// 被赋值,监听新对象
watch(newVal, fullKey, observer)
}
})
watch(val, fullKey, observer)
} /**
* 只监听数组和对象
*/
function isWatchable (obj) {
var type = typeOf(obj)
return type === 'Object' || type === 'Array'
}

最新文章

  1. freemarker种种
  2. java多线程系类:JUC线程池:03之线程池原理(二)(转)
  3. TCP、UDP、HTTP、SOCKET之间的区别
  4. No.1 S2错题
  5. 配置ogg异构oracle-mysql(1)基础环境配置
  6. Socket通信(转)
  7. c++各种数据类型表示范围
  8. virtualbox 虚拟3台虚拟机搭建hadoop集群
  9. Android项目打包开启proguard的混淆优化带来的问题
  10. JQuery学习笔记——层级选择器
  11. F - Capture
  12. JSP编译成Servlet(五)JDT Compiler编译器
  13. openfire课程
  14. 教师信息管理系统(方式一:数据库为oracle数据库;方式二:存储在文件中)
  15. Vim 安装、配置及复制粘贴操作
  16. Selenium·自动化框架集成
  17. Flask源码复习之路由
  18. [转]VR原理讲解及开发入门
  19. linux sort 多列正排序,倒排序
  20. (解释文)My SQL中主键为0和主键自排约束的关系

热门文章

  1. php Pthread 多线程 Worker
  2. android nostra13
  3. Python 多进程编程之 进程间的通信(Queue)
  4. rsync简介与rsync+inotify配置实时同步数据
  5. 团队-爬取豆瓣电影TOP250-代码设计规范
  6. virtual和abstract的区别
  7. crontab定时时间解释
  8. you-get 安装和用法
  9. 如何将他人的SOPC工程转换为自己可以使用的工程
  10. pyinstaller打包程序 带图片