数据劫持

function observe(data){if(typeof data !== 'object') returnnew Observer(data)
}//Observer 是用于给属性对象加上监听属性的
class Observer{constructor(value){this.value = valuethis.walk()}walk(){Object.keys(this.value).forEach((key) => defineReactive(this.value,key))}
}//我们需要一个全局变量来保存这个值
function defineReactive(data,key,value = data[key]){//如果value是对象,递归调用observe来监听对象//如果value不是对象,observe函数会直接返回observe(value)Object.defineProperty(data,key,{get(){console.log(`你视图访问一个getter属性 -> ${value} `);return value},set(newValue){if(newValue === value) return console.log(`你视图访问一个setter属性 -> ${newValue} `);value = newValueobserve(newValue) //设置的值也要被监听}})
}const obj = {a: 1,b: {c: 2}
}observe(obj);

代码执行顺序

执行observe(obj) 入口函数
├── new Observer(obj),并执行this.walk()遍历obj的属性,执行defineReactive()├── defineReactive(obj, a)├── 执行observe(obj.a) 发现obj.a不是对象,直接返回├── 执行defineReactive(obj, a) 的剩余代码├── defineReactive(obj, b) ├── 执行observe(obj.b) 发现obj.b是对象├── 执行 new Observer(obj.b),遍历obj.b的属性,执行defineReactive()├── 执行defineReactive(obj.b, c)├── 执行observe(obj.b.c) 发现obj.b.c不是对象,直接返回├── 执行defineReactive(obj.b, c)的剩余代码├── 执行defineReactive(obj, b)的剩余代码
代码执行结束

js发布订阅模式

发布-订阅模式 有3个模块,发布者,订阅者,调度中心

例子:这里处理中心相当于报刊办事大厅。发布者相当与某个杂志负责人,他来中心这注册一个的杂志,而订阅者相当于用户,我在中心订阅了这分杂志。每当发布者发布了一期杂志,办事大厅就会通知订阅者来拿新杂志

使用类封装,不用考虑this指向,便于理解

// 发布-订阅类 小案例
export default class Event{constructor(){}// 定义一个事件容器,用来装事件数组(因为订阅者可以是多个)handlers = {}// 参数是 事件名 和 事件方法addEventListener(type,handler){// 首先判断handlers内有没有type事件容器,没有则创建一个新数组容器if(!(type in this.handlers)){this.handlers[type] = []}// 将事件存入this.handlers[type].push(handler)}// 触发事件两个参数 (事件名, 参数)dispatchEvent(type,...params){console.log("params参数",params);// 若没有注册该事件则抛出错误if(!(type in this.handlers)){throw new Error('未注册该事件')}// 便利触发this.handlers[type].forEach(handler => {handler(...params)})}//事件移除参数(事件名,删除的事件,若无第二个参数则删除该事件的订阅和发布)removeEventlistener(type,handler){// 无效事件抛出if(!(type in this.handlers)){throw new Error("无效事件")}if(!handler){// 直接移除事件delete this.handlers[type]}else{const idx = this.handlers[type].findIndex(ele => ele  == handler)// 抛出异常事件if(idx === undefined){throw new Error('无该绑定事件');}// 移除事件this.handlers[type].splice(idx,1)this.handlers[type].length || delete this.handlers[type]}}
}
  • 知识弱点

    • //收
      let [a,...rest] = [1,2,3,4,5]
      console.log(a,rest); //1 [ 2, 3, 4, 5 ]
      
    • 所以在函数中也是一样的,形参使用(…rest) 语法,打印rest 是 收成一个数组,如果想要扩展还是要…rest

不使用类实现

  • 创建一个对象
  • 在该对象上创建一个缓存列表(调度中心)
  • on 方法用来把函数 fn 都加到缓存列表中(订阅者注册事件到调度中心),这里的缓存列表是一个数组,因为会有多个订阅者
  • emit 方法取到 arguments 里第一个当做 event,根据 event 值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
  • off 方法可以根据 event 值取消订阅(取消订阅)
  • once 方法只监听一次,调用完毕后删除缓存函数(订阅一次)
