常见的手写代码的面试题实现及解答

100 阅读3分钟

防抖

用户在短时间内多次触发,只会最终触发一次

const debounce = (fn, interval) => {
  // 闭包中缓存timeout返回的key
	let timeoutKey = undefined;
  // 返回防抖处理后的函数
  return (...args) => {
    // 清空上次未处理的timeout函数
    // 防抖的核心,在间隔时间内,连续调用多次的时候,每次都会取消上次待执行的函数
    clearTimeout(timeoutKey);
    timeoutKey = setTimeout(() => {
      // 别忘了把参数回传给fn
      fn(...args);
    }, interval)
  }
}

节流

用户在短时间内多次触发,会按照设定好的频率触发

const throttle = (fn, interval) => {
  // 闭包中缓存timeout返回的key
	let timeoutKey = undefined;
  // 返回节流处理后的函数
  return (...args) => {
    // 如果已经有待执行的了,就不需要再执行了,直接return;
    if(timeoutKey){
        return;
    }
    timeoutKey = setTimeout(() => {
      // 别忘了把参数回传给fn
      fn(...args);
      // 函数执行完之后,记得把timeoutKey清空
      timeoutKey = undefined;
    }, interval)
  }
}

手写Promise.all

// 覆盖原有构造函数中的静态方法
Promise.all = (promiseArr) => {
  // 储存成功结果
  let resolveList = [];
  // 储存成功数量
  let resolveNum = 0;
  // promiseAll接受的promise数组长度
	let promiseAllLength = promiseArr.length
  // 返回一个新的promise,全部成功后调用resolve,有一个失败就直接调用reject
  return new Promise((resolve, reject) => {
    // 检查是否全部成功
    function checkPromiseAllResolve(){
      return resolveNum === promiseAllLength
    }
    // 遍历传进来的promiseArr
  	promiseArr.forEach((item, index) => {
      // 下面这行主要是防止promiseArr中传递非Promise实例
      // 用Promise.resolve方法可以 将非promise实例转化为promise实例,能理解就用
      // Promise.resolve(item).then(res=>{

      item.then(res=>{
        // 成功返回储存在resolveList
        resolveList[index] = res;
        // 成功数量+1
        resolveNum++
        // 检查是否全部成功
        if(checkPromiseAllResolve()){
          // 如果全部成功了,把成功结果传出去
          resolve(resolveList)
        }
      }).catch(err => {
        // 任何一个失败了,都直接进行reject处理
        reject(err);
      })
    })
  })
}

模糊搜索

// 定义列表每一项的数据结构
type searchItem = {
	value: string;
  id: number;
}

// 模糊搜索的类型
type FuzzySearch = (list: searchItem[], searchVal: string) => searchItem[]

// 入参 searchItem[]和搜索的value,返回值也同样是数组
const fuzzySearch = (list, searchVal) => {
  // 创建一个新的列表,储存筛选后的数据
  const newList = [];
	list.forEach( (item) => {
  	const { value, id } = item;
    // 这里的搜索规则是只要包含就可以
    // 具体规则由当时面试官定制,复杂一些的可能需要用到正则去匹配
    if(
      value.includes(searchVal) ||
      id.includes(searchVal) ||
    ){
      // 符合条件的储存在这里
      newList.push(item);
    }
  });
  // 把筛选后的数组返回出去
  return newList
}

React TodoList

import React, { useState } from 'react';
import ReactDom from 'react-dom';

// 默认数据
const defaultData = {
  title: '',
  isDone: false,
  time: +new Date()
}

const TodoList = function () {
  // 储存代办列表
  const [dataList, setDataList] = useState([]);
  // 储存输入框输入信息
  const [inputVal, setInputVal] = useState('');
  
  // 向列表中添加
  const addList = () => {
    dataList.push({
      ...defaultData,
      title: inputVal,
      time: +new Date,
    });
    setDataList([...dataList]);

    // 清空输入框
    setInputVal('')
  }

  const onKeyDown = (e) => {
    // 按下回车键
    if(e.keyCode === 13){
     addList();
    }
  }


  const onChange = (e) => {
    setInputVal(e.target.value);
  }

  const onClickItem = (item, index) => {
    // 切换已完成或未完成状态
    item.isDone = !item.isDone;
    dataList[index] = item;
    setDataList([...dataList])
  }

  const renderList = () => {
    // 记录已完成列表
    const doneList = [];
    // 未完成列表
    const todoList = [];

    // 遍历数据源
    dataList.forEach((item, index) => {
      const { isDone, title, time } = item;
      // 如果已完成push到已完成列表中
      if(isDone){
        doneList.push(<div key={time} onClick={() => onClickItem(item, index)}>{title}</div>)
        return;
      }
      todoList.push(<div key={time} onClick={() => onClickItem(item, index)}>{title}</div>)
    })
    
    // 已完成列表和未完成列表分别渲染。
    return (
      <>
        <div className="todoList">{todoList}</div>
        <div className="doneList">{doneList}</div>
      </>
    )
  }

  return (
    <div>
      <div>
        <input value={inputVal} onChange={onChange} onKeyDown={onKeyDown} />
        <button onClick={addList}>添加</button>
      </div>
      <div>
        {renderList()}
      </div>
    </div>
  );
};

ReactDom.render(<TodoList />, document.getElementById('app'));
      

有趣的面试题 - 让 a == 1 && a == 2 && a == 3 成立

a为对象,重写valueOf方法实现


let a = {
  value: 1,
  valueOf: function() {
    return this.value++;
  }
};

console.log(a == 1 && a == 2 && a == 3); // 输出 true

a为数组,重写toString方法实现

let a = [1, 2, 3];

a.toString = a.shift;

console.log(a == 1 && a == 2 && a == 3); // 输出 true

Proxy方法实现

let a = new Proxy({}, {
  get: function(target, key) {
    return key === 'valueOf' ? () => a.value++ : a[key];
  }
});

a.value = 1;

console.log(a == 1 && a == 2 && a == 3); // 输出 true

使用 ES6 的 Symbol.toPrimitive 方法实现

let a = {
  [Symbol.toPrimitive]: (function(i) {
    return function() { return ++i; }
  })(0)
};

console.log(a == 1 && a == 2 && a == 3); // 输出 true

Label Value 对齐

效果: image.png 代码: