js
题目1
观察者和发布订阅有什么关系,什么区别?
my answer:
观察者:
observer监听数据,收集依赖
发布订阅:
订阅时将方法存入观察者容器,发布时通知,并逐一执行容器内的方法
陈小哥同学:
下单订阅外卖 订单进入饭店 排队等候 店家发布信息 做餐完毕 一个个订单取出 让外卖小哥送餐
解答:
观察者observer
被观察者:目标 target
将什么东西送到对面去
特点:
观察者针对目标,观察目标的机能是否有变化,从而去执行一系列的任务
一种设计模式,一个函数内部的某个值/某些值变化去完成的一些额外任务,
observer是一个庞大的结构
发布订阅并不是一种设计模式,一种思路,由观察者思想演变过来的思路
总结:
观察目标中数据进行增删改查的时候增加额外的任务,如组件渲染等
应用:
- 解决事件绑定的问题
- vue做数据变化的时候要做组件渲染
如何写:
- 先写目标
class Target - 通过对数据源的代理去做更多的事情
- 对当前数据校验
validateData函数(username,password…) - 实例化的时候校验
- 代理
proxyData函数,代理对象的属性 - get时返回
data[key] - set时
data[key] = newValue - 完成获取值的时候更新日志
class Observer - get时
this.observer.updateLog('get',key,data[key]) - set时
this.observer.updateLog('set',key,data[key]) - 实例化Observer时获取节点el
- 定义实例里的updateLog方法, 判断get/set,打印日志池/执行log方法
- 实例里定义日志池logPool数组属性
- getProp方法:将日志对象push到日志池
- setProp方法:将日志对象push到日志池
- log方法:创建li元素/组装get或set的html字符串模板/插入el
/*** 观察者 Observer oppsite object* 目标 Target*/class Target {constructor (data) {// username password age genderthis.data = data;this.observer = new Observer('#list');this.init();}init () {this.validateData(this.data);this.proxyData(this.data);}validateData (data) {const { username, password, age, gender } = data;username.length < 6 && (data.username = '');password.length < 6 && (data.password = '');typeof age !== 'number' && (data.age = 0);// male female!['male', 'female'].includes(gender) && (data.gender = 'female');}proxyData (data) {for (let key in data) {Object.defineProperty(this, key, {get () {this.observer.updateLog('get', key, data[key]);return data[key];},set (newValue) {this.observer.updateLog('set', key, data[key], newValue);data[key] = newValue;}})}}}class Observer {constructor (el) {this.el = document.querySelector(el);this.logPool = [];}updateLog (type, key, oldValue, newValue) {switch (type) {case 'get':this.getProp(key, oldValue);break;case 'set':this.setProp(key, oldValue, newValue);break;default:break;}console.log(this.logPool);}getProp (key, value) {const o = {type: 'get',dateTime: new Date(),key,value}this.logPool.push(o);this.log(o);}setProp (key, oldValue, newValue) {const o = {type: 'set',dateTime: new Date(),key,oldValue,newValue}this.logPool.push(o);this.log(o);}log (o) {const { type, dateTime, key } = o;const oLi = document.createElement('li');let htmlStr = '';switch (type) {case 'get':htmlStr = `${dateTime}:I got the key '${key}'.The vaue of the key is ${o.value}`;break;case 'set':htmlStr = `${dateTime}:I set the value of the key '${key}' '${o.newValue}'from the old value '${o.oldValue}';`break;default:break;}oLi.innerHTML = htmlStr;this.el.appendChild(oLi);}}
发布订阅
以事件为核心,一个事件到底有没有触发,只要触发,和事件相关的处理函数依次执行
如何写:
- 定义类
EventEmitter const ev = new EventEmitter()- 定义handle1/handle2/handle3/handle4/handle5/handle6函数
- 给6个函数绑定事件
ev.on('test',handle1) ev.trigget('test')- 定义实例里的on方法
- on方法:如果
this.handlers[type]不存在,把每个handle函数放进容器 - 定义容器
this.handles={} - 希望的写法
{test:[handle1,...],test2:[handle4,...]} - 定义实例里的trigger方法
- trigger方法:如果
this.handlers[type]存在,拿到每个handle并依次执行
class EventEmitter {constructor () {this.handlers = {};}on (type, handler, once) {if (!this.handlers[type]) {this.handlers[type] = [];}if (!this.handlers[type].includes(handler)) {this.handlers[type].push(handler);handler.once = once;}console.log(this.handlers);}once (type, handler) {this.on(type, handler, true);}off (type, handler, callback) {if (this.handlers[type]) {this.handlers[type] = this.handlers[type].filter(h => {return h !== handler;});callback && callback(type);}}trigger (type, data, context, callback) {if (this.handlers[type]) {this.handlers[type].forEach(handler => {handler.call(context ? context : this);callback && callback(data);if (handler.once) {this.off(type, handler);}});}}}const ev = new EventEmitter();function handler1 () {console.log('handler1');}function handler2 () {console.log('handler2');}function handler3 () {console.log('handler3');}function handler4 () {console.log('handler4');}function handler5 () {console.log('handler5');}function handler6 () {console.log('handler6');}ev.on('test', handler1);ev.on('test', handler2);ev.on('test', handler3);// ev.once('test1', handler4);// ev.once('test1', handler5);// ev.once('test1', handler6);ev.trigger('test', {a: 1, b: 2}, {c: 3, d: 4}, (data) => {console.log(data);});// ev.trigger('test1');// ev.trigger('test1');
课外:
vuex/redux
中央状态管理器
- state仓库 放数据
- action 行为 事件
- mutation 处理方法
流程闭环:backend API -> actions(commit) -> mutation(mutate) -> state -> render component(dispatch) -> actions…
注意:必须遵守上面的流程规矩
题目2
什么是闭包?
- 是极为简单的概念和机能
- 闭包从来跟return没有半毛钱关系
- 只跟环境作用域有关系
函数嵌套的目的是:形成内部访问外部的访问体系
纯函数:内外不相关,标准输入输出
闭包可以实现属性和方法私有化
