常见前端手写面试题

简介: 常见前端手写面试题
  • 几道手写面试题,考验对js掌握的熟练程度

随机打乱数组

 shuffle(array) {
        if (!Array.isArray(array)) {
            return array;
        }
        for (var i = array.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
         [array[i], array[j]] = [array[j], array[i]]
        }
        return array;
    },

手动实现 Arrar.prototype.map方法

  • map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
function map(arr,mapCallback) {
    首先检测传递的参数是否正确
    if(!Arrar.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
        return []
    } else {
        let result = [];
        for(let i = 0,len = arr.length;i<len;i++) {
            result.push(mapCallback(arr[i],i,arr))
        }
        return result
    }
}

手动实现Array.prototype.filter方法

  • filter()方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
function filter(arr,filterCallback) {
    if(!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') {
        return []
    } else {
        let result = [];
        for (let i=0,len=arr.length;i<len;i++) {
            if(filterCallback(arr[i],i,arr)) {
                result.push(arr[i])
            }
        }
        return result
    }
}

JS的节流和防抖

  • 函数防抖 是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
  • 防抖: 在一段时间内,事件只会最后触发一次。
  • 节流: 事件,按照一段时间的间隔来进行触发。

    // 函数防抖的实现
    function debounce(fn, wait) {
      var timer = null;
    
      return function() {
        var context = this,
          args = arguments;
    
        // 如果此时存在定时器的话,则取消之前的定时器重新记时
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
    
        // 设置定时器,使事件间隔指定事件后执行
        timer = setTimeout(() => {
          fn.apply(context, args);
        }, wait);
      };
    }
  • 函数节流 是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

    // 函数节流的实现;
    function throttle(fn, delay) {
      var preTime = Date.now();
    
      return function() {
        var context = this,
          args = arguments,
          nowTime = Date.now();
    
        // 如果两次时间间隔超过了指定时间,则执行函数。
        if (nowTime - preTime >= delay) {
          preTime = Date.now();
          return fn.apply(context, args);
        }
      };
    }

多维数组处理一维数组

// 多维数组处理一维数据
export const flatten = (arr) => {
  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
例如:
arr = [1,2,3,[22,33,44,[55,66,77,[88,99,100]]]]
flatten(arr)  => [1, 2, 3, 22, 33, 44, 55, 66, 77, 88, 99, 100]

手写call bind apply

  • call bind apply的作用都是可以进行修改this指向
  • call 和 apply的区别在于参数传递的不同
  • bind 区别在于最后会返回一个函数。

    // call
    Function.prototype.MyCall = function (context) {
      if (typeof this !== "function") {
        throw new Error('type error')
      }
      if (context === null || context === undefined) {
        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
      } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context)
      }

      // 使用Symbol 来确定唯一
      const fnSym = Symbol()

      //模拟对象的this指向
      context[fnSym] = this

      // 获取参数
      const args = [...arguments].slice(1)

      //绑定参数 并执行函数
      const result = context[fnSym](...args) 

      //清除定义的this
      delete context[fnSym]

      // 返回结果 
      return result
    } 
    
    
    // call 如果能明白的话 apply其实就是改一下参数的问题
    // apply
    Function.prototype.MyApply = function (context) {
      if (typeof this !== "function") {
        throw new Error('type error')
      }

      if (context === null || context === undefined) {
        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
      } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context) 
      }


      // 使用Symbol 来确定唯一
      const fnSym = Symbol()
      //模拟对象的this指向
      context[fnSym] = this

      // 获取参数
      const args = [...arguments][1]

      //绑定参数 并执行函数 由于apply 传入的是一个数组 所以需要解构
      const result = arguments.length > 1 ? context[fnSym](...args) : context[fnSym]()

      //清除定义的this
      delete context[fnSym]

      // 返回结果  //清除定义的this
      return result
    }
    
    
    
    // bind
    Function.prototype.MyBind = function (context) {
      if (typeof this !== "function") {
        throw new Error('type error')
      }

      if (context === null || context === undefined) {
        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
      } else {
        // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
        context = Object(context) 
      }

      //模拟对象的this指向
      const self = this

      // 获取参数
      const args = [...arguments].slice(1)
        
      // 最后返回一个函数 并绑定 this 要考虑到使用new去调用,并且bind是可以传参的
      return function Fn(...newFnArgs) {
        if (this instanceof Fn) {
            return new self(...args, ...newFnArgs)
        }
            return self.apply(context, [...args, ...newFnArgs])
        }
    }

