• 手撕常见JS面试题


    高阶函数实现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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出结果:

    ===before===
    func
    ===after===   
    
    • 1
    • 2
    • 3

    当我们 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参考 前端手写面试题详细解答

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

       // 参数收集 指定次数后执行
            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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    函数节流

    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;
                    }
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    防抖动

    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);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    function deepCopy(obj) {
       
        if (typeof obj !== 'object') return obj;
        if (typeof window !== 'undefined' && window.JSON) {
        // 浏览器环境下 并支持window.JSON 则使用 JSON
            return JSON.parse(JSON.stringify(obj))
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Discourse 的左侧边栏可以修改吗
    从零开始Blazor Server(9)--修改Layout
    手写Java设计模式之抽象工厂模式,附源码解读
    Redis 6.0 新功能
    Golang使用sqlx报错max_prepared_stmt_count超过16382
    【SQL性能优化】索引的使用原则:如何通过索引让SQL查询效率最大化?(优)
    03 树莓派QEMU模拟器介绍
    spring 源码实现
    SpringBoot2 常用注解
    Python编程中的异常处理
  • 原文地址:https://blog.csdn.net/helloworld1024fd/article/details/127859484