npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

muxing-my-tools

v1.0.9

Published

自定义工具包

Downloads

5

Readme

自定义工具函数库

一、各种自定义

1. 函数相关

1.1 call , apply , bind

1.1.1. API 说明

call()

语法: call(fn, obj, ...args)

功能: 执行fn, 使this为obj, 并将后面的n个参数传给fn(功能等同于函数对象的call方法)

apply()

语法: apply(fn, obj, args)

功能: 执行fn, 使this为obj, 并将args数组中的元素传给fn(功能等同于函数对象的apply方法)

bind()

语法: bind(fn, obj, ...args)

功能: 给fn绑定this为obj, 并指定参数为后面的n个参数 (功能等同于函数对象的bind方法)

1.1.2. 实现说明

区别call()/apply()/bind()

call(obj)/apply(obj): 调用函数, 指定函数中的this为第一个参数的值

bind(obj): 返回一个新的函数, 新函数内部会调用原来的函数, 且this为bind()指定的第一参数的值

注意: 如果obj是null/undefined, this为window

应用

call()/apply()应用: 根据伪数组生成真数组

bind(): react中组件的自定义方法 / vue中的事件回调函数内部

自定义call()/apply()

给obj添加一个临时方法, 方法名任意, 值为当前函数

通过obj调用这个临时方法, 并将接收的参数传入

删除obj上的这个临时方法属性

自定义实现bind()

返回一个新函数

在新函数内部通过原函数对象的call方法来执行原函数

指定原函数的this为obj

指定参数为bind调用的参数和后面新函数调用的参数

1.1.3. 编码实现

自定义函数对象的call方法: src/function/call.js

1 /*

2 自定义函数对象的call方法
3 */
4 export function call(fn, obj, ...args) {
5 console.log('call()')
6 ​
7 // 如果obj是undefined/null, this指定为window
8 if (obj===undefined || obj===null) {
9 // return fn(...args)
10 obj = window
11 }
12 ​
13 // 给obj添加一个临时方法, 方法指向的函数就是fn
14 obj.tempFn = fn
15 // 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj
16 const result = obj.tempFn(...args)
17 // 删除obj上的临时方法
18 delete obj.tempFn
19 // 返回fn执行的结果
20 return result
21 }

自定义函数对象的apply方法: src/function/apply.js

1 /*

2 自定义函数对象的apply方法
3 */
4 export function apply(fn, obj, args) {
5 console.log('apply()')
6 ​
7 // 如果obj是undefined/null, this指定为window
8 if (obj===undefined || obj===null) {
9 // return fn(...args)
10 obj = window
11 }
12 ​
13 // 给obj添加一个临时方法, 方法指向的函数就是fn
14 obj.tempFn = fn
15 // 通过obj来调用这个方法 ==> 也就会执行fn函数 ==> 此时fn中的this肯定为obj
16 const result = obj.tempFn(...args)
17 // 删除obj上的临时方法
18 delete obj.tempFn
19 // 返回fn执行的结果
20 return result
21 }

自定义函数对象的bind方法: src/function/bind.js

1 import {call} from './call'
2 /*
3 自定义函数对象的bind方法
4 */
5 export function bind(fn, obj, ...args) {
6 console.log('bind()')
7 // 返回一个新函数
8 return (... args2) => {
9 // 通过call调用原函数, 并指定this为obj, 实参为args与args
10 return call(fn, obj, ...args, ...args2)
11 }
12 }
1 // call的实现
2 Function.prototype.call2 = function (context) {
3 var context = context || window;
4 context.fn = this;
5 ​
6 var args = [];
7 for(var i = 1 , len = arguments.length; i < len; i++) {
8 args.push('arguments[' + i + ']');
9 }
10 ​
11 var result = eval('context.fn(' + args +')');
12 ​
13 delete context.fn
14 return result;
15 }
16 ​
17 // 测试一下
18 var value = 2 ;
19 ​
20 var obj = {
21 value: 1
22 }
23 ​
24 function bar(name, age) {
25 console.log(this.value);
26 return {
27 value: this.value,
28 name: name,
29 age: age
30 }
31 }
32 ​
33 bar.call2(null); // 2
34 ​
35 console.log(bar.call2(obj, 'kevin', 18 ));
1 // apply实现
2 Function.prototype.apply = function (context, arr) {
3 var context = Object(context) || window;
4 context.fn = this;
5 ​
6 var result;
7 if (!arr) {
8 result = context.fn();
9 }else {
10 var args = [];
11 for (var i = 0 , len = arr.length; i < len; i++) {
12 args.push('arr[' + i + ']');
13 }
14 result = eval('context.fn(' + args + ')')
15 }
16 ​
17 delete context.fn
18 return result;
19 }
1 // bind实现
2 Function.prototype.bind2 = function (context) {
3 if (typeof this !== "function") {
4 throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
5 }
6 var self = this;
7 var args = Array.prototype.slice.call(arguments, 1 );
8 var fNOP = function () {};
9 var fBound = function () {
10 var bindArgs = Array.prototype.slice.call(arguments);
11 return self.apply(this instanceof fNOP? this : context, args.concat(bindArgs));
12 }
13 fNOP.prototype = this.prototype;
14 fBound.prototype = new fNOP();
15 return fBound;
16 }

1.2. 函数节流与函数防抖

(^) 1.2.1. 相关理解

事件频繁触发可能造成的问题?

一些浏览器事件:window.onresize、window.mousemove等,触发的频率非常高,会造成界面卡顿