// 公众号对象
let eventEmitter = {};// 缓存列表,存放 event 及 fn
eventEmitter.list = {};//订阅
eventEmitter.on = function (event,fn){let _this = this;//如果没有对应的event值,既是说没有被订阅过, 就给event创建缓存列表// 如有对象中相应的event值,把fn添加到对应event 的缓存列表里(_this.list[event] || (_this.list[event] = [])).push(fn);return _this;
}//发布
eventEmitter.emit = function (){// _this 保存的是eventEmitter 里边的emit对象 所以下边调用 里边的方法需要 把_this 传入进去let _this = this;//第一个参数 是对应的 event 值,直接用数组的shift方法去除  (arguments里边保存了 调用函数的 参数)let event = [].shift.call(arguments),fns = [..._this.list[event]];//如果缓存列表里没有 fn 就返回 fasleif(!fns || fns.length === 0){return false;}// 遍历 event 值对应的缓存列表,依次执行fnfns.forEach(fn => {fn.apply(_this,arguments);});return _this;
}
  • 知识盲点

    • arguments里边保存了 调用函数的 参数,所以[].shift.call(arguments),取到的是arguments 里第一个call(arguments)意思是把this指向arguments
    • 为什么fn.apply(_this,arguments)要传入_this?
      • 因为外层是一个箭头函数,this的指向不明确,所以外层要保存 一层_this,
      • _this里边呢,保存的是eventEmitter这个对象,然后进行调用
    • 为什么需要把_this返回出去?
      • 不知道

添加off 和 once 方法

//取消订阅
eventEmitter.off = function(event,fn){let _this = this;let fns = _this.list[event]; //存放对应 标识 的 订阅列表// 如果缓存列表中没有相应的 fn ,返回falseif(!fns) return false;if(!fn){//如果没有传fn的话,就会将event值对应缓存列表中的fn都清空fns && (fns.length = 0)}else{// 若有fn,遍历对标,看看传入的fn与哪个函数相同,如果相同 直接在缓存列表中删除即可let cb = fns.findIndex(item => item === fn);fns.splice(cb,1);}return _this
}
  • 知识弱点

    • 两个相同的函数比较 布尔值 为 true ,因为引用地址是一样的
// 监听一次
eventEmitter.once = function(event,fn){// 先绑定,调用后删除let _this = this;function on(){_this.off(event,on);fn.apply(_this,arguments)}on.fn = fn;_this.on(event,on); //这里调用的 是外边的 on方法return _this;
}
  • 知识盲区

    • 这个once方法 不是说 订阅 就触发,而是 绑定到调度中心,使用一次后就执行off 方法进行一个取消订阅

    • 为什么 要 on.fn = fn 这样写? 不这样写 好像也能正常进行调用且 ,调度中心里边也会删除

      • 不知道

调用


function user1 (content) {console.log('用户1订阅了:', content);
};function user2 (content) {console.log('用户2订阅了:', content);
};function user3(content){console.log('用户3订阅了:', content);
}// 订阅
eventEmitter.on('article', user1);
eventEmitter.on('article', user2);//监听一次
eventEmitter.once("article2",user3)// 取消订阅
eventEmitter.off("article",user1)// 发布
eventEmitter.emit('article', 'Javascript 发布-订阅模式');
eventEmitter.emit('article2', 'Javascript 发布-观察模式');
eventEmitter.emit('article2', 'Javascript 发布-观察模式');

个人总结

  • 数据劫持的作用呢?就是给所有的对象属性添加上getter 和 setter属性,这样所有的对象就是响应式的了

    • 怎么添加呢?就是通过一个Observe类,里边自定义一个方法walk(),通过Object.keys()遍历 顶级对象key,调用defineReactive()方法添加,如果遍历key对应值 为对象,再递归调用
  • 了解发布订阅的作用?便于理解vue中的emit,emit,emit,on,once,once,once,off,总的来说发布订阅就是 把需要在全局使用的函数,订阅到 一个全局对象(调用中心)里边去,当我们需要在那个 阶段使用 这个函数的时候,只需要发布就能执行。该模式有利也有弊,好处就是对象之间的解耦,缺点就是创建订阅者 要消耗一定的时间和内存(暂时没有体验到)

