JS面试题:手写new、bind、柯里化、防抖节流、深拷贝

63 阅读2分钟

highlight: a11y-dark

面试题:

  1. 手写new
  2. 手写bind
  3. 手写深拷贝
  4. 柯里化
  5. 手写一个防抖节流
  6. 手写一个函数缓存

1. new

function customNew(fn,...args){
    // 1. 创建一个空对象   
    var obj = {};
    // 2. 让构造函数的原型对象 赋值给 实例对象的__proto__
    obj.__proto__ = fn.protoType;
    // 3. 改变构造函数的指针 
    let result = fn.apply(obj,args);
    // 4. 根据返回值判断
    return result instanceof Object ? result : obj;

}

2. bind

Function.prototype.customBind1 = function(...args1){
    const [first, ...rest] = args1;
    let fn = this;
    return function Fn(...args){
        // 正常这里就是直接 _this = context; 但是为了兼容new 的情况, 加了一个 检测函数是不是Fn类型的
        let _this = fn instanceof Fn  ? new Fn(...args) : first;
        return fn.apply(_this, [...args, ...rest]);
    }
}

3. 深拷贝

function deepCopy1(obj, hash= new WeakMap()){
   
    let isOject = Object.prototype.toString.call(obj) == '[object Object]';
    let isArray = Array.isArray(obj);

    if(!isArray && !isOject){
        // 不是对象或者数组: reg data fn 
        // function 的返回值是[object function]
        // reg 的返回值是[object RegExt]
        return obj;
    }
   
    if(hash.has(obj)){
        // 循环引用自身
        return hash.get(obj);
    }
   
    let new_obj =  Array.isArray(obj) ?  new Array(): new Object();
    hash.set(obj,'key');

    for(let key in obj){ // in 遍历包括原型链上的属性
       if(obj.hasOwnProperty(key)){ // 判断只是自身的属性,过滤掉原型链上的属性
          new_obj[key] =  deepCopy1(obj[key],hash);
       }      
    }
    return new_obj;

}

4. 柯里化

function curryFn(fn){
    // 这里不能使用argument 因为闭包会让第二个函数能访问到第一个函数内部的argument变量
    const arity = fn.length;
    function currtied (...args){
        if(args.length >= arity){
            return fn(...args);
        }else{
            return function (...next){
                return currtied(...args, ...next)
            }
        }
    }
    return currtied;
}

function add(a,c,b){
    return a+b+c;
}

5. 防抖节流

 // 非立即执行
    // 点击后延迟4s执行
    // 定时器版本
    function throttle(func,wait){
        let timer;
        return function(){
            const context = this;
            const args = arguments;
            if(!timer){ // timer没有值的时候
                timer = setTimeout(()=>{
                    func.apply(context,args);
                    timer = null;
                },wait)
            }
        }
    }
    // 立即执行,时间戳版本的
    function throttle2(func,wait){
        let oldTime = Date.now();
        return function(){ // 闭包为了能访问到外层的变量
            let newTime = Date.now();
            if(newTime - oldTime >=wait){
                console.log("this 指向", this)
                func.apply(null,arguments);
                oldTime = Date.now();
            }

        }

    }
 // 防抖:n秒后执行,n秒内再次触发的话,从触发时刻开始重新计时
    // 版本1: 立即执行 + 非立即执行
    function debounce(func, wait, immediate) {
        let timer;
        return function () {
            const context = this;
            const args = arguments;
            if (timer) clearTimeout(timer);
            if (immediate) {
                let callNow = !timer; // 把上次的时间存下来
                timer = setTimeout(function () {
                    timer = null;
                }, wait);//  然后在n秒后记忆清空
                if (callNow) { // 如果没有 是第一次的话直接调用原始的目标函数
                    func.apply(context, args)
                }
            } else {
                timer = setTimeout(() => {
                    func.apply(context, args)
                }, wait)
            }
        }
    }

    // 版本2:非立即执行
    function debounce2(func, wait) {
        let timer;
        return function () {
            const context = this;
            const args = arguments;
            clearInterval(timer);
            timer = setTimeout(() => {
                func.apply(this, args)
            }, wait);
        }

    }
  1. 函数缓存
function memoize(func, content){
    let cache = {};
    content = content || this;
    return function(...key){
        if(!cache[key]){
            cache[key] = func.apply(content, key)
        }else{
            return cache[key];
        }
        
    }
}