作者:myprelude@github
原文链接: https://myprelude.github.io
转载请注明出处,保留原文链接和作者信息。
bind实现
// 现代浏览器都有bind函数 我们声明mybind避免覆盖bind
Function.prototype.myBind = function(context,...args){ // context上下文 args是传入的参数
return (...fnargs)=>{ // fnargs 返回函数的参数
return this.apply(context,args.concat(fnargs)) // 通过apply实现作用域绑定并执行函数
}
}
实现函数柯理化
function curry(fn){
return (...args)=>{
if(args.length>=fn.length){
return fn.apply(null,args)
}else{
return curry(fn.bind(null,...args))
}
}
}
fn.length表示函数在定义是指定的参数,在大部分情况下和我们通过arguments获取是差不多,但是如果传递超过定义时的参数时,arguments就大于fn.length;总而言之fn.length是函数的形参个数,而arguments是函数的实参。如下
function fn(a,b,c){
console.log(fn.length)
console.log(arguments.length)
}
fn(1,2,3) // 3 // 3
fn(1,2) // 3 // 2
fn(1,2,3,4) // 3 // 4
在编写ES6箭头函数的时候,我们不能拿到arguments对象
函数组合
在在react高阶组件时,会遇到多级函数嵌套的问题如下:
Parent(Son(Children(props))) // 多个高阶组件嵌套问题
这时我们就需要一个Compose
高阶函数
function componse(...args){
return args.reduce((a,b)=>{
return (...args)=>{
return a(b(...args))
}
})
}
函数节流
对于高频任务或者事件的触发,会影响性能。一般都是对事件触发做一个限制。
function throttle(fn,wait){
let timer = null;
return function(){
let context = this,arg = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context,arguments);
},wait)
}
}
document.addEventListener('mousemove', throttle(() => console.log(new Date().getTime()), 1000), false);
上面代码就会每次等待1000秒后会在控制台答应时间。但是这会出现一个问题,如果用户一直不停的拖动鼠标,发现控制台一直都不会打印时间;在用户看来就是假死的状态。这样体验很不好,所以用户没有提一次触发鼠标移动的时应该马上打印时间,我们队上面函数改造下…
function throttle(fn,wait,immediate){
let timer = null,flag = immediate;
return function(){
let context = this,arg = arguments;
if(flag){
flag = false;
fn.apply(context,arg)
}else{
clearTimeout(timer);
timer = setTimeout(function(){
console.log(context,this)
fn.apply(context,arg)
},wait)
}
}
}
document.addEventListener('mousemove', throttle(() => console.log(new Date().getTime()), 1000, true), false);
现在又有一个问题,用户不停拖动鼠标的话控制台也不会打印日志。其实我们的目的就是让用户降低触发事件的频率。我们还需要在改造下。
function throttle(fn,wait,immediate){
let timer = null,flag = immediate,now = new Date().getTime();
return function(){
let context = this,arg = arguments,last = new Date().getTime();
if(flag){console.log(1111)
flag = false;
fn.apply(context,arg)
}else{
if(last-now>=wait){ // 大于等待时间 就直接执行
fn.apply(context,arg); console.log(2222)
now = last;
}else{ // 如果小于等待时间 放入定时器执行最后一次
clearTimeout(timer); console.log(33333)
timer = setTimeout(function(){
fn.apply(context,arg);
now = last;
},wait)
}
}
}
}
document.addEventListener('mousemove', throttle(() => console.log(new Date().getTime()), 250, true), false);
手动实现Promise
function MyPromise(fn){
this.status = 'pending';
this.successCallback = [];
this.errorCallback = [];
this.data = null;
function reject(val){
setTimeout(()=>{
if(this.status=='pending'){
this.data = val;
this.status = 'reject';
this.errorCallback.forEach((fn)=>{
fn(this.data)
})
}
})
}
function resolve(val){
setTimeout(()=>{
if(this.status=='pending'){
this.data = val;
this.status = 'resolve';
this.successCallback.forEach((fn)=>{
fn(this.data)
})
}
})
}
try{
fn(resolve,reject)
}catch(err){
reject(err)
}
}
MyPromise.prototype.then = function(callback,error){
typeof callback == 'function'?callback:function(){};
typeof error == 'function'?error:function(){};
this.successCallback.push(callback);
this.errorCallback.push(error);
return this
}
var p1 = new MyPromise(function(resolve,reject){
resolve({name:'mypromise'})
})
p1.then((data)=>{
console.log(data)
})
var p2 = new MyPromise(function(resolve,reject){
setTimeout(()=>{
reject({err:'mypromise'})
},1000)
})
p2.then((data)=>{
console.log(data)
},(data)=>{
console.log(data)
})