手撕常见JS面试题

本文深入探讨JavaScript面试中的常见问题,包括高阶函数实现的AOP、new操作背后的原理、Object.create的兼容实现、柯理化函数的多种应用,以及函数节流、防抖动等实用技巧。同时涵盖深度拷贝、bind实现、Promise管理、字符串回文判断、数组操作和算法挑战等核心知识点。
摘要由CSDN通过智能技术生成

高阶函数实现AOP(面向切面编程)

    Function.prototype.before = function (beforefn) {
   
        let _self = this; // 缓存原函数的引用
        returnfunction () {
    // 代理函数
            beforefn.apply(this, arguments); // 执行前置函数
            return _self.apply(this, arguments); // 执行原函数
        }
    }

    Function.prototype.after = function (afterfn) {
   
        let _self = this;
        returnfunction () {
   
            letset = _self.apply(this, arguments);
            afterfn.apply(this, arguments);
            returnset;
        }
    }

    let func = () => console.log('func');
    func = func.before(() => {
   
        console.log('===before===');
    }).after(() => {
   
        console.log('===after===');
    });

    func();

输出结果:

===before===
func
===after===   

当我们 new 一个类的时候 都发生了什么

/** * new2 new关键字的代码实现演示 * @param {function} func 被new的类 (构造函数) */
function new2(func) {
   
    // 创建了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象 
    let o = Object.create(func.prototype); 
    // (在构造函数中this指向当前实例)让这个类作为普通函数值行 并且里面this为实例对象 
    let k = func.call(o);
    // 最后再将实例对象返回 如果你在类中显示指定返回值k,
    // 注意如果返回的是引用类型则将默认返回的实例对象o替代掉
    return typeof k === 'object' ? k : o;
}

// 实验
functionM() {
    // 即将被new的类
    this.name = 'liwenli';
}

let m = new2(M); // 等价于 new M 这里只是模拟
console.log(m instanceof M); // instanceof 检测实例
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);

Object.create 兼容实现

let obj1 = {
   id: 1};
        Object._create = (o) => {
   
            let Fn = function() {
   }; // 临时的构造函数
            Fn.prototype = o;
            return new Fn;
        }

        let obj2 = Object._create(obj1);
        console.log(obj2.__proto__ === obj1); // true
        console.log(obj2.id); // 1

        // 原生的Object.create
        let obj3 = Object.create(obj1);
        console.log(obj3.__proto__ === obj1); // true
        console.log(obj3.id); // 1

currying 函数柯理化

curry 柯理化的实现(递归调用 + valueOf)

知识点:valueOf 浏览器环境下 当我们以log(fn)这种形式取值时,会隐式调用fn自身的valueOf 所以得到的是valueOf的返回值

functionfn() {
   };
fn.valueOf = () => console.log('valueof');
console.log(fn); // valueof


const mul = x => {
   
    const result = y => mul(x * y); // 递归调用mul
    result.valueOf = () => x;
    return result;
}
console.log(mul(2)(3)); // 6

// 在上面mul每执行一次,就会返回一个valueOf被改写后的新函数result 并且result执行会在里面调用mul(x * y)
// 在result函数的valueOf里保存着 由上一次x * y 传进来的结果x, 也就是上一次x*y 会作为这一次的输出 依然叫x

// 第一次mul(2) 此时 x为2  return result
result 为 result = y => mul(2 * y); 
// 第二次 mul(2)(3) 等价于 第一个mul返回的result(3), result执行 => mul(2 * 3) 再次调用mul 将2*3 = 6 的结果作为mul参数
// 最后mul(6) x = 6 在返回一个新函数result 此时result的valueOf = () => 6

// log(mul(2)(3)) 相当于log的最后返回的result 隐式调用valueOf 返回 6

curry 将多参数函数转换为接收单一参数的函数

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

function curry(fe) {
   
    let args = []; // 参数集合
    let len = args.length;
    returnfunctionbar() {
   
        args = [...args, ...arguments]; // 收集参数
        if (args.length >= fe.length) {
   
            return fe.apply(this, args);
        }
        return bar;
    }
}

console.log(curry(fe)(1)(2)(3)); // 6

currying 部分求值

    // currying 函数柯理化
    let currying = function(fn) {
   
        let args = [];
        returnfunctionfe() {
   
            if (arguments.length === 0) {
   
                return fn.apply(this, args);
            }
            [].push.apply(args, arguments);
            return fe;
        }
    }
    let count = currying(function (...rest) {
   
        return rest.reduce((prev, cur) => prev + cur, 0);
    });

    console.log(count(100)(200)(10)()); // 310

收集参数 延迟执行 到达指定次数才执行

   // 参数收集 指定次数后执行
        function fn(...rest) {
   console.log(rest);};
        function after(fn, time = 1) {
   
            let params = [];
            returnfunction(...rest) {
   
                params = [...params, ...rest];
                if (--time === 0) {
   
                    fn.apply(this, params);
                }
            }
        }
        let newFn = after(fn, 3); // 执行3次 内部fn才会执行
        newFn(2);
        newFn(3);
        newFn(4);

函数节流

throttle 策略的电梯。保证如果电梯第一个人进来后,50毫秒后准时运送一次,不等待。如果没有人,则待机。

let throttle = (fn, delay = 50) => {
    // 节流 控制执行间隔时间 防止频繁触发 scroll resize mousemove
            let stattime = 0;
            returnfunction (...args) {
   
                let curTime = new Date();
                if (curTime - stattime >= delay) {
   
                    fn.apply(this, args);
                    stattime = curTime;
                }
            }
        }

防抖动

debounce 策略的电梯。如果电梯里有人进来,等待50毫秒。如果又人进来,50毫秒等待重新计时,直到50毫秒超时,开始运送。参考 前端手写面试题详细解答

let debounce = (fn, time = 50) => {
    // 防抖动 控制空闲时间 用户输入频繁
            let timer;
            returnfunction (...args) {
   
                let that = this;
                clearTimeout(timer);
                timer = setTimeout(fn.bind(that, ...args), time);
            }
        }

深度拷贝兼容写法(不包括原型属性)

function deepCopy(obj) {
   
    if (typeof obj !== 'object') return obj;
    if (typeof window !== 'undefined' && window.JSON) {
    // 浏览器环境下 并支持window.JSON 则使用 JSON
        return JSON.parse(JSON.stringify(obj))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值