如果向后台发送请求,频繁触发,对服务器造成不必要的压力

如何限制事件处理函数频繁调用

函数节流

函数防抖

函数节流(throttle)

理解:

在函数需要频繁触发时: 函数执行一次后,只有大于设定的执行周期后才会执行第二次

适合多次事件按时间做平均分配触发

场景:

窗口调整(resize)

⻚面滚动(scroll)

DOM 元素的拖拽功能实现(mousemove)

抢购疯狂点击(click)

函数防抖(debounce)

理解:

在函数需要频繁触发时: 在规定时间内,只让最后一次生效,前面的不生效。

适合多次事件一次响应的情况

场景:

输入框实时搜索联想(keyup/input)

区别函数节流与防抖

1.2.2.API 说明

throttle() 节流

语法: throttle(callback, wait)

功能: 创建一个节流函数,在 wait 毫秒内最多执行 callback 一次

debounce() 防抖

语法: debounce(callback, wait)

功能: 创建一个防抖动函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 callback

1.2.3.编码实现

src/function/throttle.js: 函数节流

1 /*

2 实现函数节流

3 - 语法: throttle(callback, wait)
4 - 功能: 创建一个节流函数,在 wait 毫秒内最多执行 `callback` 一次
5 */
6 export function throttle(callback, wait) {
7 let start = 0
8 // 返回一个事件监听函数(也就是节流函数)
9 return function (event) {
10 console.log('throttle event')
11 // 只有当距离上次处理的时间间隔超过了wait时, 才执行处理事件的函数
12 const current = Date.now()
13 if ( current - start > wait) {
14 callback.call(this, event) // 需要指定this和参数
15 start = current
16 }
17 }
18 }

src/function/debounce.js: 函数防抖

1 /*

2 实现函数防抖

3 - 语法: debounce(callback, wait)
4 - 功能: 创建一个防抖动函数,该函数会从上一次被调用后,延迟 `wait` 毫秒后调用 `callback`
5 */
6 export function debounce (callback, wait) {
7 // 用来保存定时器任务的标识id
8 let timeoutId = - 1
9 // 返回一个事件监听函数(也就是防抖函数)
10 return function (event) {
11 console.log('debounce event')
12 // 清除未执行的定时器任务
13 if (timeoutId!==- 1 ) {
14 clearTimeout(timeoutId)
15 }
16 // 启动延迟 await 时间后执行的定时器任务
17 timeoutId = setTimeout(() => {
18 // 调用 callback 处理事件
19 callback.call(this, event)
20 // 处理完后重置标识
21 timeoutId = - 1
22 }, wait)
23 }
24 }

2. 数组相关

2.1. API 列表

1. map()

2. reduce()

3. filter()

4. find()

5. findIndex()

6. every()

7. some()

8. unique1() / unique2() / unique3()

9. concat()

10. slice()

11. flatten()

12. chunk() / chunk2()

13. difference()

14. pull()

15. pullAll()

16. drop()

17. dropRight()

2.2. 2.2.1. 数组声明式系列方法使用数组声明式系列方法

map(): 返回一个由回调函数的返回值组成的新数组

reduce(): 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次

回调函数,并返回最后一次回调函数的返回值

filter(): 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回

find(): 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。

findIndex(): 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。

every(): 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。

some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。

2.2.2. 编码实现

1 src/array/declares.js: 实现数组声明式处理系列工具函数
2 /*
3 实现map()
4 */
5 export function map (array, callback) {
6 const arr = []
7 for (let index = 0 ; index < array.length; index++) {
8 // 将callback的执行结果添加到结果数组中
9 arr.push(callback(array[index], index))
10 }
11 return arr
12 }
13 ​
14 /*
15 实现reduce()
16 */
17 export function reduce (array, callback, initValue) {
18 let result = initValue
19 for (let index = 0 ; index < array.length; index++) {
20 // 调用回调函数将返回的结果赋值给result
21 result = callback(result, array[index], index)
22 }
23 return result
24 }
25 ​
26 /*
27 实现filter()
28 */
29 export function filter(array, callback) {
30 ​
31 const arr = []
32 for (let index = 0 ; index < array.length; index++) {
33 if (callback(array[index], index)) {
34 arr.push(array[index])
35 }

36 }

37 return arr
38 }
39 ​
40 /*
41 实现find()
42 */
43 export function find (array, callback) {
44 for (let index = 0 ; index < array.length; index++) {
45 if (callback(array[index], index)) {
46 return array[index]
47 }
48 }
49 return undefined
50 }
51 ​
52 /*
53 实现findIndex()
54 */
55 export function findIndex (array, callback) {
56 for (let index = 0 ; index < array.length; index++) {
57 if (callback(array[index], index)) {
58 return index
59 }
60 }
61 return - 1
62 }
63 ​
64 /*
65 实现every()
66 */
67 export function every (array, callback) {
68 for (let index = 0 ; index < array.length; index++) {
69 if (!callback(array[index], index)) { // 只有一个结果为false, 直接返回false
70 return false
71 }
72 }
73 return true
74 }
75 ​
76 /*
77 实现some()
78 */
79 export function some (array, callback) {
80 for (let index = 0 ; index < array.length; index++) {
81 if (callback(array[index], index)) { // 只有一个结果为true, 直接返回true
82 return true
83 }
84 }
85 return false
86 }

2.3.2.3.1.API 数组去重说明

根据当前数组产生一个去除重复元素后的新数组

如: [2, 3, 2, 7, 6, 7] ==> [2, 3, 7, 6]

2.3.2. 实现

