call()&&apply()&&bind()

1.call()

1
2
3
4
5
6
7
8
9
10
11
12
13
function call(Fn, obj, ...arg) {
if (obj === undefined || obj === null) {
obj = window;
}
// 为obj添加临时方法,this自动指向obj
obj.temp = Fn;
// 调用temp方法
let result = obj.temp(...arg);
// 删除临时方法
delete obj.temp;
// 返回执行结果
return result;
}

2.apply()

1
2
3
4
5
6
7
8
9
10
11
12
13
function call(Fn, obj, arg) {
if (obj === undefined || obj === null) {
obj = window;
}
// 为obj添加临时方法,this自动指向obj
obj.temp = Fn;
// 调用temp方法
let result = obj.temp(...arg);
// 删除临时方法
delete obj.temp;
// 返回执行结果
return result;
}

3.bind()

1
2
3
4
5
6
7
function bind(Fn, obj, ...arg) {
// 返回一个新函数
return function (...arg2) {
// 通过call调用原函数, 并指定this为obj, 实参为args与args2
return call(Fn, obj, ...arg, ...arg2)
}
}

函数节流与防抖

1.函数节流throttle()

1
2
3
4
5
6
7
8
9
10
function throttle(callback, wait) {
let start = 0;
return function (e) {
let now = Date.now();
if (now - start > wait) {
callback.call(this, e)
start = now;
}
}
}

2.函数防抖debounce()

1
2
3
4
5
6
7
8
9
10
11
12
function debounce(callback,time) {
let timeId = null;
return function(e) {
if(timeId !== null){
clearTimeout(timeId);
}
timeId = setTimeout(function () {
callback.call(this,e);
timeId = null;
},time)
}
}

数组相关

1.map()

返回一个由回调函数的返回值组成的新数组

1
2
3
4
5
6
7
8
9
function map(arr,callback) {
// 声明空数组
let result = [];
for (let i = 0;i<arr.length;i++){
// 将callback的执行结果添加到数组中
result.push(callback(arr[i],i))
}
return result;
}

2.reduce()

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

1
2
3
4
5
6
7
8
9
function reduce(arr, callback, initValue) {
let result = initValue;
// 执行callback
for (let i = 0; i < arr.length; i++) {
// 将回调函数的执行结果赋值给result
result = callback(result, arr[i]);
}
return result;
}

3.filter()

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

1
2
3
4
5
6
7
8
9
10
11
function filter(arr, callback) {
// 声明空数组
let result = [];
for (let i = 0; i < arr.length; i++) {
// 判断回调函数的返回值
if (callback(arr[i], i)) {
result.push(arr[i])
}
}
return result;
}

4.find()

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

1
2
3
4
5
6
7
8
9
10
11
12
function find(arr, callback) {
// 遍历数组
for (let i = 0; i < arr.length; i++) {
// 判断执行结果
if (callback(arr[i], i)) {
// 返回当前正在遍历的元素
return arr[i];
}
}
// 没有遇到满足条件的元素,返回undefined
return undefined;
}

5.findIndex()

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

1
2
3
4
5
6
7
8
9
10
11
function findIndex(arr, callback) {
// 遍历数组
for (let i = 0; i < arr.length; i++) {
// 判断回调函数返回值
if (callback(arr[i], i)) {
return i
}
}
// 找不到返回-1
return -1
}

6.every()

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

1
2
3
4
5
6
7
8
9
function every(arr, callback) {
for (let i = 0; i < arr.length; i++) {
// 只有一个结果为false, 直接返回false
if (!callback(arr[i], i)) {
return false
}
}
return true
}

7.some()

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

1
2
3
4
5
6
7
8
9
function some(arr, callback) {
for (let i = 0; i < arr.length; i++) {
// 只有一个结果为true, 直接返回true
if (callback(arr[i], i)) {
return true
}
}
return false
}

8.数组去重

8.1.利用forEach()和indexOf()

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

1
2
3
4
5
6
7
8
9
function unique1 (arr) {
const result = []
arr.forEach(item => {
if (result.indexOf(item)===-1) {
result.push(item)
}
})
return result
}

8.2.利用forEach() + 对象容器

只需一重遍历, 效率高些

1
2
3
4
5
6
7
8
9
10
11
function unique2 (arr) {
const result = []
const obj = {}
arr.forEach(item => {
if (!obj.hasOwnProperty(item)) {
obj[item] = true
result.push(item)
}
})
return result
}

8.3.利用ES6语法: from + Set 或者 … + Set

1
2
3
function unique3 (arr) {
return [...new Set(arr)]
}

8.4.不使用多余空间

1
2
3
4
5
6
7
8
9
10
11
function unique4(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}

9.数组合并

1
2
3
4
5
6
7
8
9
10
11
function concat(arr,...value) {
const array = [...arr]
value.forEach(item => {
if(Array.isArray(item)){
array.push(...item)
}else{
array.push(item)
}
})
return array;
}

