JavaScript单例模式

工厂函数:创建单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 单例模式
* 定义:一个页面里边一个js类只初始化一次
*/

class Parent {
constructor() {
this.name = '小明';
this.instance = null;
}

getName() {
console.log(this.name)
}
}

Parent.singel = function () {
debugger;
if (!this.instance) {
this.instance = new Parent()
}
return this.instance
}
使用构造函数的默认属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function A(name){
// 如果已存在对应的实例
if(typeof A.instance === 'object'){
return A.instance
}
//否则正常创建实例
this.name = name

// 缓存
A.instance =this
return this
}
var a1 = new A()
var a2= new A()
console.log(a1 === a2)//true
借助闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Head = (function () {
var HeadClass = function () { }; // 声明HeadClass对象,无法在外部直接调用
var instance; // 声明一个instance对象
return function () {
if (instance) { // 如果已存在 则返回instance
return instance;
}
instance = new HeadClass() // 如果不存在 则new一个
return instance;
}
})();
var a = Head();
var b = new Head();
console.log(a===b) // true
var a = HeadClass(); // 报错,HeadClass is not defined
立即执行函数
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
var A;
(function(name){
var instance;
A = function(name){
if(instance){
return instance
}

//赋值给私有变量
instance = this

//自身属性
this.name = name
}
}());
A.prototype.pro1 = "from protptype1"

var a1 = new A('a1')
A.prototype.pro2 = "from protptype2"
var a2 = new A('a2')

console.log(a1.name)
console.log(a1.pro1)//from protptype1
console.log(a1.pro2)//from protptype2
console.log(a2.pro1)//from protptype1
console.log(a2.pro2)//from protptype2

Vue响应式原理

Object.defineproperty实现

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
const data = {
// message: 'hello world'
}

// data.a = 1

// data.message = 'hello'

// 如何给一个对象添加数据成员
// 方式一:初始化的时候添加
// 方式二:对象.xxx = xxx
// 方式三:Object.definePrope0k jrty,更高级

// 这是一个普通变量
let abc = 'hello world'

// 数据劫持、数据代理、数据监视、数据拦截器
// 参数1:数据对象
// 参数2:属性名
// 参数3:一个配置对象
Object.defineProperty(data, 'message', {
// 当属性访问的时候会调用 get 方法访问器
// get 方法的返回值就是该属性的值
// 注意:通过这种方式定义的数据成员无法存储数据
get () {
console.log('message 被访问了')
return abc
// return 'hello world'
},

// 当属性被赋值的时候会调用 set 方法
// 赋的那个值会作为参数传递给 set 方法
set (value) {
console.log('message 被赋值了', value)
document.querySelector('h1').innerHTML = value
abc = value
}
})

defineReactive(data, 'foo', 'bar')

defineReactive(data, 'user', {
name: '张三',
age: 18
})

function defineReactive (data, key, value) {
// value 是一个参数,在函数内部能被访问
Object.defineProperty(data, key, {
// 当属性访问的时候会调用 get 方法访问器
// get 方法的返回值就是该属性的值
// 注意:通过这种方式定义的数据成员无法存储数据
get () {
console.log(key, '被访问了')
return value
// return 'hello world'
},

// 当属性被赋值的时候会调用 set 方法
// 赋的那个值会作为参数传递给 set 方法
set (val) {
console.log(key, '被修改了', value)
document.querySelector('h1').innerHTML = value
// abc = value
value = val
}
})
}

Object.defineproperty实现深度监视

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
const data = {
a: 1,
b: 2,
user: {
name: 'Jack',
age: 18
}
}

observe(data)

function observe(data) {
// 如果 data 数据无效或者 data 不是一个对象,就停止处理
if (!data || typeof data !== 'object') {
return;
}

// 取出所有属性遍历,对属性成员进行代理(拦截、观察)操作
Object.keys(data).forEach(function (key) {
defineReactive(data, key, data[key]);
});
}

