myprelude

我是练习时间长达两年半的前端练习生,擅长CTRL。

javascript 几个重要的函数

15 Jun 2019 » javascript

作者: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)
    })
-->