方法1: 利用forEach()和indexOf()

说明: 本质是双重遍历, 效率差些

方法2: 利用forEach() + 对象容器

说明: 只需一重遍历, 效率高些

方法3: 利用ES6语法: from + Set 或者 ... + Set

说明: 编码简洁

2.3.3. src/array/unique.js 编码实现

1 /*
2 方法 1 : 利用forEach()和indexOf()
3 说明: 本质是双重遍历, 效率差些
4 */
5 export function unique1 (array) {
6 const arr = []
7 array.forEach(item => {
8 if (arr.indexOf(item)===- 1 ) {
9 arr.push(item)
10 }
11 })
12 return arr
13 }
14 ​
15 /*
16 方法 2 : 利用forEach() + 对象容器
17 说明: 只需一重遍历, 效率高些
18 */
19 export function unique2 (array) {
20 const arr = []
21 const obj = {}
22 array.forEach(item => {
23 if (!obj.hasOwnProperty(item)) {
24 obj[item] = true
25 arr.push(item)
26 }
27 })
28 return arr
29 }
30 ​
31 /*
32 方法 3 : 利用ES6语法
33 1 ). from + Set
34 2 ). ... + Set
35 说明: 编码简洁
36 */
37 export function unique3 (array) {
38 // return Array.from(new Set(array))
39 return [...new Set(array)]
40 }

2.4. 数组合并与切片

2.4.1. API concat(): 说明 合并

语法: var new_array = concat(array, value1[, value2[, ...[, valueN]]])

功能: 将n个数组或值与当前数组合并生成一个新数组, 原始数组不会被改变

slice(): 切片

语法: var new_array = slice(array, [begin[, end]])

功能: 返回一个由 begin 和 end 决定的原数组的浅拷⻉, 原始数组不会被改变

2.4.2. 编码实现 src/array/concat.js: 自定义数组合并

1 /*
2 语法: var new_array = concat(old_array, value1[, value2[, ...[, valueN]]])
3 功能: 将n个数组或值与当前数组合并生成一个新数组
4 */
5 export function concat (array, ...values) {
6 const arr = [...array]
7 values.forEach(value => {
8 if (Array.isArray(value)) {
9 arr.push(...value)
10 } else {
11 arr.push(value)
12 }
13 })
14 return arr
15 }

src/array/slice.js: 自定义数组切片

1 /*

2 语法: var new_array = slice(oldArr, [begin[, end]])
3 功能: 返回一个由 begin 和 end 决定的原数组的浅拷⻉, 原始数组不会被改变
4 */
5 export function slice (array, begin, end) {
6 // 如果当前数组是[], 直接返回[]
7 if (array.length === 0 ) {
8 return []
9 }
10 ​
11 // 如果begin超过最大下标, 直接返回[]
12 begin = begin || 0
13 if (begin >= array.length) {
14 return []
15 }
16 ​
17 // 如果end不大于begin, 直接返回[]
18 end = end || array.length
19 if (end > array.length) {
20 end = array.length
21 }
22 if (end <= begin) {
23 return []
24 }
25 ​
26 // 取出下标在[begin, end)区间的元素, 并保存到最终的数组中
27 const arr = []
28 for (let index = begin; index < end; index++) {

29 arr.push(array[index]) 30 } 31 ​ 32 return arr 33 }

2.5. 数组扁平化

2.5.1.API 说明

语法: flatten(array)

取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中

如: [1, [3, [2, 4]]] ==> [1, 3, 2, 4]

2.5.2. 编码实现 src/array/flatten.js

方法一: 递归 + reduce() + concat()

方法二: ... + some() + concat()

1 /*

2 数组扁平化: 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中

3 如: [ 1 , [ 3 , [ 2 , 4 ]]] ==> [ 1 , 3 , 2 , 4 ]

4 */

5 ​

6 /*

7 方法一: 递归 + reduce() + concat()
8 */
9 export function flatten1 (array) {
10 return array.reduce((pre, item) => {
11 if (Array.isArray(item) && item.some(cItem => Array.isArray(cItem))) {
12 return pre.concat(flatten1(item))
13 } else {
14 return pre.concat(item)
15 }
16 }, [])
17 }
18 ​
19 /*
20 方法二: ... + some() + concat()
21 */
22 export function flatten2 (array) {
23 let arr = [].concat(...array)
24 while (arr.some(item => Array.isArray(item))) {
25 arr = [].concat(...arr)
26 }
27 return arr
28 }

2.6.2.6.1.API 数组分块说明

语法: chunk(array, size)

功能: 将数组拆分成多个 size ⻓度的区块,每个区块组成小数组,整体组成一个二维数组

如: [1, 3, 5, 6, 7, 8] 调用chunk(arr, 4) ==> [[1, 3, 5, 6], [7,8]]

2.6.2. 编码实现 src/array/chunk.js

1 /*
2 将数组拆分成多个 size ⻓度的区块,每个区块组成小数组,整体组成一个二维数组

3 */

4 export function chunk (array, size) {
5 if (array.length=== 0 ) {
6 return []
7 }
8 size = size || 1
9 ​
10 const bigArr = []
11 let smallArr = []
12 ​
13 array.forEach(item => {
14 if (smallArr.length=== 0 ) {
15 bigArr.push(smallArr)
16 }
17 smallArr.push(item)
18 if (smallArr.length===size) {
19 smallArr = []
20 }
21 })
22 ​
23 return bigArr
24 }

2.7. 数组取差异

2.7.1.API 说明

语法: difference(arr1, arr2)