/**
* data 是数据对象
* key 是属性名
* val 当前属性名对应的值
*/
function defineReactive(data, key, val) {
// observe(1)
// observe(2)
// observe({ name: 'Jack', age: 18 })
observe(val); // 监听子属性

Object.defineProperty(data, key, {
// enumerable: true, // 可枚举
// configurable: false, // 不能再define
get: function () {
console.log(key, '被访问了')
return val;
},
set: function (newVal) {
console.log(key, '被修改了');
val = newVal;
}
});
}

使用Proxy实现(Vue中使用,性能更好)

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
const data = {
a: 1,
b: 'hello',
user: {
name: 'Jack',
age: 18
}
}

// Proxy 是一个构造函数,使用的时候需要 new
// 参数1:要代理的目标对象
// 参数2:一个配置对象
// get 访问器
// set 赋值器
// ....
// 返回值:代理对象
// 注意:被代理的目标数据对象该是什么样还是什么样
// 必须通过代理对象来访问目标对象中的数据成员才会走代理(get、set)
const p = new Proxy(data, {
// 当数据成员被访问的时候会调用 get 方法
// 参数1:被代理的目标对象
// 参数2:访问的属性名
get (target, property) {
console.log('get 方法被调用了', target, property)
return target[property]
},

// 当数据成员被修改赋值的时候会调用 set 方法
// 参数1:被代理的目标对象
// 参数2:要修改的属性名
// 参数3:要修改的值
set (target, property, value) {
console.log('set 方法被调用了')
target[property] = value
}
})

Proxy深度代理

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
const obj = {
name: 'jin',
age: 18,
skill: {
swim: true,
run: true
}
}

// 注意代理对象数据修改和删除不会影响被代理对象
const proxy = observer(obj)

// 给proxy设置代理
function observer(obj) {
// 非对象不需要代理
if (!obj || typeof obj !== 'object') {
return
}

// 获取代理对象
const proxy = new Proxy(obj, {
get(target, property) {
console.log('访问了')
console.log(target, property)
return target[property]
},
set(target, property, value) {
console.log('设置了')
console.log(target, property, value)

// 设置需要判断是因为有可能赋值的是对象,所以需要将赋值的对象转化为代理对象
target[property] = observer(value) || value
}
})

// 遍历对象key
Object.keys(obj).forEach(key => {
// 如果属性是对象的话,需要进一步代理
if (typeof obj[key] === 'object') {
obj[key] = observer(obj[key])
}
})

// 返回代理对象
return proxy
}

React组件封装方法

普通类组件实现
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
import React, { Component } from "react";

class Renderprops extends Component {
state = {
x: 0,
y: 0,
};

handlerFn = (e) => {
this.setState({ x: e.x, y: e.y });
};

componentDidMount() {
window.addEventListener("mousemove", this.handlerFn, false);
}

componentWillUnmount() {
window.removeEventListener("mousemove", this.handlerFn);
}

render() {
return (
<div>
<h1>
X:{this.state.x} Y:{this.state.y}
</h1>
</div>
);
}
}

export default Renderprops;
render props 模式
复用的是组件的状态和功能,传入的是 UI 要呈现的效果。
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
import React, { Component } from "react";

class MouseComponent extends Component {
state = {
x: 0,
y: 0,
};

handlerFn = (e) => {
this.setState({ x: e.x, y: e.y });
};

componentDidMount() {
window.addEventListener("mousemove", this.handlerFn, false);
}

componentWillUnmount() {
window.removeEventListener("mousemove", this.handlerFn);
}

render() {
return this.props.render(this.state);
}
}

class Renderprops extends Component {
shoewUI = (state) => {
const { x, y } = state;
return (
<h1>
X:{x} Y:{y}
</h1>
);
};

render() {
return <MouseComponent render={this.shoewUI} />;
}
}

export default Renderprops;
高阶组件(HOC)

