前端一面手写面试题总结

这篇博客总结了前端面试中常见的手写题目,包括使用Promise封装AJAX请求、实现大数相加、双向数据绑定、发布-订阅模式、节流函数、Promise并行限制等。还探讨了浅拷贝的不同实现方式,如Object.assign()、扩展运算符以及数组方法。此外,讲解了new操作符的工作原理、Event Bus的实现以及如何用setTimeout模拟setInterval。

使用Promise封装AJAX请求

// promise 封装实现:
function getJSON(url) {
   
   
  // 创建一个 promise 对象
  let promise = new Promise(function(resolve, reject) {
   
   
    let xhr = new XMLHttpRequest();
    // 新建一个 http 请求
    xhr.open("GET", url, true);
    // 设置状态的监听函数
    xhr.onreadystatechange = function() {
   
   
      if (this.readyState !== 4) return;
      // 当请求成功或失败时,改变 promise 的状态
      if (this.status === 200) {
   
   
        resolve(this.response);
      } else {
   
   
        reject(new Error(this.statusText));
      }
    };
    // 设置错误监听函数
    xhr.onerror = function() {
   
   
      reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = "json";
    // 设置请求头信息
    xhr.setRequestHeader("Accept", "application/json");
    // 发送 http 请求
    xhr.send(null);
  });
  return promise;
}

实现一个add方法完成两个大数相加

// 题目
let a = "9007199254740991";
let b = "1234567899999999999";

function add(a ,b){
   
   
   //...
}

实现代码如下:

function add(a ,b){
   
   
   //取两个数字的最大长度
   let maxLength = Math.max(a.length, b.length);
   //用0去补齐长度
   a = a.padStart(maxLength , 0);//"0009007199254740991"
   b = b.padStart(maxLength , 0);//"1234567899999999999"
   //定义加法过程中需要用到的变量
   let t = 0;
   let f = 0;   //"进位"
   let sum = "";
   for(let i=maxLength-1 ; i>=0 ; i--){
   
   
      t = parseInt(a[i]) + parseInt(b[i]) + f;
      f = Math.floor(t/10);
      sum = t%10 + sum;
   }
   if(f!==0){
   
   
      sum = '' + f + sum;
   }
   return sum;
}

实现双向数据绑定

let obj = {
   
   }
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
   
   
  configurable: true,
  enumerable: true,
  get() {
   
   
    console.log('获取数据了')
  },
  set(newVal) {
   
   
    console.log('数据更新了')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// 输入监听
input.addEventListener('keyup', function(e) {
   
   
  obj.text = e.target.value
})

实现发布-订阅模式

class EventCenter{
   
   
  // 1. 定义事件容器,用来装事件数组
    let handlers = {
   
   }

  // 2. 添加事件方法,参数:事件名 事件方法
  addEventListener(type, handler) {
   
   
    // 创建新数组容器
    if (!this.handlers[type]) {
   
   
      this.handlers[type] = []
    }
    // 存入事件
    this.handlers[type].push(handler)
  }

  // 3. 触发事件,参数:事件名 事件参数
  dispatchEvent(type, params) {
   
   
    // 若没有注册该事件则抛出错误
    if (!this.handlers[type]) {
   
   
      return new Error('该事件未注册')
    }
    // 触发事件
    this.handlers[type].forEach(handler => {
   
   
      handler(...params)
    })
  }

  // 4. 事件移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布
  removeEventListener(type, handler) {
   
   
    if (!this.handlers[type]) {
   
   
      return new Error('事件无效')
    }
    if (!handler) {
   
   
      // 移除事件
      delete this.handlers[type]
    } else {
   
   
      const index = this.handlers[type].findIndex(el => el === handler)
      if (index === -1) {
   
   
        return new Error('无该绑定事件')
      }
      // 移除事件
      this.handlers[type].splice(index, 1)
      if (this.handlers[type].length === 0) {
   
   
        delete this.handlers[type]
      }
    }
  }
}

手写节流函数

函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

// 函数节流的实现;
function throttle(fn, delay) {
   
   
  let curTime = Date.now();

  return function() {
   
   
    let context = this,
        args = arguments,
        nowTime = Date.now();

    // 如果两次时间间隔超过了指定时间,则执行函数。
    if (nowTime - curTime >= delay) {
   
   
      curTime = Date.now();
      return fn.apply(context, args);
    }
  };
}

Promise并行限制

就是实现有并行限制的Promise调度器问题

class Scheduler {
   
   
  constructor() {
   
   
    this.queue = [];
    this.maxCount = 2;
    this.runCounts = 0;
  }
  add(promiseCreator) {
   
   
    this.queue.push(promiseCreator);
  }
  taskStart() {
   
   
    for (let i = 0; i < this.maxCount; i++) {
   
   
      this.request
前端手写面试题汇总如下: 1. **排序算法类**:冒泡排序、选择排序、插入排序[^1]。 2. **查找算法类**:二分法查找[^1]。 3. **功能实现类**: - 实现一个计数器 - 获取 URL 参数 - 手写 new 的执行过程 - 手写实现 Object.create() - instanceof 实现 - 实现红黄绿循环打印 - 用 promise 实现异步加载图片 - 实现数组的 push、filter、map 方法 - 使用 setTimeout 实现 setInterval - 手写 bind - 实现 jsonp - 手写一个深度比较 isEqual - 手写深拷贝 - 手写数组去除重复 - 大数相加 - 节流函数 - 防抖函数 - 设计一个图片懒加载 SDK - 用正则实现 trim() 清除字符串两端空格 - 简单写一个 webpack 配置文件 - react 自定义 Hook useCountdown - 实现 call 和 apply 方法 - 写一个前端图片压缩的工具方法 [^1] 4. **数据结构转换类**:数组 -> 树的转换 [^4]。 以下是部分题目的示例代码: ```javascript // 实现数组的 map 方法 Array.prototype._map = function(fn) { if (typeof fn!== "function") { throw Error('参数必须是一个函数'); } const res = []; for (let i = 0, len = this.length; i < len; i++) { res.push(fn(this[i])); } return res; } // 模拟 instanceof function instance_of(L, R) { var O = R.prototype; L = L.__proto__; while (true) { if (L === null) return false; if (O === L) return true; L = L.__proto__; } } // 数组 -> 树 const arrtree = [ {id:1, parentId: null, name: 'a'}, {id:2, parentId: null, name: 'b'}, {id:6, parentId: 3, name: 'f'}, {id:7, parentId: 4, name: 'g'}, {id:8, parentId: 7, name: 'h'}, {id:3, parentId: 1, name: 'c'}, {id:4, parentId: 2, name: 'd'}, {id:5, parentId: 1, name: 'e'} ]; function arrayTree(arr) { if (!Array.isArray(arr) ||!arr.length) return; let map = {}; arr.forEach(v => map[v.id] = v); let list = []; arr.forEach(v => { const item = map[v.parentId]; if (item) { (item.children || (item.children = [])).push(v); } else { list.push(v); } }); return list; } ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值