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

这篇博客汇总了滴滴前端一面常见的手写面试题,涵盖了Promise封装AJAX、树形结构转换、原型继承、VirtualDOM处理、Promise与async/await、深拷贝、数据类型判断、reduce方法、二叉树遍历、setTimeout实现setInterval、forEach、防抖函数、字符串repeat、对象数组转树形结构、bind方法、链表结构、值交换、instanceOf以及简易Vue实现等多个核心知识点。

使用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;
}

树形结构转成列表(处理菜单)

[
    {
   
   
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
   
   
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
]
转成
[
    {
   
   
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
   
   
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
    ...
]

实现代码如下:

function treeToList(data) {
   
   
  let res = [];
  const dfs = (tree) => {
   
   
    tree.forEach((item) => {
   
   
      if (item.children) {
   
   
        dfs(item.children);
        delete item.children;
      }
      res.push(item);
    });
  };
  dfs(data);
  return res;
}

实现prototype继承

所谓的原型链继承就是让新实例的原型等于父类的实例:

//父方法
function SupperFunction(flag1){
   
   
    this.flag1 = flag1;
}

//子方法
function SubFunction(flag2){
   
   
    this.flag2 = flag2;
}

//父实例
var superInstance = new SupperFunction(true);

//子继承父
SubFunction.prototype = superInstance;

//子实例
var subInstance = new SubFunction(false);
//子调用自己和父的属性
subInstance.flag1;   // true
subInstance.flag2;   // false

将VirtualDom转化为真实DOM结构

这是当前SPA应用的核心概念之一

// vnode结构:
// {
   
   
//   tag,
//   attrs,
//   children,
// }

//Virtual DOM => DOM
function render(vnode, container) {
   
   
  container.appendChild(_render(vnode));
}
function _render(vnode) {
   
   
  // 如果是数字类型转化为字符串
  if (typeof vnode === 'number') {
   
   
    vnode = String(vnode);
  }
  // 字符串类型直接就是文本节点
  if (typeof vnode === 'string') {
   
   
    return document.createTextNode(vnode);
  }
  // 普通DOM
  const dom = document.createElement(vnode.tag);
  if (vnode.attrs) {
   
   
    // 遍历属性
    Object.keys(vnode.attrs).forEach(key => {
   
   
      const value = vnode.attrs[key];
      dom.setAttribute(key, value);
    })
  }
  // 子数组进行递归操作
  vnode.children.forEach(child => render(child, dom));
  return dom;
}

Promise

// 模拟实现Promise
// Promise利用三大手段解决回调地狱:
// 1. 回调函数延迟绑定
// 2. 返回值穿透
// 3. 错误冒泡

// 定义三种状态
const PENDING = 'PENDING';      // 进行中
const FULFILLED = 'FULFILLED';  // 已成功
const REJECTED = 'REJECTED';    // 已失败

class Promise {
   
   
  constructor(exector) {
   
   
    // 初始化状态
    this.status = PENDING;
    // 将成功、失败结果放在this上,便于then、catch访问
    this.value = undefined;
    this.reason = undefined;
    // 成功态回调函数队列
    this.onFulfilledCallbacks = [];
    // 失败态回调函数队列
    this.onRejectedCallbacks = [];

    const resolve = value => {
   
   
      // 只有进行中状态才能更改状态
      if (this.status === PENDING) {
   
   
        this.status = FULFILLED;
        this.value = value;
        // 成功态函数依次执行
        this.onFulfilledCallbacks.forEach(fn => fn(this.value));
      }
    }
    const reject = reason => {
   
   
      // 只有进行中状态才能更改状态
      if (this.status === PENDING) {
   
   
        this.status = REJECTED;
        this.reason = reason;
        // 失败态函数依次执行
        this.onRejectedCallbacks.forEach(fn => fn(this.reason))
      }
    }
    try {
   
   
      // 立即执行executor
      // 把内部的resolve和reject传入executor,用户可调用resolve和reject
      exector(resolve, reject);
    } catch(e) {
   
   
      // executor执行出错,将错误内容reject抛出去
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
   
   
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function'? onRejected :
      reason => {
   
    throw new Error(reason instanceof Error ? reason.message : reason) }
    // 保存this
    const self = this;
    return new Promise((resolve, reject) => {
   
   
      if (self.status === PENDING) {
   
   
        self.onFulfilledCallbacks.push(() => {
   
   
          // try捕获错误
          try {
   
   
            // 模拟微任务
            setTimeout(() => {
   
   
              const result = onFulfilled(self.value);
              // 分两种情况:
              // 1. 回调函数返回值是Promise,执行then操作
              // 2. 如果不是Promise,调用新Promise的resolve函数
              result instanceof Promise ? result.then(resolve, reject) : resolve(result);
            })
          } catch(e) {
   
   
            reject(e);
          }
        });
        self.onRejectedCallbacks.push(() => {
   
   
          // 以下同理
          try {
   
   
            setTimeout(() => {
   
   
              const result = onRejected(self.reason);
              // 不同点:此时是reject
              result instanceof Promise ? result.then(resolve, reject) : resolve(result);
            })
          } catch(e) {
   
   
            reject(e);
          }
        })
      } else if (self.status === FULFILLED) {
   
   
        try {
   
   
          setTimeout(() => {
   
   
            const result = 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值