SOURCE

/**
 * @desc call方法改变this指向的同时,需要将函数执行
*/
Function.prototype.myCall = function(context) {
    // 首先,myCall必须是Function类型才能调用
    if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
        console.error('只有函数才能调用call函数');
        return;
    }
    // 缺省的情况下,context为window。当context为简单类型的时候,强转成Object类型
    context = Object.create(context) || window; 
    // step1: 获取参数
    const args = Array.from(arguments).slice(1);
    // step2: 将 this 作为 context 的fn属性。并且将context.fn执行。这时候该函数内部的this就为context了。
    context.fn = this;    
    const result = context.fn(...args);
    // step3: 将fn属性删除
    delete context.fn;
    // step4: 返回执行结果
    return result;
}

/**
 * @desc apply 和 call 的功能一样。区别仅在于传参的方式不一样,是以数组的形式。
*/
Function.prototype.myApply = function(context) {
    // 首先判断 this 的类型,保证调用 apply 的是函数
    if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
        console.error('只有函数类型才有apply属性');
        return;
    }

    // step0: context 缺省的情况下使用window、为了避免简单类型,用Object.create 强转一下
    context = context ? Object.create(context) : window;
    // step1: 获取参数,这里是apply,直接拿第二个参数即可
    const args = arguments[1];
    // step2: 将this作为context的某个属性,并调用该属性执行
    context.fn = this;
    const result = context.fn(...args);
    // step3: 删除 context 上的fn 方法
    delete context.fn;
    // step4: 返回执行结果
    return result;
}

/**
 * @desc 实现一个bind函数,和apply的区别是:他并不会立即执行
 * 而且支持函数的柯里化
*/
Function.prototype.myBind = function(context) {
    // 首先,判断调用myBind的类型
    if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
        console.error('只有函数类型有myBind属性');
        return;
    }
    // step1: 获取参数
    const args = Array.from(arguments).slice(1);
    // step2: 用self保存this,这个this最终要被执行
    const self = this;
    const returnFn = function() {
        // 支持柯里化,bind之后的函数还可以再传参
        const extArgs = Array.from(arguments);
        // step2: 利用apply方法执行self。
        // step3: 兼容bind后的函数,作为构造函数调用时,this 指向实例
        // step4: bind支持函数柯里化
        self.apply(this instanceof returnFn ? this : context, [...args, ...extArgs]);
    }
    // 保持原型
    // returnFn.prototype = this.prototype; // 这样写的话、在原型上新增方法或者修改属性,绑定函数的原型也会跟着改变
    const fn = function() {};
    fn.prototype = this.prototype;
    returnFn.prototype = new fn();
    return returnFn;
}

/**
 * @Desc 手动实现一个new
*/
function _new(fn, ...args) {
  // step1: 创建一个空对象、并且把对象的原型指向构造函数
  const obj = Object.create(fn.prototype);
  // step2: 借助apply,用生成的对象为this调用构造函数
  const ret = fn.apply(obj, args);
  // step3: 判断构造函数的返回值是否是对象类型,如果是,直接返回。如果不是,则返回新生成的对象
  return ret instanceof Object ? ret : obj;
}

function sayHello(param1, param2, param3) {
    console.log(this.name, param1, param2);
}

const thisObj = {
    name: 'zaoren'
}

// sayHello.call(thisObj, 'day', 'day up');
sayHello.myCall(thisObj, 'day', 'day up');

sayHello.myApply(thisObj, ['day', 'day up']);

const wSayHello = sayHello.myBind(thisObj, 'day')('day up');

function Student(age) {
    this.name = 'zaoren';
    this.age = age;
    return this;
}

const student = _new(Student, '18');
console.log('student', student);
console 命令行工具 X clear

                    
>
console