2023前端必会手写面试题整理

本文整理了2023年前端面试中常见的手写代码题目,涵盖compose函数、迭代器生成、单例模式、reduce方法、redux中间件、数组扁平化、深拷贝、ES6特性、节流函数等多个方面,深入探讨了各种实现细节和技巧,适合前端开发者复习和提升技能。
摘要由CSDN通过智能技术生成

实现一个compose函数

组合多个函数,从右到左,比如:compose(f, g, h) 最终得到这个结果 (...args) => f(g(h(...args))).

题目描述:实现一个 compose 函数

// 用法如下:
function fn1(x) {
   
  return x + 1;
}
function fn2(x) {
   
  return x + 2;
}
function fn3(x) {
   
  return x + 3;
}
function fn4(x) {
   
  return x + 4;
}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a(1)); // 1+4+3+2+1=11

实现代码如下

function compose(...funcs) {
   
  if (!funcs.length) return (v) => v;

  if (funcs.length === 1) {
   
    return funcs[0]
  }

  return funcs.reduce((a, b) => {
   
    return (...args) => a(b(...args)))
  }
}

compose创建了一个从右向左执行的数据流。如果要实现从左到右的数据流,可以直接更改compose的部分代码即可实现

  • 更换Api接口:把reduce改为reduceRight
  • 交互包裹位置:把a(b(...args))改为b(a(...args))

实现迭代器生成函数

我们说迭代器对象全凭迭代器生成函数帮我们生成。在ES6中,实现一个迭代器生成函数并不是什么难事儿,因为ES6早帮我们考虑好了全套的解决方案,内置了贴心的 生成器Generator)供我们使用:

// 编写一个迭代器生成函数
function *iteratorGenerator() {
   
    yield '1号选手'
    yield '2号选手'
    yield '3号选手'
}

const iterator = iteratorGenerator()

iterator.next()
iterator.next()
iterator.next()

丢进控制台,不负众望:

写一个生成器函数并没有什么难度,但在面试的过程中,面试官往往对生成器这种语法糖背后的实现逻辑更感兴趣。下面我们要做的,不仅仅是写一个迭代器对象,而是用ES5去写一个能够生成迭代器对象的迭代器生成函数(解析在注释里):

// 定义生成器函数,入参是任意集合
function iteratorGenerator(list) {
   
    // idx记录当前访问的索引
    var idx = 0
    // len记录传入集合的长度
    var len = list.length
    return {
   
        // 自定义next方法
        next: function() {
   
            // 如果索引还没有超出集合长度,done为false
            var done = idx >= len
            // 如果done为false,则可以继续取值
            var value = !done ? list[idx++] : undefined

            // 将当前值与遍历是否完毕(done)返回
            return {
   
                done: done,
                value: value
            }
        }
    }
}

var iterator = iteratorGenerator(['1号选手', '2号选手', '3号选手'])
iterator.next()
iterator.next()
iterator.next()

此处为了记录每次遍历的位置,我们实现了一个闭包,借助自由变量来做我们的迭代过程中的“游标”。

运行一下我们自定义的迭代器,结果符合预期:

实现单例模式

核心要点: 用闭包和Proxy属性拦截

function proxy(func) {
   
    let instance;
    let handler = {
   
        constructor(target, args) {
   
            if(!instance) {
   
                instance = Reflect.constructor(fun, args);
            }
            return instance;
        }
    }
    return new Proxy(func, handler);
}

实现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++) {
   
    // 把初始值、当前值、索引、当前数组返回去。调用的时候传到函数参数中 [1,2,3,4].reduce((initVal,curr,index,arr))
    res = fn.call(null, res, arr[i], i, this); 
  }
  return res;
}

实现redux中间件

简单实现

function createStore(reducer) {
   
  let currentState
  let listeners = []

  function getState() {
   
    return currentState
  }

  function dispatch(action) {
   
    currentState = reducer(currentState, action)
    listeners.map(listener => {
   
      listener()
    })
    return action
  }

  function subscribe(cb) {
   
    listeners.push(cb)
    return () => {
   }
  }

  dispatch({
   type: 'ZZZZZZZZZZ'})

  return {
   
    getState,
    dispatch,
    subscribe
  }
}

