Javascript 进阶
封装(订阅者 + 发布者)
提出问题
问题描述:假如我是一个推销产品的应用开发经理,有很多的用户在我的平台上订阅些内容,我如何通过用户订阅的信息,准确的发送产品给需要它的客户们呢?
分析问题
- 首先我是卖家,我需要提供一个添加订阅信息和对应的用户功能函数
- 当我发布一个产品的时候,我需要精准的发送给需要它的用户
步骤
// 我们先创建一个卖家
var seller = {};
// 添加一个列表list,用于存储订阅者
seller.list = [];
// 添加一个方法listen,用于添加订阅者
seller.listen = (fn) => seller.list.push(fn);
// 添加一个方法trigger:用于发布消息
seller.trigger = function(){
// 循环停止条件:如果循环复制的值为none/undefined的时候
for (var i = 0, fn; fn = this.list[i]; i++) {
// this 指向seller,让每一个订阅者都继承发布产品的参数
fn.call(this,...arguments)
}
}
// 添加订阅者A
seller.listen(function(color, size){
console.log('我是A订阅者');
console.log("您订阅的卖家发布新的鞋:");
console.log("颜色是:", color);
console.log("尺寸是:", size);
})
// 添加订阅者B
seller.listen(function(color, size){
console.log('我是B订阅者');
console.log("您订阅的卖家发布新的鞋:");
console.log("颜色是:", color);
console.log("尺寸是:", size);
})
// 卖家发布一双新鞋,通知给订阅者
seller.trigger('红色', 42)
这样一个简单的发布过程就做好了,然而这并不是定制,因为每次发布一个产品,都要通知所有的订阅者,我们的目的是通过订阅者订阅的关键字,来精准的发布产品给有需要的客户,这次我们用对象来写
// 这是一套流程,相当于数学公式
var method1 = {
// 列表属性,保存用户及订阅信息
// list: ['手机': ['订阅者A','订阅者B'], '电脑': ['订阅者','订阅者']]
list: [],
// 添加用户及其订阅信息
// key代表关键字,user是关注关键字的用户
listen(key, user){
// 如果当前list中没有此订阅信息的人,就初始化一个空的
if(!this.list[key]){
this.list[key] = []
}
// 然后把这个用户给添加进来
this.list[key].push(user)
},
// 发布商品:发布商品的时候需要通过关键字来精准的推广给相应用户
trigger(){
// 获取到用户订阅的关键字
let key = Array.prototype.shift.call(arguments);
// 通过订阅关键字来获得相应订阅用户列表
let user = this.list[key];
// 如果没有此关键字的订阅,或者订阅此关键字的人数为空,则不需要通知
// console.log(user);
if(!user || user.length === 0){
return ;
}
// 发布给订阅用户列表中的用户
for(let i = 0, each;each = user[i++];){
// 给 each 订阅者们,传递发布的消息argument
// 不需要修改指针
// each.call(this,...arguments) 也可以这么写
each(...arguments);
// console.log(each);
}
}
};
// 入口:给转进来的对象加入method1方法
var init = function(obj){
for(var key in method1){
obj[key] = method1[key];
}
};
// 通过数学公式,创建一个卖家,并初始化方法
var seller = {};
init(seller);
// 添加订阅人A
seller.listen('显卡',function(msg){
console.log('我是订阅者A,订阅:显卡,价格为:',msg);
console.log('\n');
});
// 添加订阅人B
seller.listen('电脑',function(msg){
console.log('我是订阅者B,订阅:电脑,价格为:',msg);
console.log('\n');
});
// 添加订阅人C
seller.listen('手机',function(msg){
console.log('我是订阅者C,订阅:手机,价格为:',msg);
console.log('\n');
});
// 卖家发布信息并通知相关订阅人
seller.trigger('显卡', 2499);
seller.trigger('棒槌', 2499);
seller.trigger('手机', 999);
总结问题
- 通过上面的案例,稍微体验了一下封装是什么样的感觉,类似大的插件也是这样的思想,封装对于大厂用途比较多。
- 尽量的体会过程!!!
深复制 & 浅复制
- 深复制:深拷贝会开辟新的栈内存,原对象和新对象不共享同一块内存,修改新对象不会影响到原对象。
- 浅复制:复制指向,引用。
- object.assign()
- slice
- concat
内存
js中,内存是自动分配的,当变量不再需要的时候自动释放,过程如下:
- 分配需要的内存空间
- 使用分配的内存(读写)
- 不需要时释放或者归还 gc(垃圾回收机制)
内存泄露
不再需要此内存,然而由于某种原因,无法释放此内存。
- 全局变量带来的
- 回调函数
function a(){
return 2;
}
var b = a() - 额外的定时器
- dom引用(removeChild后,还能获得节点的信息)
- 闭包也会出现内存泄漏(优点是防止全局变量污染)
垃圾回收机制 GC
垃圾回收的方法分两种,
- 引用计数:统计被引用的次数,次数为0,被回收。
- 标记清除
分类
- 栈:用来存放基本数据类型,还有指向复杂数据类型的引用指针
- 堆:用来存放复杂数据类型,这种数据结构是一种无序的树状,通过
key
来保存指针,类似字典。 - 队列:先进先出,典型的例子是 JS中的事件循环(
eventsloop
) - 池