Skip to content

Commit 4fff498

Browse files
committed
🎨 update: JavaScript
1 parent 83ca848 commit 4fff498

18 files changed

+1509
-0
lines changed

docs/.vuepress/config.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,30 @@ module.exports = {
88
nav: [
99
{ text: '数据结构分类', link: '/dataStructure/' },
1010
{ text: '算法分类', link: '/algorithm/' },
11+
{ text: 'JavaScript', link: '/JavaScript/' },
1112
{ text: '博客', link: 'http://www.conardli.top/blog/article/' },
1213
{ text: 'github', link: 'https://github.com/ConardLi' },
1314
],
1415
sidebar: {
16+
'/JavaScript/': [
17+
'/JavaScript/',
18+
'/JavaScript/手动实现call、apply、bind',
19+
'/JavaScript/EventEmitter',
20+
'/JavaScript/防抖',
21+
'/JavaScript/节流',
22+
'/JavaScript/浅拷贝和深拷贝',
23+
'/JavaScript/数组去重、扁平、最值',
24+
'/JavaScript/数组乱序-洗牌算法',
25+
'/JavaScript/函数柯里化',
26+
'/JavaScript/手动实现JSONP',
27+
'/JavaScript/模拟实现promise',
28+
'/JavaScript/手动实现ES5继承',
29+
'/JavaScript/手动实现instanceof',
30+
'/JavaScript/基于Promise的ajax封装',
31+
'/JavaScript/单例模式',
32+
'/JavaScript/异步循环打印',
33+
'/JavaScript/图片懒加载',
34+
],
1535
'/algorithm/': [
1636
'/algorithm/',
1737
{

docs/JavaScript/EventEmitter.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
---
2+
{
3+
"title": "EventEmitter",
4+
}
5+
---
6+
## 观察者模式
7+
8+
![image](http://img.blog.csdn.net/20161126191512446)
9+
10+
这就类似我们在微信平台订阅了公众号 , 当它有新的文章发表后,就会推送给我们所有订阅的人。
11+
12+
我们作为订阅者不必每次都去查看这个公众号有没有新文章发布,公众号作为发布者会在合适时间通知我们。
13+
14+
我们与公众号之间不再强耦合在一起。公众号不关心谁订阅了它, 不管你是男是女还是宠物狗,它只需要定时向所有订阅者发布消息即可。
15+
16+
### 观察者模式的优点
17+
18+
- 可以广泛应用于异步编程,它可以代替我们传统的回调函数
19+
- 我们不需要关注对象在异步执行阶段的内部状态,我们只关心事件完成的时间点
20+
- 取代对象之间硬编码通知机制,一个对象不必显式调用另一个对象的接口,而是松耦合的联系在一起 。
21+
22+
虽然不知道彼此的细节,但不影响相互通信。更重要的是,其中一个对象改变不会影响另一个对象。
23+
24+
## Nodejs的EventEmitter
25+
26+
`Nodejs``EventEmitter`就是观察者模式的典型实现,`Nodejs``events`模块只提供了一个对象: `events.EventEmitter``。EventEmitter` 的核心就是事件触发与事件监听器功能的封装。
27+
28+
> Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
29+
30+
### Api
31+
32+
**addListener(event, listener)**
33+
34+
为指定事件添加一个监听器,默认添加到监听器数组的尾部。
35+
36+
**removeListener(event, listener)**
37+
38+
移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。
39+
40+
**setMaxListeners(n)**
41+
42+
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
43+
44+
**once(event, listener)**
45+
46+
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
47+
48+
**emit(event, [arg1], [arg2], [...])**
49+
50+
按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 `true`,否则返回 `false`
51+
52+
### 基本使用
53+
54+
```js
55+
var events = require('events');
56+
var eventEmitter = new events.EventEmitter();
57+
58+
// 监听器 #1
59+
var listener1 = function listener1() {
60+
console.log('监听器 listener1 执行。');
61+
}
62+
63+
// 监听器 #2
64+
var listener2 = function listener2() {
65+
console.log('监听器 listener2 执行。');
66+
}
67+
68+
// 绑定 connection 事件,处理函数为 listener1
69+
eventEmitter.addListener('connection', listener1);
70+
71+
// 绑定 connection 事件,调用一次,处理函数为 listener2
72+
eventEmitter.once('connection', listener2);
73+
74+
// 处理 connection 事件
75+
eventEmitter.emit('connection');
76+
77+
// 处理 connection 事件
78+
eventEmitter.emit('connection');
79+
```
80+
81+
## 手动实现EventEmitter
82+
83+
```js
84+
function EventEmitter() {
85+
this._maxListeners = 10;
86+
this._events = Object.create(null);
87+
}
88+
89+
// 向事件队列添加事件
90+
// prepend为true表示向事件队列头部添加事件
91+
EventEmitter.prototype.addListener = function (type, listener, prepend) {
92+
if (!this._events) {
93+
this._events = Object.create(null);
94+
}
95+
if (this._events[type]) {
96+
if (prepend) {
97+
this._events[type].unshift(listener);
98+
} else {
99+
this._events[type].push(listener);
100+
}
101+
} else {
102+
this._events[type] = [listener];
103+
}
104+
};
105+
106+
// 移除某个事件
107+
EventEmitter.prototype.removeListener = function (type, listener) {
108+
if (Array.isArray(this._events[type])) {
109+
if (!listener) {
110+
delete this._events[type]
111+
} else {
112+
this._events[type] = this._events[type].filter(e => e !== listener && e.origin !== listener)
113+
}
114+
}
115+
};
116+
117+
// 向事件队列添加事件,只执行一次
118+
EventEmitter.prototype.once = function (type, listener) {
119+
const only = (...args) => {
120+
listener.apply(this, args);
121+
this.removeListener(type, listener);
122+
}
123+
only.origin = listener;
124+
this.addListener(type, only);
125+
};
126+
127+
// 执行某类事件
128+
EventEmitter.prototype.emit = function (type, ...args) {
129+
if (Array.isArray(this._events[type])) {
130+
this._events[type].forEach(fn => {
131+
fn.apply(this, args);
132+
});
133+
}
134+
};
135+
136+
// 设置最大事件监听个数
137+
EventEmitter.prototype.setMaxListeners = function (count) {
138+
this.maxListeners = count;
139+
};
140+
```
141+
142+
测试代码:
143+
144+
```js
145+
var emitter = new EventEmitter();
146+
147+
var onceListener = function (args) {
148+
console.log('我只能被执行一次', args, this);
149+
}
150+
151+
var listener = function (args) {
152+
console.log('我是一个listener', args, this);
153+
}
154+
155+
emitter.once('click', onceListener);
156+
emitter.addListener('click', listener);
157+
158+
emitter.emit('click', '参数');
159+
emitter.emit('click');
160+
161+
emitter.removeListener('click', listener);
162+
emitter.emit('click');
163+
```
164+
165+
## JavaScript自定义事件
166+
167+
`DOM`也提供了类似上面`EventEmitter``API`,基本使用:
168+
169+
```js
170+
//1、创建事件
171+
var myEvent = new Event("myEvent");
172+
173+
//2、注册事件监听器
174+
elem.addEventListener("myEvent",function(e){
175+
176+
})
177+
178+
//3、触发事件
179+
elem.dispatchEvent(myEvent);
180+
```

docs/JavaScript/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
{
3+
"title": "JavaScript专题",
4+
}
5+
---
6+
7+
### 二叉树
8+
9+
- [二叉树的基本操作](./二叉树/二叉树的基本操作.md)⭐⭐
10+
- [二叉树的中序遍历](./二叉树/二叉树的中序遍历.md)⭐⭐
11+
- [二叉树的前序遍历](./二叉树/二叉树的前序遍历.md)⭐⭐
12+
- [二叉树的后序遍历](./二叉树/二叉树的后序遍历.md)⭐⭐
13+
- [重建二叉树](./二叉树/重建二叉树.md)⭐⭐
14+
- [求二叉树的遍历](./二叉树/重建二叉树.html/#求二叉树的遍历)⭐⭐
15+
- [对称的二叉树](./二叉树/对称的二叉树.md)⭐⭐
16+
- [二叉树的镜像](./二叉树/二叉树的镜像.md)⭐⭐
17+
- [二叉搜索树的第k个节点](./二叉树/二叉搜索树的第k个节点.md)⭐⭐
18+
- [二叉搜索树的后序遍历](./二叉树/二叉搜索树的后序遍历.md)⭐⭐
19+
- [二叉树的最大深度](./二叉树/二叉树的最大深度.md)⭐⭐
20+
- [二叉树的最小深度](./二叉树/二叉树的最小深度.md)⭐⭐
21+
- [平衡二叉树](./二叉树/平衡二叉树.md)⭐⭐
22+
- [不分行从上到下打印二叉树](./二叉树/从上到下打印二叉树.html/#题目1-不分行从上到下打印)⭐⭐
23+
- [把二叉树打印成多行](./二叉树/从上到下打印二叉树.html/#题目2-把二叉树打印成多行)⭐⭐
24+
- [二叉树中和为某一值的路径](./二叉树/二叉树中和为某一值的路径.md)⭐⭐⭐
25+
- [二叉搜索树与双向链表](./二叉树/二叉搜索树与双向链表.md)⭐⭐⭐
26+
- [按之字形顺序打印二叉树](./二叉树/从上到下打印二叉树.html/#题目3-按之字形顺序打印二叉树)⭐⭐⭐
27+
- [序列化二叉树](./二叉树/序列化二叉树.md)⭐⭐⭐
28+
- [二叉树的下一个节点](./二叉树/二叉树的下一个节点.md)⭐⭐⭐
29+
- [树的子结构](./二叉树/树的子结构.md)⭐⭐⭐
30+
31+
32+
###
33+
34+
- [堆的基本操作](./堆/堆的基本操作.md)⭐⭐⭐
35+
- [数据流中的中位数](./堆/数据流中的中位数.md)⭐⭐⭐
36+
- [最小的k个数](./堆/最小的k个数.md)⭐⭐⭐

docs/JavaScript/函数柯里化.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
{
3+
"title": "函数柯里化",
4+
}
5+
---
6+
7+
## 定义
8+
9+
在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
10+
11+
> 通俗易懂的解释:用闭包把参数保存起来,当参数的数量足够执行函数了,就开始执行函数。
12+
13+
14+
## 实现
15+
16+
- 判断当前函数传入的参数是否大于或等于`fn`需要参数的数量,如果是,直接执行`fn`
17+
- 如果传入参数数量不够,返回一个闭包,暂存传入的参数,并重新返回`currying`函数
18+
19+
```js
20+
function currying(fn, ...args) {
21+
if (args.length >= fn.length) {
22+
return fn(...args);
23+
} else {
24+
return (...args2) => currying(fn, ...args, ...args2);
25+
}
26+
}
27+
```
28+
29+
我们来一个简单的实例验证一下:
30+
31+
```js
32+
const curryingFun = currying(fun)
33+
curryingFun(1)(2)(3); // 1 2 3
34+
curryingFun(1, 2)(3); // 1 2 3
35+
curryingFun(1, 2, 3); // 1 2 3
36+
```
37+
38+
## 应用场景
39+
40+
### 参数复用
41+
42+
```js
43+
function getUrl(protocol, domain, path) {
44+
return protocol + "://" + domain + "/" + path;
45+
}
46+
47+
var page1 = getUrl('http', 'www.conardli.top', 'page1.html');
48+
var page2 = getUrl('http', 'www.conardli.top', 'page2.html');
49+
```
50+
51+
我们使用`currying`来简化它:
52+
53+
```js
54+
let conardliSite = currying(simpleURL)('http', 'www.conardli.top');
55+
let page1 = conardliSite('page1.html');
56+
```

docs/JavaScript/单例模式.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
{
3+
"title": "单例模式",
4+
}
5+
---
6+
7+
在合适的时候才创建对像,并且只创建唯一的一个。
8+
9+
创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。
10+
11+
使用闭包实现:
12+
13+
```js
14+
var Singleton = function(name) {
15+
this.name = name;
16+
};
17+
18+
Singleton.prototype.getName = function() {
19+
alert(this.name);
20+
};
21+
22+
Singleton.getInstance = (function(name) {
23+
var instance;
24+
return function(name){
25+
if (!instance) {
26+
instance = new Singleton(name);
27+
}
28+
return instance;
29+
}
30+
})();
31+
32+
var a = Singleton.getInstance('ConardLi');
33+
var b = Singleton.getInstance('ConardLi2');
34+
35+
console.log(a===b); //true
36+
```

0 commit comments

Comments
 (0)