######高阶函数(形式上):如果一个函数的形参或者返回值也是函数

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
import React, { Component } from "react";

// 创建高阶组件
// 1. 创建一个函数,名称约定以 with 开头
// 2. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
// 3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
// 4. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
// 5. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中

const withCompile = (Fnc) => {
return class MouseComponent extends Component {
state = {
x: 0,
y: 0,
};

handlerFn = (e) => {
this.setState({ x: e.x, y: e.y });
};

componentDidMount() {
window.addEventListener("mousemove", this.handlerFn, false);
}

componentWillUnmount() {
window.removeEventListener("mousemove", this.handlerFn);
}

render() {
return <Fnc {...this.state} {...this.props} />;
}
};
};

function Fnc(props) {
return (
<div>
title:{props.title}
<h1>
X:{props.x}Y:{props.y}
</h1>
</div>
);
}

const Result = withCompile(Fnc);

class Renderprops extends Component {
render() {
return <Result title="数据" />;
}
}

export default Renderprops;

所以组件封装是为了复用 state 和 操作 state 的方法 (组件状态逻辑 )

理解Vue事件通信原理

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
/**
* Vue事件通信原理
*/

class EventBus{
constructor(){
// 订阅事件存储数组
this.handles = []
}

// 订阅方法
on(eventName,callbake){
// 当前事件类型
const triggerEvent = this.handles[eventName]
if (!triggerEvent) {
this.handles[eventName] = []
}

// 给事件类型添加订阅事件
this.handles[eventName].push(callbake)
}

//发布方法
emit(eventName,...params){
const triggerEvent = this.handles[eventName]

// 如果有当前事件类型,则全部触发
if (triggerEvent) {
triggerEvent.forEach( callbake => callbake(...params) )
}
}
}

// 创建一个eventBus实例
const evnetBus = new EventBus()

// 订阅事件类型
evnetBus.on('eventA',(args)=>console.log(`A--${args}`))
evnetBus.on('eventA',()=>console.log('A'))
evnetBus.on('eventB',()=>console.log('B'))
evnetBus.on('eventB',()=>console.log('BB'))
evnetBus.on('eventC',()=>console.log('C'))

// 发布订阅事件
evnetBus.emit('eventA',996)

/**
* 执行结果:
* A--996
* A
*/

程序题

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* 写出代码对下列数组去重并从大到小排列
*/

function ribArr(arr) {
const set = new Set()
// 使用集合特性去重
arr.forEach(item => set.add(item))
// 获得集合迭代对象
const setInterator = set.values()
let item = setInterator.next()
// 清空形参
arr = []
// 进行迭代对象
while (!item.done) {
arr.push(item.value)
item = setInterator.next()
}
// 排序
return arr.sort((a, b) => b - a)
}

console.log(ribArr([12, 2, 2, 234, 4, 6, 6, 7, 6])) //[ 234, 12, 7, 6, 4, 2 ]



/**
* 用 js 实现随机选取 10–100 之间的 10 个数字,存入一个数组,并排序。
*/
function getRandomArr(num, max, min) {
const arr = [];
while (num--) {
arr.push(Math.round(Math.random() * (max - min)) + min)
}
return arr.sort((a, b) => a - b)
}

console.log(getRandomArr(10, 100, 10)) //[ 33, 45, 48, 65, 68, 77, 80, 90, 91, 95 ]


/**
* 已知有字符串 foo=”get-element-by-id”,写一个 function 将其转化成 驼峰表示法”getElementById”。
*/

function transformHump(params) {
// 切割字符串
const paramsArr = params.split('-')
// 存储首个子串
params = paramsArr[0]
// 将非子串首字母转化为大写
for (let index = 1; index < paramsArr.length; index++) {
const item = paramsArr[index]
params += item.charAt(0).toUpperCase() + item.slice(1)
}
return params
}
console.log(transformHump('get-element-by-id')) //getElementById



