实现监听数组方法

  1. var ArrayProxy = Object.create(Array.prototype),
  2. methods = ['push','pop','shift','unshift','splice','sort','reverse'];
  3. function defProtected(obj, key, val, enumerable, configurable) {
  4. // 如果是用户添加的方法则不监听
  5. if (obj.hasOwnProperty(key)) return
  6. Object.defineProperty(obj, key, {
  7. value : val,
  8. // 不可枚举
  9. enumerable : !!enumerable,
  10. // 不可配置
  11. configurable : !!configurable
  12. })
  13. }
  14. // 监听原生数组方法
  15. methods.forEach(function (method) {
  16. // ArrayProxy监听的对象 ,method监听的方法,第三个返回一个value
  17. defProtected(ArrayProxy, method, function () {
  18. // 这里面的this表示当前调用的数组
  19. var result = Array.prototype[method].apply(this, arguments)
  20. // 调用数组的__observer__里面的emit方法,触发更新。
  21. this.__observer__.emit('mutate', this.__observer__.path, this, {
  22. method: method,
  23. args: slice.call(arguments),
  24. result: result
  25. })
  26. return result
  27. }, !hasProto)
  28. });

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

数组remove和replace方法

  1. var hasProto = ({}).__proto__;
  2. function def(obj, key, val, enumerable, configurable) {
  3. if (obj.hasOwnProperty(key)) return
  4. Object.defineProperty(obj, key, {
  5. value : val,
  6. enumerable : !!enumerable,
  7. configurable : !!configurable
  8. })
  9. }
  10. var ArrayProxy = Object.create(Array.prototype);
  11. // 给数组添加remove和replace方法
  12. var extensions = {
  13. remove: function (index) {
  14. /*
  15. 如果index是一个函数,则调用这个函数并且判断返回值,如果返回值为true则删除,false不删除
  16. 比如下面这个,删除index大于5的项
  17. remove(function(index){
  18. return index > 5;
  19. });
  20. */
  21. if (typeof index === 'function') {
  22. var i = this.length,
  23. removed = []
  24. while (i--) {
  25. if (index(this[i])) {
  26. removed.push(this.splice(i, 1)[0])
  27. }
  28. }
  29. // 将删除的项返回,返回后为新数组
  30. return removed.reverse()
  31. } else {
  32. // 这个判断是为了实现如果数组项是字符串也能删除
  33. if (typeof index !== 'number') {
  34. index = this.indexOf(index)
  35. }
  36. if (index > -1) {
  37. return this.splice(index, 1)[0]
  38. }
  39. }
  40. },
  41. replace: function (index, data) {
  42. if (typeof index === 'function') {
  43. var i = this.length,
  44. replaced = [],
  45. replacer
  46. while (i--) {
  47. replacer = index(this[i])
  48. /*
  49. 这里之所以不是直接判断if(replacer)是因为这里的目的就是实现替换功能,而如果值不是undefined说明用户有返回值而只要有返回值就应该给它替换。
  50. */
  51. if (replacer !== undefined) {
  52. replaced.push(this.splice(i, 1, replacer)[0])
  53. }
  54. }
  55. return replaced.reverse()
  56. } else {
  57. if (typeof index !== 'number') {
  58. index = this.indexOf(index)
  59. }
  60. if (index > -1) {
  61. return this.splice(index, 1, data)[0]
  62. }
  63. }
  64. }
  65. }
  66. for (var method in extensions) {
  67. // 给ArrayProxy原型添加remove,replace方法,并且监听
  68. def(ArrayProxy, method, extensions[method], !hasProto)
  69. }

实现监听对象方法

  1. /**
  2. * 根据类型观察对象,入口
  3. */
  4. function watch (obj, path, observer) {
  5. var type = typeOf(obj)
  6. if (type === 'Object') {
  7. watchObject(obj, path, observer)
  8. } else if (type === 'Array') {
  9. watchArray(obj, path, observer)
  10. }
  11. }
  12. /**
  13. * 监听对象变化,但不监听对象中开头为$和_的属性和方法,入口
  14. */
  15. function watchObject (obj, path, observer) {
  16. for (var key in obj) {
  17. var keyPrefix = key.charAt(0)
  18. if (keyPrefix !== '$' && keyPrefix !== '_') {
  19. bind(obj, key, path, observer)
  20. }
  21. }
  22. }
  23. /**
  24. * 监听数组方法,并将其原型挂载到ArrayProxy上,入口
  25. * ArrayProxy方法实现了一些变异的数组方法以及扩展,这是实现对数组方法监听的基础
  26. */
  27. function watchArray (arr, path, observer) {
  28. def(arr, '__observer__', observer)
  29. observer.path = path
  30. if (hasProto) {
  31. arr.__proto__ = ArrayProxy
  32. } else {
  33. for (var key in ArrayProxy) {
  34. def(arr, key, ArrayProxy[key])
  35. }
  36. }
  37. }
  38. /*
  39. * 具体实现对象监听的方法
  40. */
  41. function bind (obj, key, path, observer) {
  42. var val = obj[key],
  43. watchable = isWatchable(val),
  44. values = observer.values,
  45. fullKey = (path ? path + '.' : '') + key
  46. values[fullKey] = val
  47. // 触发set事件
  48. observer.emit('set', fullKey, val)
  49. Object.defineProperty(obj, key, {
  50. enumerable: true,
  51. get: function () {
  52. // only emit get on tip values
  53. if (depsOb.active && !watchable) {
  54. observer.emit('get', fullKey)
  55. }
  56. return values[fullKey]
  57. },
  58. set: function (newVal) {
  59. values[fullKey] = newVal
  60. ensurePaths(key, newVal, values)
  61. observer.emit('set', fullKey, newVal)
  62. // 被赋值,监听新对象
  63. watch(newVal, fullKey, observer)
  64. }
  65. })
  66. watch(val, fullKey, observer)
  67. }
  68. /**
  69. * 只监听数组和对象
  70. */
  71. function isWatchable (obj) {
  72. var type = typeOf(obj)
  73. return type === 'Object' || type === 'Array'
  74. }

