# 手写系列:call、apply、bind、函数柯里化、防抖、节流、new、Promise
少废话,show my code
# call
原理都在注释里了
// 不覆盖原生call方法,起个别名叫myCall,接收this上下文context和参数params
Function.prototype.myCall = function (context, ...params) {
// context必须是个对象并且不能为null,默认为window
const _this = typeof context === "object" ? context || window : window;
// 为了避免和原有属性冲突,定义一个Symbol类型的属性
const key = Symbol();
// call方法的目的是改变函数的this指向,函数的this指向它的调用者,也就是说我们的目标是改变函数的调用者。
// 下面的this就是函数本身,给_this增加一个名为[key]的方法指向this,就能用_this来调用this了
_this[key] = this;
const result = _this[key](...params);
// 获取函数执行结果后,删除以上添加的属性
delete _this[key];
return result;
};
# apply
和call的区别在于第二个参数
Function.prototype.myApply = function (context, params) {
return this.myCall(context, ...params);
};
# bind
和call的区别在于不立即执行,返回一个函数即可
Function.prototype.myBind = function (context, ...params) {
const _this = this;
// 返回的函数也能接收参数,但是是放在params后面
return function (...args) {
return _this.myCall(context, ...[...params, ...args]);
};
};
# 函数柯里化
函数柯里化,举例,有如下函数
function test(a, b, c, d, e) {
console.log(a + b + c + d + e);
}
有一个curry转换函数对test函数进行一些转换
function curry(){
// todo
}
const transformTest = curry(test, ...args)
转换之后,原本一次性传过去的参数现在可以分步传参
// 使得
test(1,2,3,4,5)
// 等同于
transformTest(1)(2)(3)(4)(5)
// 或者
transformTest(1, 2)(3)(4, 5)
// 又或者
transformTest(1, 2, 3, 4)(5)
curry函数应该怎么写?
function curry(fn, ...args) {
// 判断参数个数是不是等于原函数参数个数
// 如果是,直接返回调用结果
if ([...args].length >= fn.length) {
return fn(...args);
} else {
// 如果不是,则返回一个函数
return (...params) => {
// 将前面传的全部参数传给curry,回到第一步的if判断,直到参数个数满足要求
return curry(fn, ...args, ...params);
};
}
}
# 防抖
防抖函数最常见的应用场景是搜索框,用户的输入一般是连续的,为了减轻服务器压力,没有必要响应每个字符的搜索结果,如果用户连续输入一段关键字后等待一定时间,没有再次输入,就可以发起搜索请求,这样就少了很多请求。
function debounce(fn, defer) {
let timer;
return () => {
// timer有值说明在defer时间内,多次触发了该函数,清空定时器,重新计时
timer && clearTimeout(timer);
timer = setTimeout(() => {
fn();
timer = null;
}, defer);
}
}
# 节流
节流,让回调函数在一定时间内只执行一次,适用于比较耗费性能的操作,如DOM操作。
function throttle(fn, defer) {
let time = new Date();
return () => {
// 两次时间差大于defer,就执行一次
if(new Date() - time > defer){
fn();
time = new Date();
}
}
}
# new
new操作符可以实例化一个对象,new一个对象的时候,会调用对象的构造函数,实例化出来的结果,它的__proto__指向构造函数的prototype。如果构造函数有返回值,直接返回这个值。
// 因无法实现new那样的操作符,用函数代替
function myNew(Constructor, ...arguments) {
const obj = new Object();
const res = Constructor.call(obj, ...arguments);
obj.__proto__ = Constructor.prototype;
return typeof res === 'object' ? res : obj;
}