/**
* 有这样一个 URL: http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,
* 请写一 段 JS 程序提取 URL 中的各个 GET 参数(参数名和参数个数不确定),将其按
* key-value 形式返回到一个 json ,如{a:’1′, b:’2′, c:”, d:’xxx’
* e:undefined}。
*/


function transformQuery(url) {
// 判断是否有添加请求
if (/\?.*$/.test(url)) {
const query = {}
// 获取请求参数数组
const queryArr = url.slice(url.indexOf('?') + 1).split('&')
// 遍历queryArr,并解析
queryArr.forEach(item => {
item = item.split('=')
console.log(item[1])
// 存入query对象
query[item[0]] = `${item[1]}`
})
return JSON.stringify(query)
}
return JSON.stringify(null)
}

console.log(transformQuery('http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e')) //{"a":"1","b":"2","c":"","d":"xxx","e":"undefined"}


/**
* 判断一个字符串中出现次数最多的字符,统计这个次数
*/

function getAppearMaxChar(str) {
let appearMaxArr = [];
for (let index = 0; index < str.length; index++) {
const params = str.match(new RegExp(str[index], 'g'));
if (appearMaxArr.length < params.length) appearMaxArr = params
}
return { char: appearMaxArr[0], count: appearMaxArr.length }
}

console.log(getAppearMaxChar('hjbjhbio888joi78g8f7f6rdr98hu')) //{ char: '8', count: 6 }


/**
* 将数字 12345678 转化成 RMB 形式 如: 12,345,678
*/

function transformRMB(num) {
const str = String(num)
let transformStr = ''
// 获取首部字符串,单独处理
const fristStr = str.substr(0, str.length % 3 || 3)
// 对非首部字符串进行拼接
for (let index = str.length - 3; index > 0; index -= 3) {
transformStr = ',' + str.substr(index, 3) + transformStr
}
// 拼接整个字符床
transformStr = fristStr + transformStr
return transformStr
}

transformRMB(123456789) //123,456,789

复习数组所有方法

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/**
* 复习Array所有方法
*/

let arr = [1,2,3,4,5,6,7,8,9,0]

// 头部出弹出
const result1 = arr.shift()
console.log('****************shift')
console.log('result:')
console.log(result1)
console.log('arr:')
console.log(arr)



// 头部添加
const result2 = arr.unshift(1)
console.log('****************unshift')
console.log('result:')
console.log(result2)
console.log('arr:')
console.log(arr)


// 尾部删除
const result3 = arr.pop()
console.log('****************pop')
console.log('result:')
console.log(result3)
console.log('arr:')
console.log(arr)

// 尾部添加
const result4 = arr.push(0)
console.log('****************push')
console.log('result:')
console.log(result4)
console.log('arr:')
console.log(arr)

// 转化为字符串
const result5 = arr.join('-')
console.log('****************join')
console.log('result:')
console.log(result5)
console.log('arr:')
console.log(arr)

/*
****************join
result:
1-2-3-4-5-6-7-8-9-0
arr:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
*/

// 反转
const result6 = arr.reverse()
console.log('****************reverse')
console.log('result:')
console.log(result6)
console.log('arr:')
console.log(arr)

/*
result:
[ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
arr:
[ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
*/


//排序
const result7 = arr.sort((a, b)=> a-b ) //回调函数的返回值为正值则不调换位置,负值则调换
console.log('****************sort')
console.log('result:')
console.log(result7)
console.log('arr:')
console.log(arr)

/*
result:
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]
arr:
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]
*/

// 合并数组
const paramArr = [11,22,33]
const result8 = arr.concat(paramArr)
console.log('****************concat')
console.log('result:')
console.log(result8)
console.log('arr:')
console.log(arr)

/*
result:
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33 ]
arr:
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
*/

// 截取数组
const result9 = arr.slice(1,4)
console.log('****************slice')
console.log('result:')
console.log(result9)
console.log('arr:')
console.log(arr)