读vue-0.6-observer.js源码的更多相关文章

  1. vue.js源码精析

    MVVM大比拼之vue.js源码精析 VUE 源码分析 简介 Vue 是 MVVM 框架中的新贵,如果我没记错的话作者应该毕业不久,现在在google.vue 如作者自己所说,在api设计上受到了很多 ...

  2. 从template到DOM(Vue.js源码角度看内部运行机制)

    写在前面 这篇文章算是对最近写的一系列Vue.js源码的文章(https://github.com/answershuto/learnVue)的总结吧,在阅读源码的过程中也确实受益匪浅,希望自己的这些 ...

  3. 从Vue.js源码角度再看数据绑定

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/an ...

  4. 【转】从Vue.js源码看异步更新DOM策略及nextTick

    在使用vue.js的时候,有时候因为一些特定的业务场景,不得不去操作DOM,比如这样: <template> <div> <div ref="test" ...

  5. Vue.js源码——事件机制

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/an ...

  6. Vue.js 源码分析(一) 代码结构

    关于Vue vue是一个兴起的前端js库,是一个精简的MVVM.MVVM模式是由经典的软件架构MVC衍生来的,当View(视图层)变化时,会自动更新到ViewModel(视图模型),反之亦然,View ...

  7. Vue.js 源码构建(三)

    Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下. 构建脚本 通常一个基于 NPM 托管的项目都会有一个 package.json 文件,它是对项目的描述文 ...

  8. vue源码分析—Vue.js 源码构建

    Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下.(Rollup 中文网和英文网) 构建脚本 通常一个基于 NPM 托管的项目都会有一个 package.j ...

  9. 2018-11-23 手工翻译Vue.js源码:尝试重命名标识符与文本

    续前文: 手工翻译Vue.js源码第一步:14个文件重命名 对core/instance/索引中的变量, 方法进行重命名如下(题图): import { 混入初始化 } from './初始化' im ...

  10. vue.js源码学习分享(一)

    今天看了vue.js源码  发现非常不错,想一边看一遍写博客和大家分享 /** * Convert a value to a string that is actually rendered. *转换 ...

随机推荐

  1. centOS7搭建nexus私服

    1.保证JDK,MAVEN已安装,firewalld服务安装 PS:yum install firewalld 2.官网下载:https://www.sonatype.com/download-oss ...

  2. highcharts echarts比较

    1,highcharts底层是svg echarts底层是canvas 2,svg和canvas的区别 canvas 依赖分辨率 不支持事件处理器 弱的文本渲染能力 能够以 .png 或 .jpg 格 ...

  3. Django 载入静态文件地址

    1,Django框架中有专门存放静态文件的目录. 项目中的CSS.图片.js都是静态文件 配置静态文件 在settings 文件中定义静态内容 2,这些静态文件,他们统一存放在项目目录,templat ...

  4. form表单保存和取出

    function saveConfig() { var configName = document.title; if (!localStorage) return; var Config = {}; ...

  5. Hibernate 和 Mybatis 两者相比的优缺点

    1.开发上手难度 hibernate的真正掌握(封装的功能和特性非常多)要比Mybatis来得难. 在真正产品级应用上要用Hibernate,不仅对开发人员的要求高,hibernate往往还不适合(多 ...

  6. Anton 上课题

    Anton 上课题 Anton likes to play chess. Also he likes to do programming. No wonder that he decided to a ...

  7. WPF中TreeView的+-号和连线style的一种实现

    最近又开始跟WPF打交道,项目里面用到了TreeView这个控件.然后需要有一个连线的外观就像是这样 二话不说,百度了一下,找到一个实现, 通道. 把代码拷贝到项目里面,跑了一下,看上去还不错.但是这 ...

  8. 第二十四节:Java语言基础-讲解数组的综合应用

    数组的综合应用 // 打印数组 public static void printArray(int[] arr) { for(int x=0;x<arr.length;x++) { if(x!= ...

  9. JavaScript的BOM编程,事件-第4章

    目标 BOM编程 window和document对象 window对象的属性和方法 document对象的属性和方法 JavaScript中对象的分类 浏览器对象:window对象 window对象, ...

  10. spring框架学习笔记3:使用注解代替配置文件

    1.导入context约束:spring-context-4.2.xsd 2.design模式打开xml配置文件,右键edit namespaces,点击add添加 完成后应该是这样: 配置文件中这样 ...