【面试题】 面试手写JS 十六题(必看)

简介: 【面试题】 面试手写JS 十六题(必看)

 给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

 

1、手写实现防抖和节流

1.1 实现防抖函数

防抖函数原理:把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算

// func是用户传入需要防抖的函数
// time是等待时间
const debounce = (callback, time = 50) => {
  let timer = 0
  return function(...args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      callback.apply(this, args)
    }, time)
  }
}
复制代码

1.2 实现节流函数

节流函数原理: 指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的时间才会执行回调函数。

// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
  let lastTime = 0
  return function(...args) {
    let now = +new Date()
    if (now - lastTime > wait) {
      lastTime = now
      func.apply(this, args)
    }
  }
}
复制代码

2、手写深拷贝

调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历。这就是我们在解这一类题时的核心的方法。

function deepClone(obj) {
    if(typeof obj !== 'object' || obj === null) {
      return obj
    }
    let copy = {}
    if(obj.constructor === Array) {
      copy = []
    }
    for(let key in obj) {
        if(obj.hasOwnProperty(key)) {
          copy[key] = deepClone(obj[key])
        }
    }
    return copy
} 
复制代码

3、手写实现call方法

call做了什么

  1. 将函数设为对象的属性
  2. 执行和删除这个函数
  3. 指定this到函数并传入给定参数执行函数
  4. 如果不传入参数,默认指向为 window
Function.prototype.myCall = function(context = window, ...args) {
  if (typeof this !== "function") {
    throw new Error('type error')
  }
  let key = Symbol('key')
  context[key] = this;
  let result = context[key](...args);
  delete context[key];
  return result;
};
复制代码

4 手写apply方法

Function.prototype.myApply = function(context = window, args) {
  let key = Symbol('key')
  context[key] = this;
  // let args = [...arguments].slice(1)
  let result = context[key](...args);
  delete context[key]; 
  return result;
}
复制代码

5、手写forEach方法

Array.prototype.myForEach = function(callback, context=window) {
  let self = this,  
      i = 0,
      len = self.length;
  for(;i<len;i++) {
    typeof callback == 'function' && callback.call(context,self[i], i)
   }
}
复制代码

6、手写filter方法

Array.prototype.myFilter=function(callback, context=window){
  let len = this.length
      newArr = [],
      i=0
  for(; i < len; i++){
    if(callback.apply(context, [this[i], i , this])){
      newArr.push(this[i]);
    }
  }
  return newArr;
}
复制代码

7、手写reduce方法

Array.prototype.myReduce = function(fn, initialValue) {
  var arr = Array.prototype.slice.call(this);
  var res, startIndex;
  res = initialValue ? initialValue : arr[0];
  startIndex = initialValue ? 0 : 1;
  for(var i = startIndex; i < arr.length; i++) {
    res = fn.call(null, res, arr[i], i, this); 
  }
  return res;
}
复制代码

8 查找字符串中出现最多的字符和个数

字符最多的是char,出现了num次

function myString(str){
    let num = 0;
    let char = '';
    let re = /(\w)\1+/g;
    str.replace(re,($0,$1) => {
        if(num < $0.length){
            num = $0.length;
            char = $1;        
        }
    });
    return {num, char}
}
复制代码

9 冒泡排序

冒泡排序的原理如下,从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到 length - 1 的位置

function bubbleSort(list) {
  var n = list.length;
  if (!n) return [];
  for (var i = 0; i < n; i++) {
    // 注意这里需要 n - i - 1
    for (var j = 0; j < n - i - 1; j++) {
      if (list[j] > list[j + 1]) {
        var temp = list[j + 1];
        list[j + 1] = list[j];
        list[j] = temp;
      }
    }
  }
  return list;
}
复制代码

10 快速排序

快排的原理如下。随机选取一个数组中的值作为基准值,从左至右取值与基准值对比大小。比基准值小的放数组左边,大的放右边,对比完成后将基准值和第一个比基准值大的值交换位置。然后将数组以基准值的位置分为两部分,继续递归以上操作

function quickSort(arr) {
  if (arr.length<=1){
    return arr;
  }
  var baseIndex = Math.floor(arr.length/2);
  var base = arr.splice(baseIndex,1)[0];
  var left=[];
  var right=[];
  for (var i = 0; i<arr.length; i++){
    if (arr[i] < base){
      left.push(arr[i]);
    }
  }else{
    right.push(arr[i]);,
  }
  return quickSort(left).concat([base],quickSort(right));
}
复制代码

11 插入排序

function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let j = i;
    let target = arr[j];
    while (j > 0 && arr[j - 1] > target) {
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = target;
  }
  return arr;
}
复制代码

12 对象扁平化

function objectFlat(obj = {}) {
  const res = {}
  function flat(item, preKey = '') {
    Object.entries(item).forEach(([key, val]) => {
      const newKey = preKey ? `${preKey}.${key}` : key
      if (val && typeof val === 'object') {
        flat(val, newKey)
      } else {
        res[newKey] = val
      }
    })
  }
  flat(obj)
  return res
}
复制代码