功能: 得到当前数组中所有不在arr中的元素组成的数组(不改变原数组)

例子: difference([1,3,5,7], [5, 8]) ==> [1, 3, 7]

2.7.2. 编码实现 src/array/difference.js

1 /*
2 difference(arr1, arr2): 得到arr1中所有不在arr2中的元素组成的数组(不改变原数组)
3 如: [ 1 , 3 , 5 , 7 ].difference([ 5 , 8 ]) ==> [ 1 , 3 , 7 ]
4 */
5 export function difference (arr1, arr2=[]) {
6 if (arr1.length=== 0 ) {
7 return []
8 } else if (arr2.length=== 0 ) {
9 return arr1.slice()
10 }
11 return arr1.filter(item => arr2.indexOf(item)===- 1 )
12 }

2.8.2.8.1.API 删除数组中部分元素相关

pull(array, ...values):

删除原数组中与value相同的元素, 返回所有删除元素的数组

说明: 原数组发生了改变

如: pull([1,3,5,3,7], 2, 7, 3, 7) ===> 原数组变为[1, 5], 返回值为[3,3,7]

pullAll(array, values):

功能与pull一致, 只是参数变为数组

如: pullAll([1,3,5,3,7], [2, 7, 3, 7]) ===> 数组 1 变为[1, 5], 返回值为[3,3,7]

2.8.2. 编码实现

src/array/pull.js

1 /*

2 1. pull(array, ...values):
3 删除数组中与value相同的元素, 返回所有删除元素的数组
4 说明: 数组发生了改变
5 如: pull([ 1 , 3 , 5 , 3 , 7 ], 2 , 7 , 3 , 7 ) ===> 数组变为[ 1 , 5 ], 返回值为[ 3 , 3 , 7 ]
6 2. pullAll(array, values):
7 功能与pull一致, 只是参数变为数组
8 如: pullAll([ 1 , 3 , 5 , 3 , 7 ], [ 2 , 7 , 3 , 7 ]) ===> 数组变为[ 1 , 5 ], 返回值为[ 3 , 3 , 7 ]
9 */
10 export function pull (array, ...values) {
11 if (array.length=== 0 || values.length=== 0 ) {
12 return []
13 }
14
15 var result = []
16 for (let index = 0 ; index < array.length; index++) {
17 const item = array[index]
18 if (values.indexOf(item)!==- 1 ) {
19 array.splice(index, 1 )
20 result.push(item)
21 index--
22 }
23 }
24 ​
25 return result
26 }
27 ​
28 export function pullAll (array, values) {
29 if (!values || !Array.isArray(values)) {
30 return []
31 }
32 return pull(array, ...values)
33 }

2.9. 2.9.1.API 得到数组的部分元素相关

drop(array, count)

得到当前数组过滤掉左边count个后剩余元素组成的数组

说明: 不改变当前数组, count默认是 1

如: drop([1,3,5,7], 2) ===> [5, 7]

dropRight(array, count)

得到当前数组过滤掉右边count个后剩余元素组成的数组

说明: 不改变当前数组, count默认是 1

如: dropRight([1,3,5,7], 2) ===> [1, 3]

2.9.2. 编码实现

src/array/drop.js

1 /*

2 1. drop(array, count):
3 得到数组过滤掉左边count个后剩余元素组成的数组
4 说明: 不改变当前数组, count默认是 1
5 如: drop([ 1 , 3 , 5 , 7 ], 2 ) ===> [ 5 , 7 ]
6 2. dropRight(array, count):
7 得到数组过滤掉右边count个后剩余元素组成的数组
8 说明: 不改变数组, count默认是 1
9 如: dropRight([ 1 , 3 , 5 , 7 ], 2 ) ===> [ 1 , 3 ]
10 */
11 ​
12 export function drop (array, count= 1 ) {
13 if (array.length === 0 || count >= array.length) {
14 return []
15 }
16 ​
17 return array.filter((item, index) => index>=count)
18 }
19 ​
20 export function dropRight (array, count= 1 ) {
21 if (array.length === 0 || count >= array.length) {
22 return []
23 }
24 ​
25 return array.filter((item, index) => index < array.length-count)
26 }

3. 对象相关

3.1. 相关 API

newInstance()

myInstanceOf()

mergeObject()

clone1() / clone2()

deepClone1() / deepClone2() / deepClone3() / deepClone4()

3.2.3.2.1.API 自定义相关 new

语法: newInstance(Fn, ...args)

功能: 创建Fn构造函数的实例对象

3.2.3. 编码实现 src/object/newInstance.js

1 export function newInstance (Fn, ...args) {
2 // 创建一个空的object实例对象obj, 作为Fn的实例对象
3 const obj = {}
4 // 将Fn的prototype属性值赋值给obj的__proto__属性值
5 obj.__proto__ = Fn.prototype
6 // 调用Fn, 指定this为obj, 参数为args列表
7 const result = Fn.call(obj, ...args)
8 // 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
9 // 否则返回obj
10 return result instanceof Object? result : obj
11 }
12 ​

3.3.3.3.1. API 自定义相关 instanceof

语法: myInstanceOf(obj, Type)

功能: 判断obj是否是Type类型的实例

实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回tru, 否则返回false

3.3.2. 编码实现 src/object/myInstanceOf.js

1 export function myInstanceOf(obj, Type) {
2 // 得到原型对象
3 let protoObj = obj.__proto__
4 ​
5 // 只要原型对象存在
6 while(protoObj) {
7 // 如果原型对象是Type的原型对象, 返回true
8 if (protoObj === Type.prototype) {
9 return true
10 }
11 // 指定原型对象的原型对象
12 protoObj = protoObj.__proto__
13 }
14
15 return false
16 }