/*
result:
[ 1, 2, 3 ]
arr:
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
*/
// 注意:slice的截取索引当为负值时,会自动加上数组长度,如果索引还为负值时等于0


// 可指定索引添加、删除、替换
const result10 = arr.splice(3,3,...[44,55,66,77]) // args: 指定位置 删除个数 替换数值...
console.log('****************splice')
console.log('result:')
console.log(result10)
console.log('arr:')
console.log(arr)

/*
result:
[ 3, 4, 5 ]
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 正序查找
const result11 = arr.indexOf(77,6) // args: 查找数值 指定开始位置
console.log('****************indexOf')
console.log('result:')
console.log(result11)
console.log('arr:')
console.log(arr)
/*
result:
6 //未找到为-1,找返回索引
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 倒叙查找
const result12 = arr.lastIndexOf(77,7) // args: 查找数值 指定开始位置
console.log('****************lastIndexOf')
console.log('result:')
console.log(result12)
console.log('arr:')
console.log(arr)
/*
result:
6
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 判断是否存在
const result13 = arr.includes(77) // args: 查找数值
console.log('****************includes')
console.log('result:')
console.log(result13)
console.log('arr:')
console.log(arr)
/*
result:
true
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 遍历forEach
const result14 = arr.forEach((item,index,arr) => console.log(`${item}---${index}`))
console.log('****************forEach')
console.log('result:')
console.log(result14)
console.log('arr:')
console.log(arr)
/*
0---0
1---1
2---2
44---3
55---4
66---5
77---6
6---7
7---8
8---9
9---10
****************forEach
result:
undefined
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/
// 注意:无返回值

// 过滤数组
const result15 = arr.filter((item,index,arr) => item >10 )
console.log('****************filter')
console.log('result:')
console.log(result15)
console.log('arr:')
console.log(arr)

/*
result:(回调函数返回值为true,将该元素加入返回数组)
[ 44, 55, 66, 77 ]
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 遍历数组查找数据,查找到便停止,返回该对象
const result16 = arr.find((item,index,arr) => item === 77 )
console.log('****************find')
console.log('result:')
console.log(result16)
console.log('arr:')
console.log(arr)
/*
result:
77
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/

// 遍历数组查找数据,查找到便停止,返回该对象的索引
const result17 = arr.findIndex((item,index,arr) => item === 77 )
console.log('****************findIndex')
console.log('result:')
console.log(result17)
console.log('arr:')
console.log(arr)
/*
result:
6
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 遍历数组判断是否符合自定义条件,一个符合便遍历停止,返回为true,否则反之false
const result18 = arr.some((item,index,arr) => item === 77 )
console.log('****************some')
console.log('result:')
console.log(result18)
console.log('arr:')
console.log(arr)
/*
result:
true
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/

// 遍历数组判断是否符合自定义条件,一个不符合便遍历停止,返回为false,否则反之true
const result19 = arr.every((item,index,arr) => item === 77 )
console.log('***************every')
console.log('result:')
console.log(result19)
console.log('arr:')
console.log(arr)
/*
result:
false
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/

// 迭代累加
const result20 = arr.reduce((a, b) => a + b )
console.log('***************reduce')
console.log('result:')
console.log(result20)
console.log('arr:')
console.log(arr)
/*
result:
275
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/

// 倒序迭代累加
const result21 = arr.reduceRight((a, b) => {console.log(`${a}---${b}`);
return a + b} )
console.log('***************reduceRight')
console.log('result:')
console.log(result21)
console.log('arr:')
console.log(arr)
/*
9---8
17---7
24---6
30---77
107---66
173---55
228---44
272---2
274---1
275---0
***************reduceRight
result:
275
arr:
[ 0, 1, 2, 44, 55, 66, 77, 6, 7, 8, 9 ]
*/


