• 记一次京东前端面试被问到的题目


    实现lodash的chunk方法–数组按指定长度拆分

    题目

    /**
     * @param input
     * @param size
     * @returns {Array}
     */
    _.chunk(['a', 'b', 'c', 'd'], 2)
    // => [['a', 'b'], ['c', 'd']]
    
    _.chunk(['a', 'b', 'c', 'd'], 3)
    // => [['a', 'b', 'c'], ['d']]
    
    _.chunk(['a', 'b', 'c', 'd'], 5)
    // => [['a', 'b', 'c', 'd']]
    
    _.chunk(['a', 'b', 'c', 'd'], 0)
    // => []
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    实现

    function chunk(arr, length) {
       
      let newArr = [];
      for (let i = 0; i < arr.length; i += length) {
       
        newArr.push(arr.slice(i, i + length));
      }
      return newArr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Function.prototype.call

    call唯一不同的是,call()方法接受的是一个参数列表

    Function.prototype.call = function(context = window, ...args) {
       
      if (typeof this !== 'function') {
       
        throw new TypeError('Type Error');
      }
      const fn = Symbol('fn');
      context[fn] = this;
    
      const res = context[fn](...args);
      delete context[fn];
      return res;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    实现防抖函数(debounce)

    防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

    那么与节流函数的区别直接看这个动画实现即可。

    手写简化版:

    // 防抖函数
    const debounce = (fn, delay) => {
       
      let timer = null;
      return (...args) => {
       
        clearTimeout(timer);
        timer = setTimeout(() => {
       
          fn.apply(this, args);
        }, delay);
      };
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    适用场景:

    • 按钮提交场景:防止多次提交按钮,只执行最后提交的一次
    • 服务端验证场景:表单验证需要服务端配合,只执行一段连续的输入事件的最后一次,还有搜索联想词功能类似

    生存环境请用lodash.debounce

    手写 bind 函数

    bind 函数的实现步骤:

    1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
    2. 保存当前函数的引用,获取其余传入参数值。
    3. 创建一个函数返回
    4. 函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 this 给 apply 调用,其余情况都传入指定的上下文对象。
    // bind 函数实现
    Function.prototype.myBind = function(context) {
       
      // 判断调用对象是否为函数
      if (typeof this !== "function") {
       
        throw new TypeError("Error");
      }
      // 获取参数
      var args = [...arguments].slice(1),
          fn = this;
      return function Fn() {
       
        // 根据调用方式,传入不同绑定值
        return fn.apply(
          this instanceof Fn ? this : context,
          args.concat(...arguments)
        );
      };
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    实现浅拷贝

    浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

    (1)Object.assign()

    Object.assign()是ES6中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

    注意:

    • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
    • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
    • 因为nullundefined 不能转化为对象,所以第一个参数不能为nullundefined,会报错。
    let target = {
       a: 1};
    let object2 = {
       b: 2};
    let object3 = {
       c: 3};
    Object.assign(target,object2,object3);  
    console.log(target);  // {a: 1, b: 2, c: 3}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    (2)扩展运算符

    使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。语法:let cloneObj = { ...obj };

    let obj1 = {
       a:1,b:{
       c:1}}
    let obj2 = {
       ...obj1};
    obj1.a = 2;
    console.log(obj1); //{a:2,b:{c:1}}
    console.log(obj2); //{a:1,b:{c:1}}
    obj1.b.c = 2;
    console.log(obj1); //{a:2,b:{c:2}}
    console.log(obj2); //{a:1,b:{c:2}}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    (3)数组方法实现数组浅拷贝
    1)Array.prototype.slice
    • slice()方法是JavaScript数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
    • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
    let arr = [1,2,3,4];
    console.log(arr.slice()); // [1,2,3,4]
    console.log(arr.slice() === arr); //false
    
    
    • 1
    • 2
    • 3
    • 4
    2)Array.prototype.concat
    • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
    • 该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝。
    let arr = [1,2,3,4];
    console.log(arr.concat()); // [1,2,3,4]
    console.log(arr.concat() === arr); //false
    
    
    • 1
    • 2
    • 3
    • 4
    (4)手写实现浅拷贝
    // 浅拷贝的实现;
    
    function shallowCopy(object) {
       
      // 只拷贝对象
      if (!object || typeof object !== "object") return;
    
      // 根据 object 的类型判断是新建一个数组还是对象
      let newObject = Array.isArray(object) ? [] : {
       };
    
      // 遍历 object,并且判断是 object 的属性才拷贝
      for (let key in object) {
       
        if (object.hasOwnProperty(key)) {
       
          newObject[key] = object[key];
        }
      }
    
      return newObject;
    }// 浅拷贝的实现;
    
    function shallowCopy(object) {
       
      // 只拷贝对象
      if (!object || typeof object !== "object") return;
    
      // 根据 object 的类型判断是新建一个数组还是对象
      let newObject = Array.isArray(object) ? [] : {
       };
    
      // 遍历 object,并且判断是 object 的属性才拷贝
      for (let key in object) {
       
        if (object.hasOwnProperty(key)) {
       
          newObject[key] = object[key];
        }
      }
    
      return newObject;
    }// 浅拷贝的实现;
    function shallowCopy(object) {
       
      // 只拷贝对象
      if (!object || typeof object !== "object") return;
      // 根据 object 的类型判断是新建一个数组还是对象
      let newObject = Array.isArray(object) ? [] : {
       };
      // 遍历 object,并且判断是 object 的属性才拷贝
      for (let key in object) {
       
        if (object.hasOwnProperty(key)) {
       
          newObject[key] = object[key];
        }
      }
      return newObject;
    }
    
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    实现apply方法

    apply原理与call很相似,不多赘述

    // 模拟 apply
    Function.prototype.myapply = function(context, arr) {
       
      var context = Object(context) || window;
      context.fn = this;
    
      var result;
      if (!arr) {
       
        result = context.fn();
      } else {
       
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
       
          args.push("arr[" + i + "]");
        }
        result = eval("context.fn(" + args + ")");
      }
    
      delete context.fn;
      return result;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    实现字符串的repeat方法

    输入字符串s,以及其重复的次数,输出重复的结果,例如输入abc,2,输出abcabc。

    function repeat(s, n) {
       
        return (new Array(n + 1)).join(s);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    递归:

    function repeat(s, n) {
       
        return (n > 0) ? s.concat(repeat(s, --n)) : "";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    实现深拷贝

    • 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用  Object.assign 和展开运算符来实现。
    • 深拷贝: 深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败
    (1)JSON.stringify()
    • JSON.parse(JSON.stringify(obj))是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringifyjs对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象。
    • 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。
    let obj1 = {
         a: 0,
                  b: {
       
                     c: 0
                     }
                };
    let obj2 = JSON.parse(JSON.stringify(obj1));
    obj1.a = 1;
    obj1.b.c = 1;
    console.log(obj1); // {a: 1, b: {c: 1}}
    console.log(obj2); // {a: 0, b: {c: 0}}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    (2)函数库lodash的_.cloneDeep方法

    该函数库也有提供_.cloneDeep用来做 Deep Copy

    var _ = require('lodash');
    var obj1 = {
       
        a: 1,
        b: {
        f: {
        g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = _.cloneDeep(obj1);
    console.log(obj1.b.f === obj2.b.f);// false
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    (3)手写实现深拷贝函数
    // 深拷贝的实现
    function deepCopy(object) {
       
      if (!object || typeof object !== "object") return;
    
      let newObject = Array.isArray(object) ? [] : {
       };
    
      for 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    [react基础]关于v6版本route的变化,以及常见应用模式
    Matlab图像处理-三基色
    async与await
    .NET轻松实现支付宝服务窗网页授权并获取用户相关信息
    如何在企业网站里做好网络安全
    基于SpringBoot框架的古风乐曲网站的设计与实现毕业设计源码271611
    《持续交付:发布可靠软件的系统方法》- 读书笔记(十五)
    FreeRTOS入门教程(队列详细使用示例)
    git相关知识记录
    [附源码]JAVA毕业设计科研项目审批管理系统(系统+LW)
  • 原文地址:https://blog.csdn.net/helloworld1024fd/article/details/127859446