防抖和节流

概念 区别 场景
防抖 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。 防抖只执行最后一次,若时间内多次触发则重新计算时间。 input 输入、 多次点击按钮
节流 高频事件触发,但在 n 秒内只会执行一次,如果 n 秒内触发多次函数,只有一次生效 节流是按一定的时间规律执行 拖拽,页面滚动

防抖

防抖

1
2
3
4
5
6
7
8
9
10
//使用闭包
function debounce(fn) {
let timer = null;
return function () {
if (timer) clearTimeout(timer); //当定时器还在的时候,清除定时器,让函数重新开始计时,直到到达指定时间后执行fn函数
timer = setTimeout(() => {
fn.call(this, arguments);
}, 1000);
};
}

节流

节流

1
2
3
4
5
6
7
8
9
10
11
//使用闭包
function throttle(fn) {
let timer = null;
return function () {
if (timer) return; //当指定时间还没结束,则返回不让定时器中的函数执行
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, 1000);
};
}

手写 apply、call 和 bind

函数 接收的参数
apply apply 调用函数的同时,改变函数的 this 指向。第一个参数为想要 this 指向的对象,第二个参数为数组,数组的 value 就是想传入函数的参数
call call 调用函数的同时,改变函数的 this 指向。第一个参数为想要 this 指向的对象,后面的为一个或多个参数,是传入调用函数的参数
bind 返回一个新的函数,新函数的 this 为 bind 的第一个参数,其余参数为新函数的参数

call

1
2
3
4
5
6
7
8
9
10
11
12
13
//手写的call方法卸载Function的原型上,这样每个函数都能访问到
Function.prototype.myCall = function (obj) {
obj = obj || window; //如果第一个参数为空,则让其指向window
const newArg = [];
for (let i = 1; i < arguments.length; i++) {
//参数从1开始,因为第0个参数为this
newArg.push(arguments[i]);
}
obj.p = this; //其中这里的this就是调用myCall的函数,给obj对象添加函数p,p = 调用myCall的函数
let result = obj.p(...newArg); //使用拓展运算符将1以后的参数传给函数p,并用result接收函数p的返回值
delete obj.p; //因为原本的obj中并没有p这个函数,所以运行完后要把它删除
return result; //返回函数执行的结果
};

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.prototype.myApply = function (obj, arg) {
obj = obj || window; //如果obj为空就让它指向window
const newArg = [];
obj.p = this; //这里的this就是调用myApply的函数,给obj添加p这个方法,此时p = 调用myApply的函数
let result = null;
//因为apply接收两个参数,第一个为this,第二个为数组参数,所以判断有无数组参数
if (!arg) {
result = obj.p(); //没有直接调用,并用result接收p的返回值,也就是调用myApply的函数的返回值
} else {
for (let i = 0; i < arg.length; i++) {
newArg.push(arg[i]);
}
result = obj.p(...newArg); //有,则将参数传给p再调用,并用result接收p的返回值,也就是调用myApply的函数的返回值
}
delete obj.p; //结束后删掉obj中的p
return result; //返回返回值,如果没有则返回的是null
};

bind

1
2
3
4
5
6
7
8
Function.prototype.myBind = function () {
const fn = this || window; //这里的this就是指向调用myBind的函数
let arg = Array.prototype.slice.call(arguments); //使用Array.prototype.slice.call可以将arguments类数组转为数组。因为slice能返回一个新数组。这里也可以用Array.from将arguments转为数组。
let _this = arg.shift(); //使用数组的队列方法shift,它能取出数组的第一个值并且返回它。
return function () {
return fn.apply(_this, arg); //返回fn的返回值,如果没有则会返回undefined
};
};
1
2
3
4
5
6
7
8
9
10
11
//Array.prototype.slice将类数组转为数组的关键
Array.prototype.slice = function (start, end) {
var result = new Array();
start = start || 0;
end = end || this.length; // this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
for (var i = start; i < end; i++) {
result.push(this[i]);
}

return result;
};

instanceof

什么是 instanceof

MDN 解释:**instanceof** 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。只要处在同一条原型链上,就会返回 true。

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
//原型继承
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
//Student继承了Person,所以Student()是Person()的实例,Student的prototype的__proto__指向的是Person的prototype,而Person是Object()的实例,Person的prototype的__proto__指向的是Object的prototype
class Student extends Person {
constructor(name, age, hobby) {
super(name, age);
this.hobby = hobby;
}
}
//同时也可以用构造函数的写法实现继承
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, hobby) {
Person.call(this, name, age); //使用call改变Person的this,并传参调用Person
this.hobby = hobby;
}
Student.prototype = new Person(); //使用new Person创建Person的实例对象赋给Student的prototype对象
Student.prototype.constructor = Student; //修正Student原型的constructor,因为此时Student.prototype为Person的实例对象,constructor指向的是Person

//所以instanceof就是用来判断该实例在不在原型链上的
console.log(student instanceof Student); //true
console.log(student instanceof Person); //true
console.log(student instanceof Object); //true
console.log(student instanceof Array); //false
console.log(Student instanceof Function); //true
console.log(Person instanceof Function); //true
console.log(Student instanceof Person); //false

手写 instanceof

1
2
3
4
5
6
7
8
9
10
11
12
//这里的obj1是代表实例对象,obj2代表构造函数
function myInstanceof(obj1, obj2) {
let objProto = obj1.__proto__; //将实例的隐式原型__proto__取出来进入循环
while (true) {
if (objProto === null) return false; //如果objProto为null,则说明原型链到了尽头也没找到对应关系
if (objProto === obj2.prototype) {
//如果objProto与obj2构造函数的原型相等,则说明它们是继承关系
return true;
}
objProto = objProto.__proto__; //如果不相等,则往上寻找,让objProto等于它__proto__的__proto__,继续进行判断
}
}