call,apply,bind,new,debounce实现代码

248 阅读2分钟

call

ES3实现方式

args 里面 push 使用下面这种方式,而不直接使用 push( arguments[i] ) 的原因是数组 toString 的时候,除了 string,number 类型的其他类型会有问题

Function.prototype.call = function (context) {
    context = context ? Object(context) : window; 
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;
}

ES6实现方式

Function.prototype.call = function (context) {
  context = context ? Object(context) : window; 
  context.fn = this;

  let args = [...arguments].slice(1);
  let result = context.fn(...args);

  delete context.fn
  return result;
}

apply

ES3实现方式

Function.prototype.apply = function (context, arr) {
    context = context ? Object(context) : window; 
    context.fn = this;

    var result;
    // 判断是否存在第二个参数
    if (!arr) {
        result = context.fn();
    } else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')');
    }

    delete context.fn
    return result;
}

ES6实现方式

Function.prototype.apply = function (context, arr) {
    context = context ? Object(context) : window; 
    context.fn = this;
  
    let result;
    if (!arr) {
        result = context.fn();
    } else {
        result = context.fn(...arr);
    }
      
    delete context.fn
    return result;
}

bind

要注意,继承原函数的原型链

Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};
    
    fNOP.prototype = this.prototype;
    
    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }
    
    fBound.prototype = new fNOP();
    return fBound;
}

new

function create() {
	// 1、获得构造函数,同时删除 arguments 中第一个参数
    Con = [].shift.call(arguments);
	// 2、创建一个空的对象并链接到原型,obj 可以访问构造函数原型中的属性
    var obj = Object.create(Con.prototype);
	// 3、绑定 this 实现继承,obj 可以访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
	// 4、优先返回构造函数返回的对象
	// 这是为了应付构造函数有返回对象的情况,下面给出实例
	return ret instanceof Object ? ret : obj;
};

new 第4点实例解释

1.构造函数返回一个对象

function Car(color, name) {
    this.color = color;
    return {
        name: name
    }
}

var car = new Car("black", "BMW");
car.color;
// undefined

car.name;
// "BMW"

2.构造函数没有return,返回undifine

function Car(color, name) {
    this.color = color;
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

3.构造函数有返回值,但是返回的不是对象

function Car(color, name) {
    this.color = color;
    return "new car";
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

debounce

 function debounce(func, wait, immediate) {
            var timeout;
            return function () {
                let context = this//如果不改变指向,则目标函数的this指向的是window
                let args = arguments//如果不传递参数,事件触发函数的参数无法传递给目标函数
                let result
                clearTimeout(timeout)
                if (immediate) {
                    if (!timeout)
                        result = func.apply(context, args)//只有立即执行才能返回值
                    timeout = setTimeout(function () {
                        timeout = null//为了到达指定时间后,可以再次执行目标函数
                    }, wait);
                }
                else {
                    timeout = setTimeout(function () {
                        func.apply(context, args)
                    }, wait);
                }
                return result
            }
        }

throttled

        function throttle(func, wait) {
            var timeout, context, args, result;
            var previous = 0;

            var throttled = function () {
                var now = +new Date();
                //下次触发 func 剩余的时间
                var remaining = wait - (now - previous);
                context = this;
                args = arguments;
                // 一次循环中,第一次执行的是if的func,后面都是执行else里面的func
                // 如果没有剩余的时间了或者你改了系统时间
                if (remaining <= 0 || remaining > wait) {
                    previous = now;
                    func.apply(context, args);
                } else if (!timeout) {
                    timeout = setTimeout(function () {
                        previous = +new Date();
                        timeout = null;
                        func.apply(context, args)
                    }, remaining);
                }
            };
            return throttled;
        }