手写实现继承

 /**
    * es6之前  寄生组合继承 
    */
    {
      function Parent(name) {
        this.name = name
        this.arr = [1, 2, 3]
      }

      Parent.prototype.say = () => {
        console.log('Hi');
      }

      function Child(name, age) {
        Parent.call(this, name)
        this.age = age
      }

      //  核心代码 通过Object.create创建新对象 子类 和 父类就会隔离
      // Object.create:创建一个新对象,使用现有的对象来提供新创建的对象的__proto__ 
      Child.prototype = Object.create(Parent.prototype)
      Child.prototype.constructor = Child
    }
    
    
    
    /**
    *   es6继承 使用关键字class
    */
     {
      class Parent {
        constructor(name) {
          this.name = name
          this.arr = [1, 2, 3]
        }
      }
      class Child extends Parent {
        constructor(name, age) {
          super(name)
          this.age = age
        }
      }
    }

手写数组的 flat

  • 原理就是,先在内部生成一个新数组,遍历原来的数组,当原数组内 存在数组并且层级deep大于等于1时进行递归, 如果不满足这个条件就可以直接push数据到新数组去
  • 递归同时要先把层级减少, 然后通过 concat 链接递归出来的数组
  • 最终返回这个数组就可以了
   const flat = function (arr, deep = 1) {
      // 声明一个新数组
      let result = []
      
      arr.forEach(item => {
        if (Array.isArray(item) && deep > 0) {
          // 层级递减
          // deep--  来自评论区的大佬指正:deep - 1
          // 使用concat链接数组  
          result = result.concat(flat(item, deep - 1))
        } else {
          result.push(item)
        }
      })
      return result
    }
目录
相关文章
|
3月前
|
缓存 前端开发 中间件
[go 面试] 前端请求到后端API的中间件流程解析
[go 面试] 前端请求到后端API的中间件流程解析
|
19天前
|
缓存 前端开发 JavaScript
"面试通关秘籍:深度解析浏览器面试必考问题,从重绘回流到事件委托,让你一举拿下前端 Offer!"
【10月更文挑战第23天】在前端开发面试中,浏览器相关知识是必考内容。本文总结了四个常见问题:浏览器渲染机制、重绘与回流、性能优化及事件委托。通过具体示例和对比分析,帮助求职者更好地理解和准备面试。掌握这些知识点,有助于提升面试表现和实际工作能力。
53 1
|
2月前
|
Web App开发 前端开发 Linux
「offer来了」浅谈前端面试中开发环境常考知识点
该文章归纳了前端开发环境中常见的面试知识点,特别是围绕Git的使用进行了详细介绍,包括Git的基本概念、常用命令以及在团队协作中的最佳实践,同时还涉及了Chrome调试工具和Linux命令行的基础操作。
「offer来了」浅谈前端面试中开发环境常考知识点
|
3月前
|
存储 XML 移动开发
前端大厂面试真题
前端大厂面试真题
|
30天前
|
Web App开发 JavaScript 前端开发
前端Node.js面试题
前端Node.js面试题
|
3月前
|
存储 前端开发 JavaScript
44 个 React 前端面试问题
【8月更文挑战第18天】
51 2
|
3月前
|
存储 JavaScript 前端开发
2022年前端js面试题
2022年前端js面试题
38 0
|
3月前
|
存储 前端开发 JavaScript
44 个 React 前端面试问题
44 个 React 前端面试问题
|
3月前
|
存储 JavaScript 前端开发
|
3月前
|
Web App开发 存储 缓存