15个手写JS,巩固你的JS基础(面试高频)

举报
江咏之 发表于 2021/11/22 23:32:14 2021/11/22
【摘要】 作为前端开发,JS是重中之重,最近结束了面试的高峰期,基本上offer也定下来了就等开奖,趁着这个时间总结下32个手写JS问题,这些都是高频面试题,希望对你能有所帮助。 关于源码都紧遵规范,都可跑...

作为前端开发,JS是重中之重,最近结束了面试的高峰期,基本上offer也定下来了就等开奖,趁着这个时间总结下32个手写JS问题,这些都是高频面试题,希望对你能有所帮助。


关于源码都紧遵规范,都可跑通MDN示例,其余的大多会涉及一些关于JS的应用题和本人面试过程


01.数组扁平化


数组扁平化是指将一个多维数组变为一个一维数组

const arr = [1, [2, [3, [4, 5]]], 6]; // => [1, 2, 3, 4, 5, 6]
    
   

    方法一:使用flat()

    const res1 = arr.flat(Infinity);
        
       

      方法二:利用正则

      const res2 = JSON.stringify(arr).replace(/\[|\]/g, '').split(',');
          
         

        但数据类型都会变为字符串

        方法三:正则改良版本

        const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');
            
           

          方法四:使用reduce

          const flatten = arr => {
            return arr.reduce((pre, cur) => {
              return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
            }, [])
          }
          const res4 = flatten(arr);
          
              
             
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          可以获取PDF书籍源码、教程等给大家免费使用
          点击链接加入群聊【web前端技术群】:


          方法五:函数递归

          const res5 = [];
          const fn = arr => {
            for (let i = 0; i < arr.length; i++) {
              if (Array.isArray(arr[i])) {
                fn(arr[i]);
              } else {
                res5.push(arr[i]);
              }
            }
          }
          fn(arr);
              
             
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          02.数组去重

          const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
          // => [1, '1', 17, true, false, 'true', 'a', {}, {}]
              
             
          • 1

          方法一:利用Set

          const res1 = Array.from(new Set(arr));
              
             

            方法二:两层for循环+splice

            const unique1 = arr => {
              let len = arr.length;
              for (let i = 0; i < len; i++) {
                for (let j = i + 1; j < len; j++) {
                  if (arr[i] === arr[j]) {
                    arr.splice(j, 1);
                    // 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能
                    len--;
                    j--;
                  }
                }
              }
              return arr;
            }
                
               
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13

            方法三:利用indexOf

            const unique2 = arr => {
              const res = [];
              for (let i = 0; i < arr.length; i++) {
                if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
              }
              return res;
            }
                
               
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6

            当然也可以用include、filter,思路大同小异。

            方法四:利用include

            const unique3 = arr => {
              const res = [];
              for (let i = 0; i < arr.length; i++) {
                if (!res.includes(arr[i])) res.push(arr[i]);
              }
              return res;
            }
                
               
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6

            方法五:利用filter

            const unique4 = arr => {
              return arr.filter((item, index) => {
                return arr.indexOf(item) === index;
              });
            }
                
               
            • 1
            • 2
            • 3
            • 4

            方法六:利用Map

            const unique5 = arr => {
              const map = new Map();
              const res = [];
              for (let i = 0; i < arr.length; i++) {
                if (!map.has(arr[i])) {
                  map.set(arr[i], true)
                  res.push(arr[i]);
                }
              }
              return res;
            }
                
               
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10

            03.类数组转化为数组

            类数组是具有length属性,但不具有数组原型上的方法。常见的类数组有arguments、DOM操作方法返回的结果。

            方法一:Array.from

            Array.from(document.querySelectorAll('div'))
                
               

              方法二:Array.prototype.slice.call()

              Array.prototype.slice.call(document.querySelectorAll('div'))
                  
                 

                方法三:扩展运算符

                [...document.querySelectorAll('div')]
                    
                   

                  方法四:利用concat

                  Array.prototype.concat.apply([], document.querySelectorAll('div'));
                      
                     

                    04.Array.prototype.filter()

                    Array.prototype.filter = function(callback, thisArg) {
                      if (this == undefined) {
                        throw new TypeError('this is null or not undefined');
                      }
                      if (typeof callback !== 'function') {
                        throw new TypeError(callback + 'is not a function');
                      }
                      const res = [];
                      // 让O成为回调函数的对象传递(强制转换对象)
                      const O = Object(this);
                      // >>>0 保证len为number,且为正整数
                      const len = O.length >>> 0;
                      for (let i = 0; i < len; i++) {
                        // 检查i是否在O的属性(会检查原型链)
                        if (i in O) {
                          // 回调函数调用传参
                          if (callback.call(thisArg, O[i], i, O)) {
                            res.push(O[i]);
                          }
                        }
                      }
                      return res;
                    }
                    
                        
                       
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10
                    • 11
                    • 12
                    • 13
                    • 14
                    • 15
                    • 16
                    • 17
                    • 18
                    • 19
                    • 20
                    • 21
                    • 22
                    • 23

                    对于>>>0有疑问的:解释>>>0的作用

                    05.Array.prototype.map()

                    Array.prototype.map = function(callback, thisArg) {
                      if (this == undefined) {
                        throw new TypeError('this is null or not defined');
                      }
                      if (typeof callback !== 'function') {
                        throw new TypeError(callback + ' is not a function');
                      }
                      const res = [];
                      // 同理
                      const O = Object(this);
                      const len = O.length >>> 0;
                      for (let i = 0; i < len; i++) {
                        if (i in O) {
                          // 调用回调函数并传入新数组
                          res[i] = callback.call(thisArg, O[i], i, this);
                        }
                      }
                      return res;
                    }
                        
                       
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10
                    • 11
                    • 12
                    • 13
                    • 14
                    • 15
                    • 16
                    • 17
                    • 18

                    06.Array.prototype.forEach()

                    forEach跟map类似,唯一不同的是forEach是没有返回值的。

                    Array.prototype.forEach = function(callback, thisArg) {
                      if (this == null) {
                        throw new TypeError('this is null or not defined');
                      }
                      if (typeof callback !== "function") {
                        throw new TypeError(callback + ' is not a function');
                      }
                      const O = Object(this);
                      const len = O.length >>> 0;
                      let k = 0;
                      while (k < len) {
                        if (k in O) {
                          callback.call(thisArg, O[k], k, O);
                        }
                        k++;
                      }
                    }
                        
                       
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10
                    • 11
                    • 12
                    • 13
                    • 14
                    • 15
                    • 16

                    07.Array.prototype.reduce()

                    在这里插入图片描述

                    Array.prototype.reduce = function(callback, initialValue) {
                    if (this == undefined) {
                    throw new TypeError(‘this is null or not defined’);
                    }
                    if (typeof callback !== ‘function’) {
                    throw new TypeError(callbackfn + ’ is not a function’);
                    }
                    const O = Object(this);
                    const len = this.length >>> 0;
                    let accumulator = initialValue;
                    let k = 0;
                    // 如果第二个参数为undefined的情况下
                    // 则数组的第一个有效值作为累加器的初始值
                    if (accumulator === undefined) {
                    while (k < len && !(k in O)) {
                    k++;
                    }
                    // 如果超出数组界限还没有找到累加器的初始值,则TypeError
                    if (k >= len) {
                    throw new TypeError(‘Reduce of empty array with no initial value’);
                    }
                    accumulator = O[k++];
                    }
                    while (k < len) {
                    if (k in O) {
                    accumulator = callback.call(undefined, accumulator, O[k], k, O);
                    }
                    k++;
                    }
                    return accumulator;
                    }
                    • 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

                    08.Function.prototype.apply()

                    第一个参数是绑定的this,默认为window,第二个参数是数组或类数组

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

                    09.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 = contextfn;
                    delete context[fn];
                    return res;
                    }
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9

                    10.Function.prototype.bind

                    Function.prototype.bind = function(context, …args) {
                    if (typeof this !== ‘function’) {
                    throw new Error(“Type Error”);
                    }
                    // 保存this的值
                    var self = this;
                    return function F() {
                    // 考虑new的情况
                    if(this instanceof F) {
                    return new self(…args, …arguments)
                    }
                    return self.apply(context, […args, …arguments])
                    }
                    }
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10
                    • 11
                    • 12
                    • 13

                    11.debounce(防抖)

                    触发高频时间后n秒内函数只会执行一次,如果n秒内高频时间再次触发,则重新计算时间。

                    const debounce = (fn, time) => {
                    let timeout = null;
                    return function() {
                    clearTimeout(timeout)
                    timeout = setTimeout(() => {
                    fn.apply(this, arguments);
                    }, time);
                    }
                    };
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8

                    防抖常应用于用户进行搜索输入节约请求资源,window触发resize事件时进行防抖只触发一次。

                    12.throttle(节流)

                    高频时间触发,但n秒内只会执行一次,所以节流会稀释函数的执行频率。

                    const throttle = (fn, time) => {
                    let flag = true;
                    return function() {
                    if (!flag) return;
                    flag = false;
                    setTimeout(() => {
                    fn.apply(this, arguments);
                    flag = true;
                    }, time);
                    }
                    }
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10

                    节流常应用于鼠标不断点击触发、监听滚动事件。

                    13.函数珂里化

                    指的是将一个接受多个参数的函数 变为 接受一个参数返回一个函数的固定形式,这样便于再次调用,例如f(1)(2)

                    经典面试题:实现add(1)(2)(3)(4)=10;add(1)(1,2,3)(2)=9;

                    function add() {
                    const _args = […arguments];
                    function fn() {
                    _args.push(…arguments);
                    return fn;
                    }
                    fn.toString = function() {
                    return _args.reduce((sum, cur) => sum + cur);
                    }
                    return fn;
                    }
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10

                    14.模拟new操作

                    3个步骤:

                    1. ctor.prototype为原型创建一个对象。
                    2. 执行构造函数并将this绑定到新创建的对象上。
                    3. 判断构造函数执行返回的结果是否是引用数据类型,若是则返回构造函数执行的结果,否则返回创建的对象。
                    function newOperator(ctor, …args) {
                    if (typeof ctor !== ‘function’) {
                    throw new TypeError(‘Type Error’);
                    }
                    const obj = Object.create(ctor.prototype);
                    const res = ctor.apply(obj, args);
                    const isObject = typeof res === ‘object’ && res !== null;
                    const isFunction = typeof res === ‘function’;
                    return isObject || isFunction ? res : obj;
                    }
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10

                    15.instanceof

                    instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

                    const myInstanceof = (left, right) => {
                    // 基本数据类型都返回false
                    if (typeof left !== ‘object’ || left === null) return false;
                    let proto = Object.getPrototypeOf(left);
                    while (true) {
                    if (proto === null) return false;
                    if (proto === right.prototype) return true;
                    proto = Object.getPrototypeOf(proto);
                    }
                    }
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9

                    文章来源: jiangwenxin.blog.csdn.net,作者:前端江太公,版权归原作者所有,如需转载,请联系作者。

                    原文链接:jiangwenxin.blog.csdn.net/article/details/121234940

                    【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
                    • 点赞
                    • 收藏
                    • 关注作者

                    评论(0

                    0/1000
                    抱歉,系统识别当前为高风险访问,暂不支持该操作

                    全部回复

                    上滑加载中

                    设置昵称

                    在此一键设置昵称,即可参与社区互动!

                    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

                    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。