// 静态值填充
const result22 = arr.fill(999,3,7) //args: 填充值 填充起始位置 结束位置
console.log('***************fill')
console.log('result:')
console.log(result22)
console.log('arr:')
console.log(arr)

/*
result:
[ 0, 1, 2, 999, 999, 999, 999, 6, 7, 8, 9 ]
arr:
[ 0, 1, 2, 999, 999, 999, 999, 6, 7, 8, 9 ]
*/

// 数组内取值覆盖
const result26 = arr.copyWithin(3,6,9) //args: 赋值前一个位置 取值后一个位置 取值前一个结束位置
console.log('***************copyWithin')
console.log('result:')
console.log(result26)
console.log('arr:')
console.log(arr)
/*
result:
[ 0, 1, 2, 999, 6, 7, 999, 6, 7, 8, 9 ]
arr:
[ 0, 1, 2, 999, 6, 7, 999, 6, 7, 8, 9 ]
*/

/*返回迭代对象,使用next()获取对象*/
// 值和索引
console.dir(arr.entries())
// 索引
console.dir(arr.keys())
// 值
console.dir(arr.values())
// 转化数组
console.dir(arr.toString())


// 扁平化嵌套数组去空项 --》可以使用reduce + concat + 递归实现
const result23 = [12,23,34,545,,[12,13,34,45,56,6,[12,43,4]]].flat(Infinity) //args: 结构深度
console.log('***************flat')
console.log('result:')
console.log(result23)
console.log('arr:')
console.log(arr)
/*
[12, 23, 34, 545, 12, 13, 34, 45, 56, 6, 12, 43, 4]
*/


// .map遍历数组,返回值创建一个新数组
const result24 =[12,23,34,43,45,4].map((item, index, arr) => item*2 )
console.log('***************flat')
console.log('result:')
console.log(result24)
console.log('arr:')
console.log(arr)
/*
[24, 46, 68, 86, 90, 8]
*/

// .flatmap 会将返回数组进行flat(1)操作再返回数组
const result25 =[12,23,34534,2323].flatMap((item, index, arr) => [item*2] )
console.log('***************flat')
console.log('result:')
console.log(result25)
console.log('arr:')
console.log(arr)

/*
[24, 46, 69068, 4646]
*/

封装函数模拟关键字new实现

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
/**
* 模拟 关键字 new的实现
*
* 基本步骤:
* 1、创建一个新对象
* 2、改变构造函数的this指向,初始化新对象(初始化属性)
* 3、将新对象的对象原型__proto__指向构造函数的原型对象prototype(获取方法)
* 4、返回新对象
* */


function mockNew(){
// 获取构造函数,并将伪数组arguments除去构造函数,留下参数
const constructor = [].shift.call(arguments)

//对传入的构造函数进行判断,是否为一个函数
if (typeof constructor !== 'function') {
throw new Error('首个参数为函数')
}

// 创建构造函数的原型对象的新对象,直接可以获得构造函数的方法
const resultObj = Object.create(constructor.prototype)

//改变构造函数内部的this指向并调用,初始化新对象的属性,监视构造函数是否有返回值
const watchObj= constructor.apply(resultObj,arguments)

//返回新对象(watchObj判断构造函数是否有返回对象,有则返回则该构造函数return的对象,没有则返回新对象,new 关键字 ECMAScript规范)
return typeof watchObj === 'object' && watchObj != null ? watchObj : resultObj
}

// 构造函数,属性写在此处,new 该类时会自动执行
function Person ( name ) {
this.name = name
}
//该构造函数的方法,挂载到Person.prototype上
Person.prototype = {
show(){
console.log('我是构造函数的方法🥳')
}
}

const mockObj = mockNew(Person,'我是你的名字')

console.log(mockObj) //{ name: '我是你的名字' }

mockObj.show() //我是构造函数的方法🥳

模仿jQuery分装Ajax方法

