# 基础手写
# 字符串相关
/*
1. 字符串倒序: reverseString(str) 生成一个倒序的字符串
*/
export function reverseString(str) {
// return str.split('').reverse().join('')
// return [...str].reverse().join('')
return Array.from(str).reverse().join('')
}
/*
2. 字符串是否是回文: palindrome(str) 如果给定的字符串是回文,则返回 true ;否则返回 false
*/
export function palindrome(str) {
return str === reverseString(str)
}
/*
3. 截取字符串: truncate(str, num) 如果字符串的长度超过了num, 截取前面num长度部分, 并以...结束
*/
export function truncate(str, num) {
return str.length > num ? str.slice(0, num) + '...' : str
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 数据类型判断
typeof 可以正确识别:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,但是对于其他的都会认为是 object,比如 Null、Date 等,所以通过 typeof 来判断数据类型会不准确。
function getType(obj){
let type = typeof obj;
if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回
return type;
}
// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); // 注意正则中间有个空格
}
/* 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?思考下 */
getType([]) // "Array" typeof []是object,因此toString返回
getType('123') // "string" typeof 直接返回
getType(window) // "Window" toString返回
getType(null) // "Null"首字母大写,typeof null是object,需toString来判断
getType(undefined) // "undefined" typeof 直接返回
getType() // "undefined" typeof 直接返回
getType(function(){}) // "function" typeof能判断,因此首字母小写
getType(/123/g) //"RegExp" toString返回
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
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
# 类型转换
# 1.隐式类型转换
null == undefined // true 规则2
null == 0 // false 规则2
'' == null // false 规则2
'' == 0 // true 规则4 字符串转隐式转换成Number之后再对比
'123' == 123 // true 规则4 字符串转隐式转换成Number之后再对比
0 == false // true e规则 布尔型隐式转换成Number之后再对比
1 == true // true e规则 布尔型隐式转换成Number之后再对比
var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
// 注意这里a又可以等于1、2、3
console.log(a == 1 && a == 2 && a ==3); //true f规则 Object隐式转换
// 注:但是执行过3遍之后,再重新执行a==3或之前的数字就是false,因为value已经加上去了,这里需要注意一下
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
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
# 2.Object类型转换
var obj = {
value: 1,
valueOf() {
return 2;
},
toString() {
return '3'
},
[Symbol.toPrimitive]() {
return 4
}
}
console.log(obj + 1); // 输出5
// 因为有Symbol.toPrimitive,就优先执行这个;如果Symbol.toPrimitive这段代码删掉,则执行valueOf打印结果为3;如果valueOf也去掉,则调用toString返回'31'(字符串拼接)
// 再看两个特殊的case:
10 + {}
// "10[object Object]",注意:{}会默认调用valueOf是{},不是基础类型继续转换,调用toString,返回结果"[object Object]",于是和10进行'+'运算,按照字符串拼接规则来,参考'+'的规则C
[1,2,undefined,4,5] + 10
// "1,2,,4,510",注意[1,2,undefined,4,5]会默认先调用valueOf结果还是这个数组,不是基础数据类型继续转换,也还是调用toString,返回"1,2,,4,5",然后再和10进行运算,还是按照字符串拼接规则,参考'+'的第3条规则
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
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
# 深拷贝
//浅拷贝:只考虑对象类型。
function shallowCopy(obj) {
if (typeof obj !== "object") return;
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
//简单版深拷贝:只考虑普通对象属性,不考虑内置对象和函数。
function deepClone(obj) {
if (typeof obj !== "object") return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] =
typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date)
return new Date(obj) // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp)
return new RegExp(obj) //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
// 下面是验证代码
let obj = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: { name: '我是一个对象', id: 1 },
arr: [0, 1, 2],
func: function () { console.log('我是一个函数') },
date: new Date(0),
reg: new RegExp('/我是一个正则/ig'),
[Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log('obj', obj)
console.log('cloneObj', cloneObj)
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
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
# 继承
# 1.原型继承
function Parent1() {
this.name = 'parent1';
this.play = [1, 2, 3]
}
function Child1() {
this.type = 'child2';
}
Child1.prototype = new Parent1();
console.log(new Child1());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.构造函数继承 (call)
function Parent1(){
this.name = 'parent1';
}
Parent1.prototype.getName = function () {
return this.name;
}
function Child1(){
Parent1.call(this);
this.type = 'child1'
}
let child = new Child1();
console.log(child); // 没问题
console.log(child.getName()); // 会报错
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
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
# 3.组合继承(原型继承+构造函数继承)
function Parent3 () {
this.name = 'parent3';
this.play = [1, 2, 3];
}
Parent3.prototype.getName = function () {
return this.name;
}
function Child3() {
// 第二次调用 Parent3()
Parent3.call(this);
this.type = 'child3';
}
// 第一次调用 Parent3()
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play); // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'
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
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
# 4.原型式继承(Object.create)
let parent4 = {
name: "parent4",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
let person4 = Object.create(parent4);
person4.name = "tom";
person4.friends.push("jerry");
let person5 = Object.create(parent4);
person5.friends.push("lucy");
console.log(person4.name);
console.log(person4.name === person4.getName());
console.log(person5.name);
console.log(person4.friends);
console.log(person5.friends);
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
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
# 5.寄生式继承
let parent5 = {
name: "parent5",
friends: ["p1", "p2", "p3"],
getName: function() {
return this.name;
}
};
function clone(original) {
let clone = Object.create(original);
clone.getFriends = function() {
return this.friends;
};
return clone;
}
let person5 = clone(parent5);
console.log(person5.getName());
console.log(person5.getFriends());
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
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
# 6.寄生组合式继承(类似extends)
function clone (parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
function Child6() {
Parent6.call(this);
this.friends = 'child5';
}
clone(Parent6, Child6);
Child6.prototype.getFriends = function () {
return this.friends;
}
let person6 = new Child6();
console.log(person6);
console.log(person6.getName());
console.log(person6.getFriends());
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
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
# 7.ES6 entends继承
class Person {
constructor(name) {
this.name = name
}
// 原型方法
// 即 Person.prototype.getName = function() { }
// 下面可以简写为 getName() {...}
getName = function () {
console.log('Person:', this.name)
}
}
class Gamer extends Person {
constructor(name, age) {
// 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
super(name)
this.age = age
}
}
const asuna = new Gamer('Asuna', 20)
asuna.getName() // 成功访问到父类的方法
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
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
# 发布订阅模式/事件总线
class EventEmitter {
constructor() {
// handlers是一个map,用于存储事件与回调之间的对应关系
this.handlers = {}
}
// on方法用于安装事件监听器,它接受目标事件名和回调函数作为参数
on(eventName, cb) {
// 先检查一下目标事件名有没有对应的监听函数队列
if (!this.handlers[eventName]) {
// 如果没有,那么首先初始化一个监听函数队列
this.handlers[eventName] = []
}
// 把回调函数推入目标事件的监听函数队列里去
this.handlers[eventName].push(cb)
}
// emit方法用于触发目标事件,它接受事件名和监听函数入参作为参数
emit(eventName, ...args) {
// 检查目标事件是否有监听函数队列
if (this.handlers[eventName]) {
// 这里需要对 this.handlers[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
const handlers = this.handlers[eventName].slice()
// 如果有,则逐个调用队列里的回调函数
handlers.forEach((callback) => {
callback(...args)
})
}
}
// 移除某个事件回调队列里的指定回调函数
off(eventName, cb) {
const callbacks = this.handlers[eventName]
const index = callbacks.indexOf(cb)
if (index !== -1) {
callbacks.splice(index, 1)
}
}
// 为事件注册单次监听器
once(eventName, cb) {
// 对回调函数进行包装,使其执行完毕自动被移除
const wrapper = (...args) => {
cb(...args)
this.off(eventName, wrapper)
}
this.on(eventName, wrapper)
}
}
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
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
# call/apply/bind
# 1.call
Function.prototype.myCall = function (obj, ...arg) {
const context = obj || window;
const fn = Symbol(); // 保证唯一
context[fn] = this;
const res = context[fn](...arg);
delete context[fn];
return res;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2.apply
Function.prototype.myApply = function (obj, arg) {
const context = obj || window;
const fn = Symbol(); // 保证唯一
context[fn] = this;
const res = context[fn](...arg);
delete context[fn];
return res;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 3.bind
Function.prototype.bind = function (obj, ...args) {
const context = obj || window;
const fn = Symbol()
context[fn] = this;
let that = this;
const res = function(...others){
return that.apply(
this instanceof that ? this : context, //new时, this指向res的实例,res继承自that
[...args,...others]
)
}
// 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
res.prototype = Object.create(that.prototype)
return res
}
// 使用
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return this.x + ',' + this.y;
}
let YPoint = Point.bind(null, 1);
let axiosPoint = new YPoint(2);
console.log(axiosPoint.toString()) // '1,2'
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
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
https://www.jianshu.com/p/b540e1e17f54
# 数组基本方法
- map(): 返回一个由回调函数的返回值组成的新数组
- filter(): 将所有在过滤函数中返回true的数组元素放进一个新数组中并返回
- find(): 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回undefined。
- findIndex(): 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回-1。
- every(): 如果数组中的每个元素都满足测试函数,则返回true,否则返回false。
- some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。
- reduce(): 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值
/*
实现map()
*/
export function map (array, callback) {
const arr = []
for (let i = 0; i < array.length; i++) {
arr.push(callback(array[i], i))
}
return arr
}
/*
实现filter()
*/
export function filter(array, callback) {
const arr = []
for (let i = 0; i < array.length; i++) {
if (callback(array[i], i)) {
arr.push(array[i])
}
}
return arr
}
/*
实现find()
*/
export function find (array, callback) {
for (let i = 0; i < array.length; i++) {
if (callback(array[i], i)) {
return array[i]
}
}
return undefined
}
/*
实现findIndex()
*/
export function findIndex (array, callback) {
for (let i = 0; i < array.length; i++) {
if (callback(array[i], i)) {
return i
}
}
return -1
}
实现every()
*/
export function every (array, callback) {
for (let i = 0; i < array.length; i++) {
if (!callback(array[i], i)) { // 只有一个结果为false, 直接返回false
return false
}
}
return true
}
/*
实现some()
*/
export function some (array, callback) {
for (let i = 0; i < array.length; i++) {
if (callback(array[i], i)) { // 只有一个结果为true, 直接返回true
return true
}
}
return false
}
/*
reduce()
*/
function reduce(array, callback, initValue) {
let acc = initValue;
let startAtIndex = 0;
if (initValue === undefined) {
acc = array[0];
startAtIndex = 1;
}
for (let i = startAtIndex; i < array.length; i++) {
acc = callback(acc, array[i], i, array)
}
return acc;
}
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
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
# 数组去重
根据当前数组产生一个去除重复元素后的新数组
如: [2, 3, 2, 7, 6, 7] ==> [2, 3, 7, 6]
方法1: 利用forEach()和indexOf() ○说明: 本质是双重遍历, 效率差些
方法2: 利用forEach() + 对象容器 ○说明: 只需一重遍历, 效率高些
方法3: 利用ES6语法: from + Set 或者 ... + Set ○说明: 编码简洁
/*
方法1: 利用forEach()和indexOf()
说明: 本质是双重遍历, 效率差些
*/
export function unique1 (array) {
const arr = []
array.forEach(item => {
if (arr.indexOf(item) === -1) {
arr.push(item)
}
})
return arr
}
/*
方法2: 利用forEach() + 对象容器
说明: 只需一重遍历, 效率高些
*/
export function unique2 (array) {
const arr = []
const obj = {}
array.forEach(item => {
if (!obj.hasOwnProperty(item)) {
obj[item] = true
arr.push(item)
}
})
return arr
}
/*
方法3: 利用ES6语法
1). from + Set
2). ... + Set
说明: 编码简洁
*/
export function unique3 (array) {
// return Array.from(new Set(array))
return [...new Set(array)]
}
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
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
# 数组扁平化
- 语法: flatten(array)
- 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中
- 如: [1, [3, [2, 4]]] ==> [1, 3, 2, 4]
ES2019 方法 flat(depth) 深度,默认值为 1。
- flat() 方法会移除数组中的空项
- 使用 Infinity,可展开任意深度的嵌套数组
var arr = [1, 2, [3, 4]];
// 展开一层数组
arr.flat();
// 等效于
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]
// 使用扩展运算符 ...
const flattened = arr => [].concat(...arr);
// 方法1:ES6中 Array.prototype.flat()
console.log(arr.flat(Infinity))
// 方法2:迭代的思路
function flat(arr, depth = 1) {
while (arr.some((item) => Array.isArray(item)) && depth > 0) {
arr = [].concat(...arr);
depth--;
}
return arr;
}
// 方法3:递归
function flat(arr, depth = 1) {
let result = [];
function _loop(arr) {
arr.forEach((item) => {
if (Array.isArray(item) && depth > 0) {
_loop(item, depth--);
} else {
result.push(item);
}
});
}
_loop(arr);
return result;
}
// 方法4:reduce + 递归
function flat(arr, depth = 1) {
return depth > 0
? arr.reduce(
(acc, val) =>
acc.concat(Array.isArray(val) ? flat(val, depth - 1) : val),
[]
)
: arr.slice();
}
// toString
function flat (arr) {
return arr.toString().split(',').map(item => Number(item)) //转成字符串会只有一个逗号相隔
}
console.log(flat(arr))
// stringify
function flat (arr) {
let str = JSON.stringify(arr)
str = str.replace(/\[|\]/g, '') //去掉中括号
return str.split(',').map(item => Number(item)))
}
console.log(flat(arr))
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
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
# 节流/防抖
# 1.节流throttle
//🔥时间戳,缺点:第一次会执行,最后少一次
function throttle1(fn, timeout) {
let last = 0;
return function () {
let now = Date.now();
if (now - last >= timeout) {
last = now;
fn.apply(this, arguments); // this是dom,args 第一个是event事件
}
};
}
//🔥定时器,缺点:最后一次也会等一个时间间隔
function throttle2(fn, timeout) {
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, timeout);
}
};
}
// 结合
function throttle3(fn, timeout) {
let timer = null;
let last = 0;
return function () {
let now = Date.now();
let remaining = timeout - (now - last);
if (remaining <= 0) {
fn.apply(this, arguments);
last = now;
} else if(!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
last = Date.now();
timer = null
}, remaining);
}
};
}
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
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
# 2.防抖debounce
//🔥初级版本 第一次执行延时
function debounce1(fn,wait){
let timer = null;
return function(){
if(timer){
clearTimeout(timer);
}
timer=setTimeout(()=>{
fn.apply(this, arguments);
},wait);
}
}
// 🔥反过来 第一次执行
function debounce2(fn, timeout) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
if (!timer) {
fn.apply(this, arguments);
}
timer = setTimeout(() => {
timer = null;
}, timeout);
};
}
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
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
# 原型相关
# 1.new
- 语法: newInstance(Fn, ...args)
- 功能: 创建 Fn 构造函数的实例对象
function myNew(Fn, ...args) {
const obj = {};
// = Object.setPrototypeOf(obj, Fn.prototype);
obj.__proto__ = Fn.prototype;
const result = Fn.apply(obj, args)
// 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
return (typeof result === 'object' && result != null) ? result : obj
// 最后一句也可以 return result instanceof Object ? result : obj
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2.instanceof
- 语法: myInstanceOf(obj, Type)
- 功能: 判断 ob j是否是 Type 类型的实例
- 实现: Type 的原型对象是否是 obj 的原型链上的某个对象, 如果是返回 true, 否则返回 false
function myInstanceof(left, right) {
// 这里先用typeof来判断基础数据类型,如果是,直接返回false
if(typeof left !== 'object' || left === null) return false;
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left);
while(true) { //循环往下寻找,直到找到相同的原型对象
if(proto === null) return false;
if(proto === right.prototype) return true;//找到相同原型对象,返回true
proto = Object.getPrototypeof(proto);
}
}
// 验证一下自己实现的myInstanceof是否OK
console.log(myInstanceof(new Number(123), Number)); // true
console.log(myInstanceof(123, Number)); // false
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
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
# 3.Object.create
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
Object.create = function (proto) {
function F() { }
F.prototype = proto;
return new F();
};
1
2
3
4
5
2
3
4
5
使用:
// 子类继承父类的原型属性
Worker.prototype = Object.create(Person.prototype)
Worker.prototype.constructor = Worker
1
2
3
2
3
# 异步控制并发数
function limitRequest(urls = [], limit = 3) {
return new Promise((resolve, reject) => {
const len = urls.length
let count = 0
// Start limit tasks simultaneously
while (limit > 0) {
start()
limit -= 1
}
function start() {
const url = urls.shift() // Take the first task from the array
if (url) {
axios.post(url).finally(() => {
if (count == len - 1) {
// The last task is completed
resolve()
} else {
// After completion, start the next task
count++
start()
}
})
}
}
})
}
// test
limitRequest(['http://xxa','http://xxb','http://xxc','http://xxd','http://xxe'])
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
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
# 获取url参数
// URLSearchParams 方法
// Create an instance of URLSearchParams
const urlSearchParams = new URLSearchParams(window.location.search);
// Convert the list of key-value pairs into an object
const params = Object.fromEntries(urlSearchParams.entries());
// split 方法
function getParams(url) {
const res = {}
if (url.includes('?')) {
const str = url.split('?')[1]
const arr = str.split('&')
arr.forEach(item => {
const key = item.split('=')[0]
const val = item.split('=')[1]
res[key] = decodeURIComponent(val) // decode
})
}
return res
}
// test
const user = getParams('http://www.baidu.com?user=%E9%98%BF%E9%A3%9E&age=16')
console.log(user) // {user:'abor', age: '16'}
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
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
# 函数柯里化
function sumFn(a,b,c){return a+ b + c};
let sum = curry(sumFn);
sum(2)(3)(5)//10
sum(2,3)(5)//10
function curry(fn,...args){
let fnLen = fn.length,
argsLen = args.length;
//对比函数的参数和当前传入参数
//若参数不够就继续递归返回curry
//若参数够就调用函数返回相应的值
if(fnLen > argsLen){
return function(...arg2s){
return curry(fn,...args,...arg2s)
}
}else{
return fn(...args)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
← 错误处理