1)观察者模式和发布/订阅模式真不一样
发布订阅模式
var pubsub = (()=>{ var topics = {}; //内部存储对象
function subscribe(topic,fn){ //订阅
if(!topics[topic]){
topics[topic] = [];
}
topics[topic].push(fn); //存储
}
function publish(topic,...args){ //共享存储对象,发布
if(!topics[topic])
return;
for(let fn of topics[topic]){ //遍历依次执行
fn(...args);
} }
return
{
subscribe, //暴露方法
publish
} })()
测试代码
pubsub.subscribe('test',function(a,b){ //订阅者A订阅了test事件 console.log(a,b); }); pubsub.publish('test','123','HH'); //123 HH(发布者B发布了test事件)
上面的例子是发布订阅模式。订阅者A和发布者B是通过pubsub这个对象关联起来的,他们没有直接的交流。A \B中去使用pubsub这个对象,和AB本身是无关联的。
实现下观察者模式。
首先是目标的构造函数,他有个数组,用于添加观察者。还有个广播方法,遍历观察者数组后调用他们的update方法:
class Subject{
constructor(){
this.subs = []; }
addSub(sub){ //‘订阅’
this.subs.push(sub);
}
notify(){ //‘发布’
this.subs.forEach(sub=> { sub.update(); });
} }
那么观察者就得有个update方法:
class Observer{
update(){ console.log('update'); }
}
测试代码
let subject = new Subject(); let ob = new Observer();
//目标添加观察者了 subject.addSub(ob);
//目标发布消息调用观察者的更新方法了 subject.notify(); //update
观察者模式:定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知。
模式特征以及角色:
- 一个目标者对象 Subject,拥有方法:添加 / 删除 / 通知 Observer;
- 多个观察者对象 Observer,拥有方法:接收 Subject 状态变更通知并处理;
- 目标对象 Subject 状态变更时,通知所有 Observer。
Subject 可以添加一系列 Observer, Subject 负责维护与这些 Observer 之间的联系,“你对我有兴趣,我更新就会通知你”。
Subject - 被观察者,发布者;
Observer - 观察者,订阅者;
// 目标者
class Subject {
constructor() {
this. observers = []; // 观察者列表
}
// 添加订阅者
add(observer) {
this.observers.push(observer);
}
// 删除...
remove(observer) {
let idx = this.observers.findIndex(item => item === observer);
idx > -1 && this.observers.splice(idx, 1);
}
// 通知
notify() {
for(let o of this.observers) {
o.update();
}
}
}
// 观察者
class Observer {
constructor(name) {
this.name = name;
}
// 目标对象更新时触发的回调,即收到更新通知后的回调
update() {
console.log(`目标者通知我更新了,我是:${this.name}`);
}
}
// 实例化目标者
let subject = new Subject();
// 实例化两个观察者
let obs1 = new Observer('前端');
let obs2 = new Observer('后端');
// 向目标者添加观察者
subject.add(obs1);
subject.add(obs2);
subject.notify();
subject 被观察 (订、取消。通知) Observer观察者(处理消息的回调)
发布订阅模式
/ 控制中心
let pubSub = {
list: {},
// 订阅
subscribe: function(key, fn) {
if (!this.list[key]) this.list[key] = [];
this.list[key].push(fn);
},
//取消订阅
unsubscribe: function(key, fn) {
let fnList = this.list[key];
if (!fnList) return false;
if (!fn) { // 不传入指定的方法,清空所用 key 下的订阅
fnList && (fnList.length = 0);
} else {
fnList.forEach((item, index) => {
item === fn && fnList.splice(index, 1);
});
}
},
// 发布
publish: function(key, ...args) {
for (let fn of this.list[key]) fn.call(this, ...args);
}
}
// 订阅
pubSub.subscribe('onwork', time => {
console.log(`上班了:${time}`);
})
pubSub.subscribe('offwork', time => {
console.log(`下班了:${time}`);
})
pubSub.subscribe('launch', time => {
console.log(`吃饭了:${time}`);
})
pubSub.subscribe('onwork', work => {
console.log(`上班了:${work}`);
})
// 发布
pubSub.publish('offwork', '18:00:00');
pubSub.publish('launch', '12:00:00');
// 取消订阅
pubSub.unsubscribe('onwork');
优缺点:
优点:解耦更好,细粒度更容易掌控;
缺点:不易阅读,额外对象创建,消耗时间和内存(很多设计模式的通病)
两种模式的关联和区别
发布订阅模式更灵活,是进阶版的观察者模式,指定对应分发。
- 观察者模式维护单一事件对应多个依赖该事件的对象关系;
- 发布订阅维护多个事件(主题)及依赖各事件(主题)的对象之间的关系;
- 观察者模式是目标对象直接触发通知(全部通知),观察对象被迫接收通知。发布订阅模式多了个中间层(事件中心),由其去管理通知广播(只通知订阅对应事件的对象);
- 观察者模式对象间依赖关系较强,发布订阅模式中对象之间实现真正的解耦。
2)认证-彻底搞懂 Cookie、Session、Token、JWT
路径不同不属于跨域
三、非同源限制
【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
【2】无法接触非同源网页的 DOM
【3】无法向非同源地址发送 AJAX 请求
cookie 一般都是由于用户访问页面而被创建的,可是并不是只有在创建 cookie 的页面才可以访问这个cookie。在默认情况下,出于安全方面的考虑,只有与创建 cookie 的页面处于同一个目录或在创建cookie页面的子目录下的网页才可以访问。那么此时如果希望其父级或者整个网页都能够使用cookie,就需要进行路径的设置。
path表示cookie所在的目录,asp.net默认为/,就是根目录。在同一个服务器上有目录如下:/test/,/test/cd/,/test/dd/,现设一个cookie1的path为/test/,cookie2的path为/test/cd/,那么test下的所有页面都可以访问到cookie1,而/test/和/test/dd/的子页面不能访问cookie2。这是因为cookie能让其path路径下的页面访问。
让这个设置的cookie 能被其他目录或者父级的目录访问的方法:
document.cookie = "name = value; path=/";
domain表示的是cookie所在的域,默认为请求的地址,如网址为www.jb51.net/test/test.aspx,那么domain默认为www.jb51.net。而跨域访问,如域A为t1.test.com,域B为t2.test.com,那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.test.com;如果要在域A生产一个令域A不能访问而域B能访问的cookie就要将该cookie的domain设置为t2.test.com。