Javascript 进阶

封装(订阅者 + 发布者)

提出问题

问题描述:假如我是一个推销产品的应用开发经理,有很多的用户在我的平台上订阅些内容,我如何通过用户订阅的信息,准确的发送产品给需要它的客户们呢?

分析问题

  • 首先我是卖家,我需要提供一个添加订阅信息和对应的用户功能函数
  • 当我发布一个产品的时候,我需要精准的发送给需要它的用户

步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 我们先创建一个卖家
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);

这样一个简单的发布过程就做好了,然而这并不是定制,因为每次发布一个产品,都要通知所有的订阅者,我们的目的是通过订阅者订阅的关键字,来精准的发布产品给有需要的客户,这次我们用对象来写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 这是一套流程,相当于数学公式
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);

总结问题

  • 通过上面的案例,稍微体验了一下封装是什么样的感觉,类似大的插件也是这样的思想,封装对于大厂用途比较多。
  • 尽量的体会过程!!!

深复制 & 浅复制

  • 深复制:深拷贝会开辟新的栈内存,原对象和新对象不共享同一块内存,修改新对象不会影响到原对象。
  • 浅复制:复制指向,引用。
    1. object.assign()
    2. slice
    3. concat

内存

js 中,内存是自动分配的,当变量不再需要的时候自动释放,过程如下:

  1. 分配需要的内存空间
  2. 使用分配的内存(读写)
  3. 不需要时释放或者归还 gc(垃圾回收机制)

内存泄露

不再需要此内存,然而由于某种原因,无法释放此内存。

  1. 全局变量带来的
  2. 回调函数
    function a(){
    return 2;
    }
    var b = a()
  3. 额外的定时器
  4. dom 引用(removeChild 后,还能获得节点的信息)
  5. 闭包也会出现内存泄漏(优点是防止全局变量污染)

垃圾回收机制 GC

垃圾回收的方法分两种,

  1. 引用计数:统计被引用的次数,次数为 0,被回收。
  2. 标记清除

分类

  • 栈:用来存放基本数据类型,还有指向复杂数据类型的引用指针
  • 堆:用来存放复杂数据类型,这种数据结构是一种无序的树状,通过key来保存指针,类似字典。
  • 队列:先进先出,典型的例子是 JS 中的事件循环(eventsloop

this**

我的 this 详解。