// 应用实例如下:
function reducer(state = 0, action) {
   
  switch (action.type) {
   
    case 'ADD':
      return state + 1
    case 'MINUS':
      return state - 1
    default:
      return state
  }
}

const store = createStore(reducer)

console.log(store);
store.subscribe(() => {
   
  console.log('change');
})
console.log(store.getState());
console.log(store.dispatch({
   type: 'ADD'}));
console.log(store.getState());

2. 迷你版

export const createStore = (reducer,enhancer)=>{
   
    if(enhancer) {
   
        return enhancer(createStore)(reducer)
    }
    let currentState = {
   }
    let currentListeners = []

    const getState = ()=>currentState
    const subscribe = (listener)=>{
   
        currentListeners.push(listener)
    }
    const dispatch = action=>{
   
        currentState = reducer(currentState, action)
        currentListeners.forEach(v=>v())
        return action
    }
    dispatch({
   type:'@@INIT'})
    return {
   getState,subscribe,dispatch}
}

//中间件实现
export applyMiddleWare(...middlewares){
   
    return createStore=>...args=>{
   
        const store = createStore(...args)
        let dispatch = store.dispatch

        const midApi = {
   
            getState:store.getState,
            dispatch:...args=>dispatch(...args)
        }
        const middlewaresChain = middlewares.map(middleware=>middleware(midApi))
        dispatch = compose(...middlewaresChain)(store.dispatch)
        return {
   
            ...store,
            dispatch
        }
    }

// fn1(fn2(fn3())) 把函数嵌套依次调用
export function compose(...funcs){
   
    if(funcs.length===0){
   
        return arg=>arg
    }
    if(funs.length===1){
   
        return funs[0]
    }
    return funcs.reduce((ret,item)=>(...args)=>ret(item(...args)))
}


//bindActionCreator实现

function bindActionCreator(creator,dispatch){
   
    return ...args=>dispatch(creator(...args))
}
function bindActionCreators(creators,didpatch){
   
    //let bound = {}
    //Object.keys(creators).forEach(v=>{
   
   //     let creator = creator[v]
     //   bound[v] = bindActionCreator(creator,dispatch)
    //})
    //return bound

    return Object.keys(creators).reduce((ret,item)=>{
   
        ret[item] = bindActionCreator(creators[item],dispatch)
        return ret
    },{
   })
}

实现数组扁平化flat方法

题目描述: 实现一个方法使多维数组变成一维数组

let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);

第0种处理:直接的调用

arr_flat = arr.flat(Infinity);

第一种处理

ary = str.replace(/(\[|\])/g, '').split(',');

第二种处理

str = str.replace(/(\[\]))/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);

第三种处理:递归处理

let result = [];
let fn = function(ary) {
   
  for(let i = 0; i < ary.length; i++) }{
   
    let item = ary[i];
    if (Array.isArray(ary[i])){
   
      fn(item);
    } else {
   
      result.push(item);
    }
  }
}

第四种处理:用 reduce 实现数组的 flat 方法

function flatten(ary) {
   
    return ary.reduce((pre, cur) => {
   
        return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
    }, []);
}
let ary = [1, 2, [3, 4], [5, [6, 7]]]
console.log(flatten(ary))

第五种处理:能用迭代的思路去实现

function flatten(arr) {
   
  if (!arr.length) return;
  while (arr.some((item) => Array.isArray(item))) {
   
    arr = [].concat(...arr);
  }
  return arr;
}
// console.log(flatten([1, 2, [1, [2, 3, [4, 5, [6]]]]]));

第六种处理:扩展运算符

while (ary.some(Array.isArray)) {
   
  ary = [].concat(...ary);
}

参考 前端进阶面试题详细解答

实现深拷贝

简洁版本

简单版:

const newObj = JSON.parse(JSON.stringify(oldObj));

局限性:

  • 他无法实现对函数 、RegExp等特殊对象的克隆
  • 会抛弃对象的constructor,所有的构造函数会指向Object
  • 对象有循环引用,会报错

面试简版

function deepClone(obj) {
   
    // 如果是 值类型 或 null,则直接return
    if(typeof obj !== 'object' || obj === null) {
   
      return obj
    }

    // 定义结果对象
    let copy = {
   }

    // 如果对象是数组,则定义结果数组
    if(obj
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值