原生封装

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
function ajax(obj) {
defaults = {
type: "get",
url: void 0,
async: true,
dataType: "json",
data: {},
jsonp: "callback",
jsonpCallBack:
"jQuery" + ("1.12.2" + Math.random()).replace(/\D/g, "") + Date.now(), //随机生成回调函数名,避免get请求的缓存问题
success(data) {
console.log(data);
},
};
// 处理传过来的参数对象,更新默认值
for (const key in obj) {
defaults[key] = obj[key];
}

// 由于发送给后台数据,是有 键=值&....,所以遍历数据对象defaults.data
var str = "";
if (defaults.data) {
for (const key in defaults.data) {
str += key + "=" + defaults.data[key] + "&";
}
}

// 剔出最后一个&
str = str.substring(0, str.length - 1);

//--------------------------------判断是否是jsonp数据类型,因为jsonp请求方式和普通的请求处理逻辑不同
if (!defaults.dataType == "jsonp") {
ajaxFa();
} else {
//--------------------------jsonp请求主要是通过script的src属性·

ajaxJsonp(str);
}
}
//**********************普通同源请求****************
function ajaxFa() {
//创建XMLHttpRequest
const xhr = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
// 创建成功 xhr.readyState == 0 数据初始化成功

// 为了以防背后xhr.send()请求过快而为监听到事件,所以先注册 可以监听xhr.readyState状态变化 与当响应数据xhr.response(xhr.readyState==3)过大会分段传回,也会不断触发该函数,该阶段触发的情况 与单独的onpregress()事件等价,
xhr.onreadystatechange = () => {
// 等于4时,响应数据接收解析完毕 --》可以等同与onload()事件
if (xhr.readyState == 4) {
// 等于200数据请求成功
if (xhr.status == 200) {
let data = xhr.responseText;
// 判断是否是json数据类型
if (defaults.dataType == "json") {
data = JSON.parse(data);
}
// 执行回调函数
defaults.success(data);
}
}
};

if (defaults.type == "get") {
// 获取可能请求参数有中文,所以需要进行转码
defaults.url += encodeURI("?" + str);
}
// 与服务器建立链接
xhr.open(defaults.type, defaults.url, defaults.async);
// 此时 xhr.readtSate == 1

// 判断请求类型是否为post
let parme = null;
if (defaults.type == "post") {
// 由于post请求,将参数是放到请求体中所以,转码方式与get有所不同,是设置请求头
xhr.setRequestHeader("Content-Type", "applocation/x-www-form-urlencoded");
parme = str;
}
// 发送请求
xhr.send(parme);
// 当转变为2时直接触发onreadystatechange
}
//**********************跨域请求jsonp****************
function ajaxJsonp(str) {
// 将回调函数挂载到window上
window[defaults.jsonCallBack] = (data) => {
defaults.success(data);
};
// console.log(defaults.jsonCallBack);
if (str) {
str = "&" + str;
}
// 将回调函数与请求参数添加到请求路径背后
defaults.url += "?" + defaults.jsonp + "=" + defaults.jsonCallBack + str;
// 应为服务器传回的数据是文本格式,但只要传回的文本格式是js解析的文本便会执行
// 创建script标签
const script = document.createElement("script");
// 为script标签添加src属性
script["src"] = defaults.url;
// 获取hend标签
const head = document.querySelector("head")[0];
// 将script标签添加到head中
head.appendChild(script);
}