3.4.3.4.1.API 合并多个对象相关

语法: object mergeObject(...objs)

功能: 合并多个对象, 返回一个合并后对象(不改变原对象)

例子:

{ a: [{ x: 2 }, { y: 4 }], b: 1}

{ a: { z: 3}, b: [2, 3], c: 'foo'}

合并后: { a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }

3.4.2. 编码实现 src/object/mergeObject.js

1 export function mergeObject(...objs) {
2 const result = {}
3 ​
4 // 遍历objs
5 objs.forEach(obj => {
6 Object.keys(obj).forEach(key => {
7 // 如果result还没有key值属性
8 if (!result.hasOwnProperty(key)) {
9 result[key] = obj[key]
10 } else { // 否则 合并属性
11 result[key] = [].concat(result[key], obj[key])
12 }
13 })
14 })
15 ​
16 // 可以使用reduce来代替forEach手动添加
17 return result
18 }

3.5. 3.5.1. 对象区别浅拷⻉与深拷⻉ / 数组拷⻉

纯语言表达:

浅拷⻉: 只是复制了对象属性或数组元素本身(只是引用地址值)

深拷⻉: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)

举例说明: 拷⻉persons数组(多个人对象的数组)

浅拷⻉: 只是拷⻉了每个person对象的引用地址值, 每个person对象只有一份

深拷⻉: 每个person对象也被复制了一份新的

3.5.2. 实现浅拷⻉ src/object/clone.js

1 /*
2 实现浅拷⻉
3 方法一: 利用ES6语法
4 方法二: 利用ES5语法: for...in
5 */
6 /* 方法一: 利用ES6语法*/
7 export function clone1(target) {
8 // 如果是对象(不是函数, 也就是可能是object对象或者数组)
9 if (target!=null && typeof target==='object') {
10 if (target instanceof Array) {
11 // return target.slice()
12 // return target.filter(() => true)
13 // return target.map(item => item)
14 return [...target]
15 } else {
16 // return Object.assign({}, target)
17 return {...target}
18 }
19 }
20 // 基本类型或者函数, 直接返回
21 return target
22 }
23 ​
24 /* 方法二: 利用ES5语法: for...in */
25 export function clone2(target) {
26 if (target!=null && typeof target==='object') {
27 const cloneTarget = Array.isArray(target)? [] : {}
28 for (let key in target) {
29 if (target.hasOwnProperty(key)) {
30 cloneTarget[key] = target[key]
31 }
32 }
33 return cloneTarget
34 } else {
35 return target
36 }
37 }

3.5.3. 实现深拷⻉

实现一: 大众乞丐版

问题1: 函数属性会丢失

问题2: 循环引用会出错

实现二: 面试基础版

解决问题1: 函数属性还没丢失

实现三: 面试加强版本

解决问题2: 循环引用正常

实现四: 面试加强版本2(优化遍历性能)

数组: while | for | forEach() 优于 for-in | keys()&forEach()

对象: for-in 与 keys()&forEach() 差不多

编码实现: src/object/deepClone.js

1 /*

2 深度克隆

3 1 ). 大众乞丐版

4 问题 1 : 函数属性会丢失

5 问题 2 : 循环引用会出错

6 2 ). 面试基础版本

7 解决问题 1 : 函数属性还没丢失

8 3 ). 面试加强版本

9 解决问题 2 : 循环引用正常

10 4 ). 面试加强版本 2 (优化遍历性能)

11 数组: while | for | forEach() 优于 for-in | keys()&forEach() 12 对象: for-in 与 keys()&forEach() 差不多 13 / 14 ​ 15 / 16 1 ). 大众乞丐版 17 问题 1 : 函数属性会丢失 18 问题 2 : 循环引用会出错 19 / 20 export function deepClone1(target) { 21 return JSON.parse(JSON.stringify(target)) 22 } 23 ​ 24 / 25 2 ). 面试基础版本 26 解决问题 1 : 函数属性还没丢失 27 / 28 export function deepClone2 (target) { 29 if (target!==null && typeof target==='object') { 30 const cloneTarget = target instanceof Array? [] : {} 31 32 for (const key in target) { 33 if (target.hasOwnProperty(key)) { 34 cloneTarget[key] = deepClone2(target[key]) 35 } 36 } 37 ​ 38 return cloneTarget 39 } 40 ​ 41 return target 42 } 43 ​ 44 / 45 3 ). 面试加强版本 46 解决问题 2 : 循环引用正常 47 */ 48 export function deepClone3 (target, map=new Map()) { 49 if (target!==null && typeof target==='object') {

50 // 从缓存容器中读取克隆对象

51 let cloneTarget = map.get(target) 52 // 如果存在, 返回前面缓存的克隆对象 53 if (cloneTarget) { 54 return cloneTarget 55 } 56 // 创建克隆对象(可能是{}或者[]) 57 cloneTarget = target instanceof Array? [] : {} 58 // 缓存到map中 59 map.set(target, cloneTarget) 60 ​ 61 for (const key in target) { 62 if (target.hasOwnProperty(key)) { 63 // 递归调用, 深度克隆对象, 且传入缓存容器map 64 cloneTarget[key] = deepClone3(target[key], map) 65 } 66 } 67 ​ 68 return cloneTarget 69 } 70 ​ 71 return target 72 } 73 ​ 74 /* 75 4 ). 面试加强版本 2 (优化遍历性能) 76 数组: while | for | forEach() 优于 for-in | keys()&forEach() 77 对象: for-in 与 keys()&forEach() 差不多 78 */ 79 export function deepClone4 (target, map=new Map()) { 80 if (target!==null && typeof target==='object') { 81 // 从缓存容器中读取克隆对象 82 let cloneTarget = map.get(target) 83 // 如果存在, 返回前面缓存的克隆对象 84 if (cloneTarget) { 85 return cloneTarget 86 } 87 // 创建克隆对象(可能是{}或者[]) 88 if (target instanceof Array) { 89 cloneTarget = [] 90 // 缓存到map中 91 map.set(target, cloneTarget) 92 target.forEach((item, index) => { 93 cloneTarget[index] = deepClone4(item, map) 94 }) 95 } else { 96 cloneTarget = {} 97 // 缓存到map中 98 map.set(target, cloneTarget) 99 Object.keys(target).forEach(key => { 100 cloneTarget[key] = deepClone4(target[key], map) 101 }) 102 } 103 ​

104 return cloneTarget
105 }
106 ​
107 return target
108 }

