# 2021年核心面试题详解

# 1、相关面试题

# 1、实现lodash中的get函数:get(data, 'a[3].b')

//_.get(object, path, [defaultValue])
// 根据 object对象的path路径获取值。 如果解析 value 是 undefined 会以 defaultValue 取代。
// 以免直接使用a[0].c时因a[0]为undefind造成undefind.c报错
// 使用方法:let a = {b:{c:'name'}}
// get(a, 'b[c]', 5)
const get = (data, path, defaultValue = void 0) => {
  const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.');
  // paths => ['a', '3', 'b'];
  let result = data;
  for (const path of paths) {
    result = Object(data)[path];  // edge case data -> null 
    if (result == null || result == undefined) {
      return defaultValue;
    }
  }
  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2、实现 add(1)(2)(3) 柯里化函数

参考资料:

1、前端经典面试题解密-add(1)(2)(3)(4) == 10到底是个啥? (opens new window)

2、js高阶函数应用—函数柯里化和反柯里化 (opens new window)

3、js高阶函数应用—函数柯里化和反柯里化(二) (opens new window)

4、函数的length属性 (opens new window)

函数柯里化概念: 柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。本题实现将add(1, 2, 3)函数柯里化,即add(1)(2)(3)

length 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。与之对比的是,arguments.length 是函数被调用时实际传参的个数。

//粗暴版本
function add (a) {
    return function (b) {
        return function (c) {
            return a + b + c;
        }
    }
}
console.log(add(1)(2)(3)); // 6

//参数长度固定
function curry(fn){
    let len = fn.length 
    let args = []
    return function _c(...newArgs){
        args = [...args, ...newArgs] //合并参数
        if(args.length < len){
            return _c
        }else{
            let res = fn.apply(this, args.slice(0, len))
            args = [] //清空数组,防止下次调用时影响
            return res
        }
    }
}

const add = (a, b, c) => a + b + c;
const curryAdd = curry(add);
console.log(curryAdd(1)(2)(3)); // 6
console.log(curryAdd(1, 2)(3)); // 6
console.log(curryAdd(1)(2, 3)); // 6

//参数长度不固定
function add(...args) {
    return args.reduce((a, b) => a + b)
}
 
function currying (fn) {
    let args = []
    return function _c (...newArgs) {
        if (newArgs.length) {
            args = [
                ...args,
                ...newArgs
            ]
            return _c
        } else {
            let res = fn.apply(this, args)
            args = [] //保证再次调用时清空
            return res
        }
    }
}
 
let addCurry = currying(add)
console.log(addCurry(1)(2)(3)(4, 5)())  //15
console.log(addCurry(1)(2)(3, 4, 5)())  //15
console.log(addCurry(1)(2, 3, 4, 5)())  //15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# 3、手写await和async

参考:手写async await的最简实现(20行) (opens new window)

function fn(s){
    return new Promise(function(resolve){
        setTimeout(() => {
            resolve(s)
        }, s)
    })
}
//用generator和yield实现await
function * gFn(){
    console.time('t1')
    let t1 = yield fn(1000)
    console.timeEnd('t1')
    console.time('t2')
    let t2 = yield fn(2000)
    console.timeEnd('t2')
    return 'success'
}

function asyncToGenerator(fn){
    let generator = fn()
    return new Promise(function(resolve, reject){
        let step = function(key, args){
            let res = null
            try{
                res = generator[key](args)
            }catch(e){
                reject(e)
            }
            let {value, done} = res
            if(done){
                resolve(value)
            }else{
                Promise.resolve(value).then((value) => {
                    step('next', value)
                }).catch((e)=>{
                    step('throw', e)
                })
            }
        }
        step('next')
    })
}

let t = asyncToGenerator(gFn)
t.then(r => {
    console.log('r', r)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 4、如何优化 node 镜像制作

  • DOCKER_BUILDKIT 查看 dockerfile instruction 耗时
  • FROM YOUR_OLD_DOCK 基于历史最新的业务镜像构建
  • COPY 等指令,充分利用 cache
  • 优化 OS 大小,alpine
  • npm i --only=production 移除 devDependencies
  • 抽出来放 CDN
  • ...

关注一下 devOps

# 5、webpack 热更新原理

/**

- 内存文件系统
- |
- 读写
- |
- webpack compile     - watch -      代码
- |                               |
- ----------------------------change
- |
- server(websocket) --> manifest(hash.hot-update.json / hash.hot-update.js) | hash & chunk
- |
- |
- Browser: hotDownloadManifest(拉 manifest)
- |
- | get hash chunkid
- |
- hotDownloadUpdateChunk(拉 chunkjs 文件)
- |
- |
- hotAddUpdateChunk(update the chunk)
- |
- |
- hotUpdateDownloaded
*/

// homework: 思考如何让传统的 webpack hmr 更快?
// 思路:
// 1. 为什么慢?
// 2. 跟模块模式有关联吗? ESM
// 3. 想想 vite?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 2、个人简历

  • 简历的标题一定要带上名字(比如:张三-本-4年-高级前端开发工程师.pdf)

  • 强烈建议不要搞些花哨的网页版简历

  • 简历最好不要超过 1 页,至多不超过 2 页

  • 简历三大原则:清晰,简短,必要

  • 简历不是一成不变,可针对 JD 定制,指哪儿打哪儿

# 1、个人介绍

联系方式,意向工作地,岗位诉求,学校学历

# 2、个人技术部分

一、不建议写个人技术列表,如果非要写,切记以下内容不建议写上去:

1、其实你不是很精深的知识领域,比如只是配置过 webpack

2、常识性的知识技能不要写了,比如 git 操作等

3、过时已久的也不建议写了,比如你写一个熟练使用 backbone

二、注意以下措辞的区别:

1、了解:表示你听说过这个概念,甚至了解与此概念有关的基本原理

2、熟悉:表示你通过 Demo 的形式实践过某个技术,或做过一两个与该技术有关的项目,但缺乏沉

3、熟练掌握:表示你在工业级环境下,通过数个项目的实践已经掌握了某种技术的核心原理,并能够

灵活地应用在开发中

4、精通:表示你通过很多次的项目实践和潜心研究,已经对某种技术的原理和应用掌握到近乎尽善尽

美的程度

三、加分项:

1、社区博客文章,高转发高点赞那种,加分

2、Github 一片绿油油的,主导/参与维护一些社区知名项目, PR 内容都是逻辑代码级别的,不是

文档

3、除前端常用技术栈,还掌握了一些其他技能,后端语言等

# 3、个人项目(60%)

1、bad case:我在该项目中完成了 XXX,YYY 需求,运用了 a,b,c 技术。

2、good case: XXX 项目出现 XXX 问题,我作为 XXX,负责其中的 XXX 部分,我通过 XXX 方式(或技术方案) ,成功解决了该问题,使 XXX 提高了 XXX,XXX 增长了 XXX

# 3、面试流程

1、校招(45min)

  • 自我介绍1-3min
  • 简历项目10min
  • 算法easy/mid
  • 算法easy/mid

2、社招(1h)

  • 1年以下,关注代码能力和算法,和校招类似,对项目经验没太多要求
  • 1-3年,前端基础 + 代码能力 + 技术的广度
  • 3-5年,项目经历,有一个精通的知识点,canvas/node/性能优化 + 技术的深度,代码 + 算法(分数占比不高)
Last Updated: 11/11/2021, 11:04:53 AM