10.数组切片

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
function slice(arr, start, end) {
// 如果当前数组为[],返回[]
if (arr.length === 0) {
return []
}
// 如果开始位置大雨数组长度,返回[]
start = start || 0
if (start >= arr.length) {
return []
}
// 如果结束位置大于数组长度,end更新为数组长度
end = end || arr.length
if (end > arr.length) {
end = arr.length;
}
// 如果结束位置小于等于开始位置,返回[]
if (end <= start) {
return []
}
let result = [];
// 取出下标在[start,end)之间的元素,放入result中
for (let i = start; i < end; i++) {
result.push(arr[i])
}
return result;
}

11.数组扁平化

##11.1.递归 + concat()

1
2
3
4
5
6
7
8
9
10
11
12
13
function flatten1(arr) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
// 如果是数组,遍历
result = result.concat(flatten1(arr[i]))
} else {
// 不是数组,合并入result中
result = result.concat(arr[i])
}
}
return result;
}

11.2. … + some() + concat()

1
2
3
4
5
6
7
function flatten2(arr) {
let result = [].concat(...arr)
// 如果数组中存在第二级,展开合并
while (result.some(item => Array.isArray(item))) {
result = [].concat(...result)
}
}

12.数组分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function chunk(arr, size = 1) {
let result = [];
let temp = [];
// 遍历数组
arr.forEach(item => {
if (temp.length === 0) {
// 如果临时数组为空,压入结果数组中
result.push(temp)
}
// 将元素压入临时数组中
temp.push(item)
if (temp.length === size) {
// 长度满足后,清空temp
temp = []
}
})
return result;
}

13.数组求差集

1
2
3
4
5
6
7
8
9
10
11
function difference(arr1,arr2=[]) {
// 如果arr1为[],返回[]
if(arr1.length === 0){
return [];
}
// 如果arr2为[],通过slice深拷贝arr1返回
if(arr2.length === 0){
return arr1.slice();
}
return arr1.filter(item => !arr2.includes(item))
}

14.删除数组中部分元素

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function pull(arr, ...values) {
if (arr.length === 0 || values.length === 0) {
return [];
}
let result = [];
for (let i = 0; i < arr.length; i++) {
if (values.indexOf(arr[i]) !== -1) {
arr.splice(i,1);
result.push(arr[i]);
i--;
}
}
return result;
}

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

1
2
3
4
5
6
function pullAll(arr, values) {
if (!values || !Array.isArray(values)) {
return [];
}
return pull(arr, ...values)
}

16.获取数组部分元素

16.1.drop(array, count)

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

1
2
3
4
5
6
function drop(arr, count = 1) {
if (arr.length === 0 || count >= arr.length) {
return [];
}
return arr.filter((item, i) => i >= count)
}

16.2.dropRight(array, count)

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

1
2
3
4
5
6
function dropRight(arr, count = 1) {
if (arr.length === 0 || count >= arr.length) {
return [];
}
return arr.filter((item, i) => i < arr.length - count)
}

对象相关

new

根据构造函数实例化对象

1
2
3
4
5
6
7
8
9
function New(Fn, ...args) {
// 1.创建一个新对象
const obj = {};
// 2.修改函数内部的this指向新对象,并执行
const result = Fn.call(obj, ...args)
obj.__proto__ = Fn.prototype;
// 3.返回新对象
return result instanceof Object ? result : obj;
}

instanceOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function InstanceOf(obj,type) {
// 获取obj的隐式原型对象
let protoObj = obj.__proto__;
// 遍历原型链
while (protoObj){
// 检测obj的原型对象和type的原型对象是否相等
if (protoObj === type.__proto__){
return true;
}
// 不相等,向上查找原型对象
protoObj = protoObj.__proto__;
}
return false;
}

对象合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function mergeObj(...objs) {
let result = {};
// 遍历对象
objs.forEach(obj => {
// 获取对象的所有key并遍历
Object.keys(obj).forEach(key => {
if (result.hasOwnProperty(key)) {
// 存在,则合并属性
result[key] = [].concat(result[key], obj[key])
} else {
// 如果结果对象中不存在,则添加属性
result[key] = obj[key];
}
})
})
return result;
}

对象/数组拷贝

浅拷贝

1)…

1
2
3
4
5
6
7
8
9
10
11
12
function clone1(target) {
// 判断是否为对象
if (target !== null && typeof target === 'Object') {
if (Array.isArray(target)) {
return [...target]
} else {
return {...target}
}
} else {
return target;
}
}

2)for…in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function clone2(target) {
// 判断是否为对象
if (target !== null && typeof target === 'Object') {
const result = Array.isArray(target) ? [] : {};
// 遍历target数据
for (let key in target) {
// 判断target中是否包含该属性
if (target.hasOwnProperty(key)) {
// 将该属性写入result中
result[key] = target[key]
}
}
return result;
} else {
return target;
}
}