4.4.1. API 字符串相关相关

字符串倒序

语法: reverseString(str)

功能: 生成一个倒序的字符串

字符串是否是回文

语法: palindrome(str)

功能: 如果给定的字符串是回文,则返回 true ;否则返回 false

截取字符串

语法: truncate(str, num)

功能: 如果字符串的⻓度超过了num, 截取前面num⻓度部分, 并以...结束

4.2. 编码实现 src/string/index.js

1 /*

2 1. 字符串倒序: reverseString(str) 生成一个倒序的字符串
3 */
4 export function reverseString(str) {
5 // return str.split('').reverse().join('')
6 // return [...str].reverse().join('')
7 return Array.from(str).reverse().join('')
8 }
9 ​
10 /*
11 2. 字符串是否是回文: palindrome(str) 如果给定的字符串是回文,则返回 true ;否则返回 false
12 */
13 export function palindrome(str) {
14 return str === reverseString(str)
15 }
16 ​
17 /*
18 3. 截取字符串: truncate(str, num) 如果字符串的⻓度超过了num, 截取前面num⻓度部分, 并以...结束
19 */
20 export function truncate(str, num) {
21 return str.length > num? str.slice( 0 , num) + '...' : str
22 }
23 ​

6. 手写 DOM 事件监听 ( 带委托 )

6.1. 理解事件冒泡与事件委托

事件冒泡的流程

基于DOM树形结构

事件在目标元素上处理后, 会由内向外(上)逐层传递

应用场景: 事件代理/委托/委派

事件委托/代理

将多个子元素的同类事件监听委托给(绑定在)共同的一个父组件上

好处:

减少内存占用(事件监听回调从n变为

动态添加的内部元素也能响应

6.2. API 相关

语法:addEventListener(element, type, fn, selector)

说明:如果selector没有,直接给element绑定事件,如果selector有,将selector对应的多个元素的事件委托绑

定给父元素element

6.2. 编码实现

src/event-bind/index.js

1 /*

2 语法:addEventListener(element, type, fn, selector)
3 说明:如果selector没有,直接给element绑定事件,
4 如果selector有,将selector对应的多个元素的事件委托绑定给父元素element
5 */
6 export function addEventListener(element, type, fn, selector) {
7 // 如果没有指定selector, 普通的事件绑定
8 if (!selector) {
9 element.addEventListener(type, fn)
10 } else {// 否则是代委托的事件绑定
11 element.addEventListener(type, function (event) {
12 // 得到真正发生事件的目标元素
13 const target = event.target
14 // 如果与选择器匹配
15 if (target.matches(selector)) {
16 // 调用处理事件的回调fn, 并指定this为目标元素, 参数为event
17 fn.call(target, event)
18 }
19 })
20 }
21 }

7.7.1. A P I 手写 ajax 相关请求函数

语法:

axios(options)

参数配置对象:url, method, params与data

返回值为:promise对象

axios.get(url, options)

axios.post(url, data, options)

axios.put(url, data, options)

axios.delete(url, options)

功能:使用xhr发送ajax请求的工具函数,与axios库功能类似

7. 2. 实现整体流程

1. 函数的参数为一个配置对象

{ url: '', // 请求地址 method: '', // 请求方式GET/POST/PUT/DELETE params: {}, // GET/DELETE请求的query参数 data:

{}, // POST或DELETE请求的请求体参数 }

2. 返回值: 函数的返回值为promise, 成功的结果为response, 失败的结果为error

3. 能处理多种类型的请求: GET/POST/PUT/DELETE

4. 响应json数据自动解析为js的对象/数组

7. 3. 编码实现 src/axios/index.js

1 /* 发送任意类型请求的函数 */

2 function axios({
3 url,
4 method='GET',
5 params={},
6 data={}
7 }) {
8 // 返回一个promise对象

9 return new Promise((resolve, reject) => { 10 ​ 11 // 处理method(转大写) 12 method = method.toUpperCase() 13 14 // 处理query参数(拼接到url上) id=1&xxx=abc 15 /* { id: 1, xxx: 'abc'} */ 16 let queryString = '' 17 Object.keys(params).forEach(key => { 18 queryString += ${key}=${params[key]}& 19 }) 20 if (queryString) { // id=1&xxx=abc& 21 // 去除最后的& 22 queryString = queryString.substring( 0 , queryString.length- 1 ) 23 // 接到url 24 url += '?' + queryString 25 } 26 ​ 27 ​ 28 // 1. 执行异步ajax请求 29 // 创建xhr对象 30 const request = new XMLHttpRequest() 31 // 打开连接(初始化请求, 没有请求) 32 request.open(method, url, true) 33 34 // 发送请求 35 if (method==='GET') { 36 request.send() 37 } else if (method==='POST' || method==='PUT' || method==='DELETE'){ 38 // 告诉服务器请求体的格式是json 39 request.setRequestHeader('Content-Type', 'application/json;charset=utf-8') 40 // 发送json格式请求体参数 41 request.send(JSON.stringify(data)) 42 } 43 44 // 绑定状态改变的监听 45 request.onreadystatechange = function () { 46 // 如果请求没有完成, 直接结束 47 if (request.readyState!== 4 ) { 48 return 49 } 50 // 如果响应状态码在[200, 300)之间代表成功, 否则失败 51 const {status, statusText} = request 52 // 2.1. 如果请求成功了, 调用resolve() 53 if (status>= 200 && status<= 299 ) { 54 // 准备结果数据对象response 55 const response = { 56 data: JSON.parse(request.response), 57 status, 58 statusText 59 } 60 resolve(response) 61 } else { // 2.2. 如果请求失败了, 调用reject() 62 reject(new Error('request error status is ' + status))

63 }

64 }

65 })

66 }