优化代码结构

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
function ajax({
type = "get",
url = void 0,
data = {},
async = true,
dataType = "json",
jsonp = "callback",
jsonpCallBack = `JQuery_111_${Date.now()}${Math.random()
.toString()
.replace(".", "")}`,
success = (data) => {
console.log(data);
},
}) {
// 处理请求参数对象
let requestParams = "";
for (const key in data) {
if (Object.hasOwnProperty.call(data, key)) {
requestParams += `${key}=${data[key]}&`;
}
}
requestParams = requestParams.slice(0, -1);

// 判断是否是jsonp跨域
if (type === "jsonp") {
// 挂载调用函数
window[jsonpCallBack] = function (data) {
success(data);
};
const headNode = document.querySelector("head")[0];
const scriptNode = document.createElement("script");
scriptNode.src = `${url}?${jsonp}=${jsonpCallBack}&${requestParams}`;
headNode.appendChild(scriptNode);
}
// 使用XMLHttpRequest对象
else {
// 创建
const xhr = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
// xhr.readyState ===0

xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
let res = xhr.responseText;
dataType === "json" ? success(JSON.parse(res)) : success(res);
}
};

if (type === "get") {
url += "?" + requestParams;
}
xhr.open(type, encodeURI(url), async);

let temp = null;
if (type === "post") {
temp = requestParams;
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencode");
}
xhr.send(temp);
}
}

TypeScript 编写

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
interface AjaxConfig {
url: string; // 请求地址
type?: "get" | "head" | "delete" | "option" | "post" | "patch" | "put"; // 请求方式
dataType?: "json" | "jsonp" | "text" | "xml"; // 返回数据格式
data?: object; // 请求参数
jsonp?: string; // 后端获取回调函数名的键
jsonpCallBack?: string; // 回调函数名
async?: boolean; // 是否异步请求
success?: Function; // 请求成功回调
}

class Ajax {
private requestParams: string = "";
private requestSetting: AjaxConfig = {
url: "",
type: "get",
data: {},
dataType: "json",
jsonp: "callBack",
jsonpCallBack: `Peanut_Ajax_${Date.now()}_${Math.random()
.toString()
.replace(".", "")}`,
async: true,
success(data: any) {
console.log("请求成功:", data);
},
};
// 初始化数据
constructor(agrObj?: AjaxConfig) {
for (const key in agrObj) {
if (Object.prototype.hasOwnProperty.call(agrObj, key)) {
this.requestSetting[key] = agrObj[key];
}
}
}
// 请求方法
private request(agrObj?: AjaxConfig) {
for (const key in agrObj) {
if (Object.prototype.hasOwnProperty.call(agrObj, key)) {
this.requestSetting[key] = agrObj[key];
}
}

if (!this.requestSetting.url) {
throw new Error("没有URL");
return;
}

// 是否是jsonp请求
this.requestSetting.dataType === "jsonp"
? this.jsonpRequest()
: this.defaultRequest();
}

// 请求参数处理
private requestParamsTransform(obj: object): string {
let result: string = "";
for (const key in obj) {
result += `${key}=${obj[key]}&`;
}
return result.slice(0, -1);
}

// 普通请求
private defaultRequest() {
let { url, type, dataType, success, data, async } = this.requestSetting;
let sendParams = null;

const xhr: XMLHttpRequest = XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");

xhr.onreadystatechange = () => {
console.log(xhr.readyState, "onreadystatechange");
if (xhr.readyState === 4 && xhr.status === 200) {
const res =
dataType === "json" ? JSON.parse(xhr.responseText) : xhr.responseText;

success(res);
}
};

if (type === "get") {
const urlParams = encodeURI(this.requestParamsTransform(data));
url += "?" + urlParams;
}
xhr.open(type, url, async);

if (type === "post") {
sendParams = this.requestParamsTransform(data);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencode");
}
xhr.send(sendParams);
}

// jsonp请求
private jsonpRequest() {
const { jsonp, jsonpCallBack, url, success, data } = this.requestSetting;
// 挂载成功回调
window[jsonpCallBack] = success;
const request: string = this.requestParamsTransform(data);

const scriptNode: HTMLScriptElement = document.createElement("script");
scriptNode.src = `${url}?${jsonp}=${jsonpCallBack}&${request}`;

// 将script标签添加到头部
const head: HTMLHeadElement = document.getElementsByTagName("head")[0];
head.appendChild(scriptNode);
}
}