深拷贝

例子

1
2
3
4
5
6
7
let obj = {
a: 1, b: ['e', 'f'], c: {d: 20}, g: function () {
}
}
obj.b.push(obj.c)
obj.c.d = obj.b
deepClone3(obj)

1)’JSON.parse(JSON.stringify())’

缺点:1.无法克隆函数属性;2.无法解决循环引用

1
2
3
function deepClone1(target) {
return JSON.parse(JSON.stringify(target));
}

2)递归拷贝,解决无法克隆函数属性

1
2
3
4
5
6
7
8
9
10
11
12
13
function deepClone2(target) {
if (target !== null && typeof target === 'object') {
const result = Array.isArray(target) ? [] : {};
for (let key in target) {
if (target.hasOwnProperty(key)) {
result[key] = deepClone2(target[key])
}
}
return result;
} else {
return target;
}
}

3)使用缓存容器,解决无法循环应用问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function deepClone3(target, map = new Map()) {
if (target !== null && typeof target === 'object') {
// 判断传入的map中存不存在对象的克隆
let result = map.get(target)
if (result) {
return target;
}
result = Array.isArray(target) ? [] : {};
map.set(target, result)
for (let key in target) {
if (target.hasOwnProperty(key)) {
result[key] = deepClone3(target[key], map)
}
}
return result;
} else {
return target;
}
}

4)性能优化

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function deepClone4(target, map = new Map()) {
if (target !== null && target === "object") {
let result = map.get(target);
if (result) {
return result;
}
if (Array.isArray(target)) {
result = [];
map.set(target, result);
target.forEach((item, i) => {
result[i] = deepClone4(item[i], map)
})
} else {
result = {};
map.set(target, result);
Object.keys(target).forEach(key => {
result[key] = deepClone4(target[key], map)
})
}
return result;
}else{
return target;
}
}

字符串相关

倒序

1
2
3
4
5
function reverseString(str) {
// return str.split('').reverse().join('')
// return [...str].reverse().join('')
return Array.from(str).reverse().join('')
}

检测是否回文

1
2
3
function palindrome(str) {
return str === reverseString(str)
}

截取字符串,以…隐藏多余字符串

1
2
3
function truncate(str, size) {
return str.length > size ? str.slice(0, size) + '...' : str
}

手写DOM事件监听

事件捕获与事件冒泡

事件捕获 事件冒泡

事件委托函数

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

好处:

  • 减少内存占用(事件监听回调从n变为
  • 动态添加的内部元素也能响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function addEventListener(el, type, Fn, selector) {
if (typeof el === 'string') {
el = document.querySelector(el);
}
if (!selector) {
// 如果没有selector,普通事件绑定
el.addEventListener(type, Fn);
} else {
// 否则是代委托的事件绑定
el.addEventListener(type, function (e) {
// 获取真正发生事件的目标元素
const target = e.target;
// 判断目标元素是否与selector相同
if (target.matches(selector)) {
// 调用处理事件的回调函数Fn,绑定当前this,参数为e
Fn.call(target, e)
}
})
}
}

事件总线

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
const eventBus = {callbacks: {}}

// 注册事件监听
eventBus.on = function (type, callback) {
const callbacks = this.callbacks[type];
if (!callbacks) {
this.callbacks[type] = [callback];
} else {
callbacks.push(callback);
}
}

// 触发事件监听
eventBus.emit = function (type, data) {
const callbacks = this.callbacks[type];
if (callbacks && callbacks.length > 0) {
this.callbacks[type].forEach(cb => {
cb(data)
})
}
}

// 移除事件监听
eventBus.off = function (type) {
if (!type) {
this.callbacks = {};
} else {
delete this.callbacks[type]
}
}

消息订阅与发布

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
const sub = {
callbacks: {},
id: 0
}

// 订阅
sub.subscribe = function (type, callback) {
const token = "token_" + this.id;
const callbacks = this.callbacks[type];
if (callbacks) {
callbacks[token] = callback
} else {
this.callbacks[type] = {
[token]: callback
}
console.log(this.callbacks)
}
this.id++;
return token;
}

// 发布
sub.publish = function (type, data) {
const callbacks = this.callbacks[type];
if (callbacks) {
Object.values(this.callbacks).forEach(cb => {
cb(data)
})
}
}

// 取消
sub.unsubscribe = function (flag) {
if (flag === undefined) {
this.callbacks = {};
} else if (typeof flag === "string") {
if (flag.indexOf("token_") === 0) {
const callbacks = Object.values(this.callbacks).find(callback => callback.hasOwnProperty(flag));
if (callbacks) {
delete callbacks[flag]
}
} else {
delete this.callbacks[flag]
}
} else {
throw new Error("传入参数错误")
}
}

最后更新: 2021年09月23日 14:20