67 ​

68 /* 发送特定请求的静态方法 */

69 axios.get = function (url, options) {
70 return axios(Object.assign(options, {url, method: 'GET'}))
71 }
72 axios.delete = function (url, options) {
73 return axios(Object.assign(options, {url, method: 'DELETE'}))
74 }
75 axios.post = function (url, data, options) {
76 return axios(Object.assign(options, {url, data, method: 'POST'}))
77 }
78 axios.put = function (url, data, options) {
79 return axios(Object.assign(options, {url, data, method: 'PUT'}))
80 }
81 ​
82 export default axios

8. 手写事件总线

8.1.API 说明

1. eventBus: 包含所有功能的事件总线对象

2. eventBus.on(eventName, listener): 绑定事件监听

3. eventBus.emit(eventName, data): 分发事件

4. eventBus.off(eventName): 解绑指定事件名的事件监听, 如果没有指定解绑所有

8.2. 编码实现 src/event-bus/index.js

1 const eventBus = {}
2 ​
3 /*
4 {
5 add: [callback1, callback2]
6 delete: [callback3]
7 }
8 */
9 let callbacksObj = {}
10 ​
11 /*
12 绑定事件监听
13 */
14 eventBus.on = function (eventName, callback) {
15 const callbacks = callbacksObj[eventName]
16 if (callbacks) {
17 callbacks.push(callback)
18 } else {
19 callbacksObj[eventName] = [callback]
20 }
21 }
22 ​
23 /*
24 分发事件

25 */

26 eventBus.emit = function (eventName, data) {
27 const callbacks = callbacksObj[eventName]
28 if (callbacks && callbacks.length > 0 ) {
29 callbacks.forEach(callback => {
30 callback(data)
31 })
32 }
33 }
34 ​
35 /*
36 移除事件监听
37 */
38 eventBus.off = function (eventName) {
39 if (eventName) {
40 delete callbacksObj[eventName]
41 } else {
42 callbacksObj = {}
43 }
44 }
45 ​
46 export default eventBus
47 ​

9.9.1.API 手写消息订阅与发布说明

1. PubSub: 包含所有功能的订阅/发布消息的管理者

2. PubSub.subscribe(msg, subscriber): 订阅消息: 指定消息名和订阅者回调函数

3. PubSub.publish(msg, data): 异步发布消息: 指定消息名和数据

4. PubSub.publishSync(msg, data): 同步发布消息: 指定消息名和数据

5. PubSub.unsubscribe(flag): 取消订阅: 根据标识取消某个或某些消息的订阅

9.2. 编码实现

1 /*

2 自定义消息订阅与发布

3 */

4 ​

