美文网首页
记大厂笔试题(前端相关)

记大厂笔试题(前端相关)

作者: pansly | 来源:发表于2021-07-25 10:22 被阅读0次

    阿里系列

    Q1: 请实现一个 find 函数,功能等同于 document.getElementById 。

    解析: 难点在于如何获取入口节点。 假如你知道入口节点,例如root节点,那递归就行了。

    考察点: 因为平常绝大多数前端童鞋不是用React就是用Vue。 很少有人能对DOM的一些原生操作做到熟悉。

    // 方法1,手动写递归。
    const nodes = document.body; // 万物起点,获取 body DOM
    
    function getId(node,nodeIdMap){
        if(node){ 
            if(node.id) nodeIdMap[node.id] = node; // 如果 id 属性存在,则把该DOM存入 Map
            const children = node.children;
            for(let i = 0 ; i < children.length; i++){ // 循环
                getId(children[i],nodeIdMap) // + 递归
            }
        }
        return nodeIdMap
    }
    
    const ids = getId(nodes,{})
    
    const findId = (id) => ids[id]
    // 大家可以拿这段代码试一下。
    
    // 方法2, 借助 document.createNodeIterator 可以去MDN上查看该API
    const f =  (id)=> document.createNodeIterator(
        document.body,
        NodeFilter.SHOW_ALL,
        {
          acceptNode(node) {
            return node.id === id? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
          }
        }
    );
    
    f('anyId').nextNode(); // 即可获取 任意ID 的DOM
    

    Q2: 实现 一个 find 。

    测试数据为:

    var data = [
        {title: 't1', userId: '10086', name: 'Jay'},
        {title: 't2', userId: '10087', name: 'Tom1'},
        {title: 't3', userId: '10088', name: 'Tina2'},
    ]
    

    希望如此调用:

    find(data).where({
    name: /\d$/,
    }).orderBy('userId','desc');
    

    得到结果是:

    [
        {title: 't3', userId: '10088', name: 'Tina2'},
        {title: 't2', userId: '10087', name: 'Tom1'}
    ]
    

    考察点: 这其实是一条sql语句。 就像 select title, userId,name from 表名 where 条件 order by userId desc

    分析: 咋看上去有点像链式调用? 但这是坑,面试官绝对不是想让你写个链式调用。(因为我就是这么干的,然后被怼了。)

    // 先写个类
    
    class FindData {
    
      constructor(data) {
        this.data = data;
      }
    
      where(regs) {
        const keys = Object.keys(regs);
        const d = this.data.filter((item) => {
          let r = true;
          for (let i = 0; i < keys.length; i++) {
            const current = item[keys[i]];
            if (!(current && regs[keys[i]].test(current))) {
              r = false;
            }
          }
          return r;
        })
        return new FindData(d);
      }
    
      orderBy(name, sort) {
        let fn;
        if(sort === 'desc'){
          fn = (a,b)=> b[name] - a[name]
        }else if(sort === 'asc'){
          fn = (a,b)=> a[name] - b[name]
        }
        return this.data.sort(fn)
      }
    }
    
    var find = (d) =>  new FindData(d);
    
    find(data).where({name: /\d$/}).orderBy('userId','desc');
    

    Q3: 将字符串转成千分位。例如 '12345678' 转化成千分位是 '12,345,678'.

    考察点: 正则。 如果你写的不是正则,那也一定会让你用正则写。零宽断言了解一下

    // 零宽断言
    str.replace(/\d{1,3}(?=(\d{3})+$)/g,function(s){
        return s + ','
    }) 
    

    小结一下

    阿里的笔试题不难。说真的,并不难。 但需要你非常深厚的基础。往往你熟悉的不会考你,你不知道的、少用的、模糊的一定会考你。 反正我是跪在了正则的零宽断言上了。 😭😭😭😭

    字节系列

    Q1: 实现数组扁平化 flat 方法。

    var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
    
    function flattenDeep(arr1) {
       return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
    }
    flattenDeep(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
    

    Q2: 实现对象扁平化 flat 方法。

    function flat(obj, key = "", res = {}, isArray = false) {
      for (let [k, v] of Object.entries(obj)) {
        if (Array.isArray(v)) {
          let tmp = isArray ? key + "[" + k + "]" : key + k;
          flat(v, tmp, res, true)
        } else if (typeof v === "object") {
          let tmp = isArray ? key + "[" + k + "]." : key + k + ".";
          flat(v, tmp, res)
        } else {
          let tmp = isArray ? key + "[" + k + "]" : key + k;
          res[tmp] = v
        }
      }
      return res
    }
    

    Q3: 实现 applyMiddleWare, 达到如下示例

    function rawMethod(a){
        return a + 1;
    }
    
    function middleware1(next){
        return function(a){
            return next(a) + 1;
        }
    }
    function middleware2(next){
        return function(a){
            return next(a) + 1;
        }
    }
    
    function middleware3(next){
        return function(a){
            return next(a) + 1;
        }
    }
    
    var newMethod = applyMiddleWare(rawMethod, middleware3, middleware2);
    var x = newMethod(1); 
    // 要求调用顺序: middleware2 -> middleware3 -> middleware. 结果 x = 3
    var newMethod2 = applyMiddleWare(newMethod, middleware1);
    var y = newMethod2(10); 
    // 要求调用顺序: middleware1 -> middleware2 -> middleware3 -> middleware. 结果 y = 131
    

    考察点: 看名称就知道,这特么是要你手写一个 Redux 的 applyMiddleWare 方法啊! 这方法干嘛的?合并中间件插件调用的好伐。。

    分析: 需要 compose 方法。

    function compose(...funcs) {
        if (funcs.length === 0) {
            return args => args; //如果没有要组合的函数,则返回的函数原封不动的返回参数
        } else if (funcs.length === 1) {
            //要组合的函数只有一个
            return funcs[0];
        }
    
        // redux官方写法,在下面附有步骤分析,非常巧妙
        // return funcs.reduce((a, b) => (...args) => a(b(...args)));
    
        // 这是可读性好一些的写法,和上面代码功能一样
        return function (...args) {
            let lastReturn = null; // 记录上一个函数返回的值
            for (let i = funcs.length - 1; i >= 0; i--) {
                const func = funcs[i];
                if (i === funcs.length - 1) {
                    lastReturn = func(...args);
                } else {
                    lastReturn = func(lastReturn);
                }
            }
            return lastReturn;
        }
    
    复制代码
    
    
    export function applyMiddleware(...middlewares) {
        // 接收creatStore方法
        return function (createStore) {
            // 接收reducer和默认状态 ,用于创建仓库
            return function (reducer, defaultState) {
                //创建仓库
                const store = createStore(reducer, defaultState);
                let dispatch = () => { throw new Error("目前还不能使用dispatch") };
    
                const simpleStore = {
                    getState: store.getState,
                    // 这里不能写成dispatch: dispatch,否则一直是上面那个报错的dispatch
                    // 也不能写成store.dispatch,否则一直是最原始的dispatch
                    // 写成函数形式是为了保证引用地址一致
                    dispatch: (...args) => dispatch(...args)
                }
    
                //给dispatch赋值
                //根据中间件数组,得到一个dispatch创建函数的数组
                const dispatchProducers = middlewares.map(mid => mid(simpleStore));
                // 在完成dispatch前,是调用不了simpleStore中的dispatch的,只有等包装完
                dispatch = compose(...dispatchProducers)(store.dispatch)
                return {
                    ...store,
                    dispatch,
                }
            }
        }
    
    、、
    复制代码
    

    Q4 : 现有一台阶,共m 层。 一只青蛙每次可以跳任意层数(1 - n)的台阶,。 请问青蛙跳到第m层台阶有多种跳法?

    考察点: 经典 青蛙跳台阶问题。 力扣上也有原题。 这道变态版 青蛙跳台阶 力扣上也有原题。我这里就不写了,因为知道答案的你可能会觉得有点侮辱智商😂😂😂😂

    小结一下

    字节的笔试题风格也很明显,喜欢考一下常用API的实现。 算法题,也都能从力扣上面找到相近的题。 如果说阿里是考你变态的基础,那字节就是考你变态的脑神经了。

    腾讯系列

    Q1: 写一个双链表循环树。

    我跪了。 跪稳了。 求各位大佬指点。 (我曾经被问到过如何写一个单链表循环树,现在特么升级成双链表循环树了。😭😭😭😭😭😭😭)

    Q2:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组[3,4,5,1,2][1,2,3,4,5]的一个旋转,该数组的最小值为 1。

    考察点: 二分查找。 力扣原题解析

    累了。写不动了。。 直接来小结一下

    小结一下

    腾讯笔试题和字节有点像,不过腾讯更偏向于数据结构算法思想。 需要你对一些常见的额数据结构例如 链表 的操作有比较深的理解。

    相关文章

      网友评论

          本文标题:记大厂笔试题(前端相关)

          本文链接:https://www.haomeiwen.com/subject/vgbbmltx.html