13、手写发布订阅模式

  • 创建一个对象
  • on方法用来把回调函数fn都加到缓存列表中
  • emit 根据key值去执行对应缓存列表中的函数
  • off方法可以根据key值取消订阅
class EventEmiter {
  constructor() {
    this._events = {}
  }
  on(eventName,callback) {
    if(!this._events) {
      this._events = {}
    }
    this._events[eventName] = [...(this._events[eventName] || []),callback]
  }
  emit(eventName, ...args) {
    if(!this._events[eventName]) {
      return
    }
    this._events[eventName].forEach(fn=>fn(...args))
  }
  off(eventName,cb) {
    if(!this._events[eventName]) {
      return
    }
    this._events[eventName] = this._events[eventName].filter(fn=>fn != cb && fn.l != cb)
  }
  once(eventName,callback) {
    const one = (...args)=>{
      callback(args)
      this.off(eventName,one)
    }
    one.l = callback
    this.on(eventName,one)
  }
}
复制代码

14、手写Ajax

  • 创建 XMLHttpRequest 实例
  • 发出 HTTP 请求
  • 服务器返回 XML 格式的字符串
  • JS 解析 XML,并更新局部页面
  • 不过随着历史进程的推进,XML 已经被淘汰,取而代之的是 JSON。
function ajax(url) {
  return new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest()
    xhr.open('get', url)
    xhr.onreadystatechange = () => {
      if (xhr.readyState == 4) {
        if (xhr.status >= 200 && xhr.status <= 300) {
          resolve(JSON.parse(xhr.responseText))
        } else {
          reject('请求出错')
        }
      }
    }
    xhr.send()
  })
}
复制代码

15、数组去重

function uniqueArr(arr) { return [...new Set(arr)]; }
复制代码

16、实现new的过程

new操作符做了这些事:

  • 创建一个全新的对象
  • 这个对象的__proto__要指向构造函数的原型prototype
  • 执行构造函数,使用 call/apply 改变 this 的指向
  • 返回值为object类型则作为new方法的返回值返回,否则返回上述全新对象
function myNew(fn, ...args) {
  let newObj = Object.create(fn.prototype);
  let res = fn.apply(newObj, args);
  return typeof res === 'object' ? res: newObj;
}

 

 给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

相关文章
|
1月前
|
JavaScript 前端开发
常见的JS面试题
【8月更文挑战第5天】 常见的JS面试题
49 3
|
24天前
|
Java
【多线程面试题十六】、谈谈ReentrantLock的实现原理
这篇文章解释了`ReentrantLock`的实现原理,它基于Java中的`AbstractQueuedSynchronizer`(AQS)构建,通过重写AQS的`tryAcquire`和`tryRelease`方法来实现锁的获取与释放,并详细描述了AQS内部的同步队列和条件队列以及独占模式的工作原理。
【多线程面试题十六】、谈谈ReentrantLock的实现原理
|
25天前
|
JavaScript
【Vue面试题十六】、Vue.observable你有了解过吗?说说看
这篇文章详细介绍了Vue中的`Vue.observable`特性,解释了它如何使一个对象变成响应式数据,并可用于渲染函数和计算属性内。文章讨论了`Vue.observable`在不同Vue版本中的行为差异,展示了其在非父子组件通信中的使用场景,并通过代码示例说明了如何在组件中使用`observable`对象。此外,文章还深入分析了`Vue.observable`的实现原理,包括`Observer`类的构造和`defineReactive`方法的工作方式。
【Vue面试题十六】、Vue.observable你有了解过吗?说说看
|
24天前
|
安全 Java
【Java集合类面试十六】、HashMap与ConcurrentHashMap有什么区别?
HashMap是非线程安全的,而ConcurrentHashMap通过减少锁粒度来提高并发性能,检索操作无需锁,从而提供更好的线程安全性和性能。
|
25天前
|
Java
【Java基础面试十六】、Java中的多态是怎么实现的?
这篇文章解释了Java中多态的实现机制,主要是通过继承,允许将子类实例赋给父类引用,并在运行时表现出子类的行为特征,实现这一过程通常涉及普通类、抽象类或接口的使用。
|
25天前
|
存储 JavaScript 前端开发
2022年前端js面试题
2022年前端js面试题
20 0
|
1月前
|
JavaScript 前端开发 程序员
JS小白请看!一招让你的面试成功率大大提高——规范代码
JS小白请看!一招让你的面试成功率大大提高——规范代码
|
1月前
|
JavaScript 前端开发 UED
小白请看! 大厂面试题 :如何用JS实现瀑布流
小白请看! 大厂面试题 :如何用JS实现瀑布流
|
1月前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
1月前
|
JavaScript 前端开发
JS:类型转换(四)从底层逻辑让你搞懂经典面试问题 [ ] == ![ ] ?
JS:类型转换(四)从底层逻辑让你搞懂经典面试问题 [ ] == ![ ] ?