5 const PubSub = {}
6 /*
7 {
8 add: {
9 token1: callback1,
10 token2: callback2
11 },
12 update: {
13 token3: callback3
14 }
15 }
16 */
17 let callbacksObj = {} // 保存所有回调的容器
18 let id = 0 // 用于生成token的标记
19 ​
20 // 1. 订阅消息
21 PubSub.subscribe = function (msgName, callback) {

22 ​

23 // 确定token 24 const token = 'token_' + ++id 25 // 取出当前消息对应的callbacks 26 const callbacks = callbacksObj[msgName] 27 if (!callbacks) { 28 callbacksObj[msgName] = { 29 [token]: callback 30 } 31 } else { 32 callbacks[token] = callback 33 } 34 // 返回token 35 return token 36 } 37 ​ 38 ​ 39 // 2. 发布异步的消息 40 PubSub.publish = function (msgName, data) { 41 // 取出当前消息对应的callbacks 42 let callbacks = callbacksObj[msgName] 43 // 如果有值 44 if (callbacks) { 45 // callbacks = Object.assign({}, callbacks) 46 // 启动定时器, 异步执行所有的回调函数 47 setTimeout(() => { 48 Object.values(callbacks).forEach(callback => { 49 callback(data) 50 }) 51 }, 0 ) 52 } 53 } 54 ​ 55 // 3. 发布同步的消息 56 PubSub.publishSync = function (msgName, data) { 57 // 取出当前消息对应的callbacks 58 const callbacks = callbacksObj[msgName] 59 // 如果有值 60 if (callbacks) { 61 // 立即同步执行所有的回调函数 62 Object.values(callbacks).forEach(callback => { 63 callback(data) 64 }) 65 } 66 } 67 ​ 68 /* 69 4. 取消消息订阅 70 1 ). 没有传值, flag为undefined 71 2 ). 传入token字符串 72 3 ). msgName字符串 73 */ 74 PubSub.unsubscribe = function (flag) { 75 // 如果flag没有指定或者为null, 取消所有

76 if (flag === undefined) {
77 callbacksObj = {}
78 } else if (typeof flag === 'string') {
79 if (flag.indexOf('token_') === 0 ) { // flag是token
80 // 找到flag对应的callbacks
81 const callbacks = Object.values(callbacksObj).find(callbacks => callbacks.hasOwnProperty(flag
82 // 如果存在, 删除对应的属性
83 if (callbacks) {
84 delete callbacks[flag]
85 }
86 } else { // flag是msgName
87 delete callbacksObj[flag]
88 }
89 ​
90 } else {
91 throw new Error('如果传入参数, 必须是字符串类型')
92 }
93 }
94 ​
95 export default PubSub
96 ​

二、打包自定义工具库

1. 创建工具包项目

1.1. 安装 Node

检查是否已经安装node(node中自带npm)

如果node与npm命令不识别,说明你还没有安装node

安装node, 这里我就不带大家一步一步安装了(比较简单)

方案一: 下载尚硅谷node的视频教程来学习安装

方案二: 百度查找node的安装教程

1.2. 创建项目

1 # 创建一个空的项目文件夹: myTools
2 # 在文件夹下执行命令
3 npm init -y

1.3.下载依赖包

1 npm i webpack webpack-cli

1.4.配置webpack.config.jswebpack

1 const path = require('path');
2 module.exports = {
3 // 模式
4 mode: 'development', // 也可以使用 production
5 // 入口
6 entry: './src/index.js',
7 // 出口
8 output: {
9 // 打包文件夹
10 path: path.resolve(__dirname, 'dist'),
11 // 打包文件
12 filename: 'myTools-utils.js',
13 // 向外暴露的对象的名称
14 library: 'myTools',
15 // 打包生成库可以通过esm/commonjs/reqirejs的语法引入
16 libraryTarget: 'umd',
17 }
18 }

1.5.在入口src/index.jsJS中暴露功能

1 export function test() {
2 document.write('测试自定义包')
3 console.log('test()')
4 }

1.6.配置打包命令package.json

1 "scripts": {
2 "build:watch": "webpack --watch"
3 },

1.7.对项目进行打包

1 npm run build:watch

1.8.测试使用自定义包

test/first.html
1 <body>
2 <script src="../dist/myTools-utils.js"></script>
3 <script>
4 aUtils.test()
5 </script>
6 </body>

2. 发布到 npm 中央仓库

2.1. 完善 package.json

注意:

name: 必须是唯一的名称(在npm在线中央仓库中没有同名的)

main: 必须指定为打包生成的js文件

keywords: 指定一些方便别的程序员搜索到当前库的关键字

1 {

2 "name": "muxing-my-tools",
3 "version": "1.0.2",
4 "description": "",
5 "main": "./dist/myTools-utils.js",
6 "directories": {
7 "test": "test"
8 },
9 "scripts": {
10 "test": "echo \"Error: no test specified\" && exit 1",
11 "build:watch": "webpack --watch"
12 },
13 "keywords": [
14 "utils",
15 "array",
16 "object",
17 "function",
18 "string",
19 "axios",
20 "event-bus",
21 "pub-sub"
22 ],
23 "author": "muxing",
24 "license": "ISC",
25 "dependencies": {
26 "webpack": "^5.28.0",
27 "webpack-cli": "^4.5.0"
28 }
29 }

2.2.npm 配置

npm配置的中央仓库不能是淘宝镜像

发布前必须执行: npm config set registry https://registry.npmjs.org/

不用发布时: npm config set registry http://registry.npm.taobao.org/

查看配置: npm config list

2.3. 注册 npm 中央仓库账号

注册地址: https://www.npmjs.com/

关键信息: 用户名/密码/邮箱(需要验证)

2.4. 添加用户

执行: npm login

登陆npm仓库

依次指定用户名/密码/邮箱

2.5. 发布仓库

执行: npm publish

2.6. 更新代码后再发布

修改项目库的版本号: package.json 中的version 从1.0.0 改为1.0.1, 注意一定要变大

修改代码后重新打包: npm run build

执行发布: npm publish

2.7. 强制删除已发布的库

执行: npm unpublish --force

注意: 必须在 72 小时内, 否则不能再删除

3. 使用自定义工具包

3.1. 下载工具包

1 # 名称是你前面指定的库的名称

2 npm install muxing-my-tools
3 ​
4 # 更新包
5 npm upgrade muxing-my-tools

3.2. 网⻚中引入并使用

1 <script src="../../dist/myTools-utils.js"></script>
2 <script>
3 myTools.test()
4 </script>

3.3. 模块化引入并使用

1 // 使用ESM引入

2 import {test} from 'muxing-my-tools'
3 test()
4 ​
5 // 使用commonjs引入
6 const {test} = require('muxing-my-tools')
7 test()

文档:自定义工具函数库.note 参考: https://zxfjd3g.github.io/atguigu_utils-docs/ 链接:http://note.youdao.com/noteshare?id=e9c8c96e7e0de1ae1d094fbdc3ab687b&sub=C9959E9683BA4C508716EFCF91E713D7