SOURCE

// 手动实现一个 myCall 函数
Function.prototype.myCall = function(context) {
    // 判断调用者的类型
    if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
        console.error('只有函数有Call方法');
        return;
    }
    // 缺省的情况用window
    context = context ? Object.create(context) : window;
    const args = Array.from(arguments).slice(1);
    context.fn = this;
    const result = context.fn(...args);
    delete context.fn;
    return result;
}

// 手动实现一个 apply
Function.prototype.myApply = function(context) {
    // 判断调用者的类型
    if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
        console.error('只有函数有Call方法');
        return;
    }
    context = context ? Object.create(context) : window;
    const args = arguments[1];
    context.fn = this;
    const result = context.fn(...args);
    delete context.fn;
    return result;
}

// 手写Bind
Function.prototype.myBind = function(context) {
    // 判断调用者的类型
    if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
        console.error('只有函数有Call方法');
        return;
    }
    context = context ? Object.create(context) : window;
    const args = Array.from(arguments).slice(1);
    const self = this;
    // 返回一个函数
    const returnFn = function() {
        const extArgs = Array.from(arguments); 
        // 兼容一下通过new的构造函数进来的情况       
        self.apply(this instanceof returnFn ? returnFn : this, [...args, ...extArgs]);
    }
    // 保持原型,防止原型丢失。
    const fn = function() {};
    fn.prototype = this.prototype;
    returnFn.prototype = new fn(); // 用一个中间函数方式原型链丢失
    return returnFn
}

// 手写new关键字
function _new(fn, ...args) {
    // 1. 创建对象,并且把prototype指向fn
    const obj = Object.create(fn.prototype);
    // 2. 调用构造函数
    const ret = fn.apply(obj, args);
    // 3. 构造函数构造出来的如果是对象,直接返回,否则返回执行fn.apply之前的obj
    return obj instanceof Object ? ret : obj;
}

// 防抖
function debounce(fn, delay) {
    let timer;
    return function(...args) {
        // 如果有定时器,清除,重新计算时间
        if (timer) {
            clearTimeout(timer);
        }
        setTimeout(() => {
            fn.apply()
        }, delay)
    }
}

// 节流
function throttle(fn, wait) {
    let canRun = true;
    return function(...args) {
        if (!canRun) {
            return;
        }
        // 继续上锁
        canRun = false;
        setTimeout(() => {
            fn.apply(this, ...args);
            canRun = true;
        },wait)
    }
}

function cloneDeep(target, cache = new WeakMap()) {
    // 判断 null 和 非 Object场景
    if (target === null || typeof target !== 'object') {
        return target;
    }
    // 处理 Date、RegExp场景
    if (target instanceof Date) {
        return new Date(target);
    }
    if (target instanceof RegExp) {
        return new RegExp(target);
    }

    // 处理对象,首先需要防止循环引用 a.b = a;
    if (cache.has(target)) {
        return cache.get(target);
    }
    // 重新创建对象, 保持原型链
    const obj = new target.contructor();
    // 缓存一下对象
    cache.set(target, obj);
    Object.keys(key => {
        // 递归调用一下
        obj[key] = cloneDeep(target[key], cache);
    });
    return 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);


// 测试
const obj = { name: 'Jack', address: { x: 100, y: 200 } }
obj.a = obj // 循环引用
const newObj = cloneDeep(obj)
console.log(newObj.address === obj.address) // false
console 命令行工具 X clear

                    
>
console