数据劫持-发布订阅模式相关推荐

  1. 观察者模式与发布/订阅模式的区别

    [原]观察者模式与发布/订阅模式的区别 不管是维基百科还是百度百科,搜索观察者模式,都会发现观察者模式的定义是观察者模式(有时又被称为发布/订阅模式),即一个目标对象管理所有相依于它的观察者对象,并且 ...

  2. 发布订阅模式vs观察者模式

    背景 最近在研究react的状态管理器zustand时,研究源码时发现其组件注册绑定是通过观察者模式结合react hooks实现更新的.而联想之前写vue的时候,经常会用到vue内置的自定义事件进行 ...

  3. 从发布-订阅模式到Vue响应系统

    概念 发布-订阅模式又称为观察者模式,它定义的是一种一对多的依赖关系,当一个状态发生改变的时候,所有以来这个状态的对象都会得到通知. 生活中的发布-订阅模式 上面事发布-订阅模式的一个比较正式的解释, ...

  4. 设计模式之发布订阅模式

    发布--订阅模式简介 发布--订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,多个观察者对象都依赖于一个目标对象,当目标对象的状态发生变化时,所有依赖于这个对象的观察者对象都会收到通知. ...

  5. 观察者模式VS发布订阅模式

    一.前言 观察者模式vs发布/订阅模式很容易混淆,像是凤梨和菠萝,傻傻分不清.Observer模式通常用Publish/Subscribe模式实现,我刚开始学习js的时候,以为这是同一回事,但是仔细学 ...

  6. Vue响应式原理探究之“发布-订阅”模式

    前言 在面试题中经常会出现与"发布订阅"模式相关的题目,比如考察我们对Vue响应式的理解,也会有题目直接考验我们对"发布订阅"模式或者观察者模式的理解,甚至还会 ...

  7. 【EventBus】发布-订阅模式 ( 使用代码实现发布-订阅模式 )

    文章目录 一.发布-订阅模式 二.代码实现发布-订阅模式 1.订阅者接口 2.订阅者实现类 3.发布者 4.调度中心 5.客户端 一.发布-订阅模式 发布订阅模式 : 发布者 Publisher : ...

  8. JavaScript 设计模式之观察者模式与发布订阅模式

    前言 在软体工程中,设计模式(design pattern)是对软体设计中普遍存在(反复出现)的各种问题,所提出的解决方案. 设计模式并不直接用来完成程式码的编写,而是描述在各种不同情况下,要怎么解决 ...

  9. 点击事件调用匿名函数如何传参_事件发布/订阅模式的简单实现

    这是一种广泛应用于异步编程的模式,是回调函数的事件化,常常用来解耦业务逻辑.事件的发布者无需关注订阅的侦听器如何实现业务逻辑,甚至不用关注有多少个侦听器存在.数据通过消息的方式可以灵活的传递. --& ...

最新文章

  1. [TT]-Trustonic acronyms
  2. 样例解释:1013 数素数 (20分)
  3. java 继承多态的一些理解和不理解
  4. 计算机组成原理,P函数,深入浅出计算机组成原理学习笔记:第五讲
  5. hana-banach定理
  6. format 函数包含_Python成为专业人士笔记-高级对象Format格式化
  7. java dispatcher详解_dispatcherservlet初始化过程详解
  8. scrapy 爬虫-爬美食节
  9. asp.net调用前台js调用后台代码分享
  10. 使用jsoup爬虫超时分析与处理
  11. oracle windows系统下卸载oracle 11g和安装oracle 11g
  12. Javashop电商系统7.1.5源码,Java电商系统源码分享
  13. mysql数据库在哪里写语句_MySQL数据库基本操作以及SQL语句
  14. android自定义多选框 带图片,Android自定义单选多选下拉列表的实例代码
  15. C++库常用函数一览表
  16. 语音识别芯片LD3320介绍
  17. 2014年最新Itcast C++培训3期培训班视频教程
  18. Unity学习记录:制作双屏垃圾分类小游戏
  19. 解决CUDA out of memory. Tried to allocate 2.10 GiB (GPU 0; 14.76 GiB total capac
  20. 基于JSP的超市会员管理系统毕业设计

热门文章

  1. HTML网页设计基础笔记 • 【目录】
  2. 北京中信银行总行地址_中信银行地址
  3. dpdk内存池 mpool 实现机制
  4. html+css+javascript 开发英语生词本
  5. 信息系统项目管理师证书(高项)有什么用?
  6. 前端 H5 横屏 独特处理方案详解
  7. 双馈风力发电机的matlab仿真模型,基于MATLAB_SIMULINK的并网型双馈风力发电机仿真模型的研究[1]...
  8. 计算机网络技术第四版知识点,计算机网络技术知识点总结
  9. 高可靠性领域如何选取处理器系统和ADC/DAC?
  10. 记 H5调用打开android文件管理上传文件