滴滴前端一面常考手写面试题合集

本文详细介绍了前端面试中常见的手写面试题,包括实现call方法、类数组转化为数组、深拷贝的多种实现方式、发布-订阅模式、查找文章中最高频单词、Object.is、instanceOf、Array.prototype.filter、大量数据渲染优化、AJAX请求、节流函数、Promise并行限制、数组的map方法、二叉树遍历、拖拽功能、数据类型判断等核心知识点,旨在帮助应聘者全面掌握前端基础和深入技能。
摘要由CSDN通过智能技术生成

实现call方法

call做了什么:

  • 将函数设为对象的属性
  • 执行和删除这个函数
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数,默认指向为 window
// 模拟 call bar.mycall(null);
//实现一个call方法:
// 原理:利用 context.xxx = self obj.xx = func-->obj.xx()
Function.prototype.myCall = function(context = window, ...args) {
   
  if (typeof this !== "function") {
   
    throw new Error('type error')
  }
  // this-->func  context--> obj  args--> 传递过来的参数

  // 在context上加一个唯一值不影响context上的属性
  let key = Symbol('key')
  context[key] = this; // context为调用的上下文,this此处为函数,将这个函数作为context的方法
  // let args = [...arguments].slice(1)   //第一个参数为obj所以删除,伪数组转为数组

  // 绑定参数 并执行函数
  let result = context[key](...args);
  // 清除定义的this 不删除会导致context属性越来越多
  delete context[key];

  // 返回结果 
  return result;
};
//用法:f.call(obj,arg1)
function f(a,b){
   
 console.log(a+b)
 console.log(this.name)
}
let obj={
   
 name:1
}
f.myCall(obj,1,2) //否则this指向window

实现类数组转化为数组

类数组转换为数组的方法有这样几种:

  • 通过 call 调用数组的 slice 方法来实现转换
Array.prototype.slice.call(arrayLike);

  • 通过 call 调用数组的 splice 方法来实现转换
Array.prototype.splice.call(arrayLike, 0);

  • 通过 apply 调用数组的 concat 方法来实现转换
Array.prototype.concat.apply([], arrayLike);

  • 通过 Array.from 方法来实现转换
Array.from(arrayLike);

实现数组的push方法

let arr = [];
Array.prototype.push = function() {
   
    for( let i = 0 ; i < arguments.length ; i++){
   
        this[this.length] = arguments[i] ;
    }
    return this.length;
}

JSONP

script标签不遵循同源协议,可以用来进行跨域请求,优点就是兼容性好但仅限于GET请求

const jsonp = ({
     url, params, callbackName }) => {
   
  const generateUrl = () => {
   
    let dataSrc = '';
    for (let key in params) {
   
      if (Object.prototype.hasOwnProperty.call(params, key)) {
   
        dataSrc += `${
     key}=${
     params[key]}&`;
      }
    }
    dataSrc += `callback=${
     callbackName}`;
    return `${
     url}?${
     dataSrc}`;
  }
  return new Promise((resolve, reject) => {
   
    const scriptEle = document.createElement('script');
    scriptEle.src = generateUrl();
    document.body.appendChild(scriptEle);
    window[callbackName] = data => {
   
      resolve(data);
      document.removeChild(scriptEle);
    }
  })
}

实现深拷贝

  • 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用  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}}

(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

(3)手写实现深拷贝函数
// 深拷贝的实现
function deepCopy(object) {
   
  if (!object || typeof object !== "object") return;

  let newObject = Array.isArray(object) ? [] : {
   };

  for (let key in object) {
   
    if (object.hasOwnProperty(key)) {
   
      newObject[key] =
        typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }

  return newObject;
}

实现发布-订阅模式

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

  // 2. 添加事件方法࿰
以下是一些可能会在2023前端面试中常见的手写题: 1. 实现一个事件订阅/发布模式 ```javascript class EventEmitter { constructor() { this.events = {}; } on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push(callback); } emit(eventName, ...args) { const callbacks = this.events[eventName]; if (callbacks) { callbacks.forEach((callback) => { callback(...args); }); } } off(eventName, callback) { const callbacks = this.events[eventName]; if (callbacks) { const index = callbacks.indexOf(callback); if (index !== -1) { callbacks.splice(index, 1); } } } } ``` 2. 实现一个 Promise ```javascript class MyPromise { constructor(executor) { this.state = "pending"; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((callback) => callback()); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((callback) => callback()); } }; try { executor(resolve, reject); } catch (error) { reject(error); } } then(onFulfilled, onRejected) { const promise2 = new MyPromise((resolve, reject) => { const onFulfilledCallback = () => { try { const x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }; const onRejectedCallback = () => { try { const x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }; if (this.state === "fulfilled") { onFulfilledCallback(); } else if (this.state === "rejected") { onRejectedCallback(); } else { this.onFulfilledCallbacks.push(onFulfilledCallback); this.onRejectedCallbacks.push(onRejectedCallback); } }); return promise2; } catch(onRejected) { return this.then(null, onRejected); } } function resolvePromise(promise, x, resolve, reject) { if (promise === x) { reject(new TypeError("Chaining cycle detected")); } else if (x instanceof MyPromise) { x.then( (value) => resolve(value), (reason) => reject(reason) ); } else { resolve(x); } } ``` 3. 实现一个防抖函数 ```javascript function debounce(func, delay) { let timer; return function (...args) { if (timer) { clearTimeout(timer); } timer = setTimeout(() => { func.apply(this, args); }, delay); }; } ``` 4. 实现一个节流函数 ```javascript function throttle(func, delay) { let timer; return function (...args) { if (!timer) { timer = setTimeout(() => { func.apply(this, args); timer = null; }, delay); } }; } ``` 5. 实现一个深拷贝函数 ```javascript function deepClone(obj) { if (typeof obj !== "object" || obj === null) { return obj; } let result; if (Array.isArray(obj)) { result = []; } else { result = {}; } for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { result[key] = deepClone(obj[key]); } } return result; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值