共计 6134 个字符,预计需要花费 16 分钟才能阅读完成。
前言
明天给大家分享一个字节跳动系公司——石墨文档的面经吧!废话不多说,先看题目!
题目
一面
- 1、[’10’, ’10’, ’10’, ’10’, ’10’].map(parseInt) 的输入值是什么?
- 2、你们当初的技术栈是什么?为什么要应用 ts?
- 3、setTimeout 的执行过程(事件循环,同步、异步)?
- 4、对 Promise 的了解,与 async、await 的区别,async、await 是怎么实现的?
- 5、解释 requestAnimationFrame/requestIdleCallback,别离有什么用?
- 6、react 性能优化?
- 7、说说对 flex 的了解?
- 8、回流、重绘是什么?如何缩小回流和重绘?
- 10、怎么寻找 react 页面卡顿的起因?
-
11、编程题:实现一个对象的 flatten 办法,如下:
const obj = {a: { b: 1, c: 2, d: { e: 5} }, b: [1, 3, {a: 2, b: 3}], c: 3 }
flatten(obj){} 后果返回如下:
// {// 'a.b': 1, // 'a.c': 2, // 'a.d.e': 5, // 'b[0]': 1, // 'b[1]': 3, // 'b[2].a': 2, // 'b[2].b': 3 // c: 3 // }
二面
- 1、说说对 web worker 的了解
- 2、service worker 和强缓存相比,有哪些劣势?
- 3、说说对堆栈溢出的了解
- 4、position 中的 sticky 是什么,还有哪些其余的?
- 5、ts 中,any 和 unknown 别离是什么意思?泛型怎么应用?
- 6、bind 有什么用?间断多个 bind,最初 this 指向是什么?
- 7、webpack 的 plugin 怎么实现?
- 8、编程题:
现已知一个字符串是由正整数和加减乘除四个运算符 (+ – /) 组成。例如存在字符串 const str = ’11+2-34+5/24+10/5’,当初须要将高优先级运算,用小括号包裹起来,例如后果为 ’11+2-(34)+(5/2*4)+(10/5)’。留神可能会呈现间断的乘除运算,须要包裹到一起。请用 javascript 实现这一过程
三面
-
1、手写体:应用 TypeScript 实现一个 get 函数来获取它的属性值
const data = {name: 'tom', age: 18, address: 'xxx'}
- 2、ts 中的 any、unknown 的区别
- 3、有用过 ts 中的 keyof 吗?
- 4、for in/for of 的区别?
- 5、Promise 值穿透?
解答
一面
1、[’10’, ’10’, ’10’, ’10’, ’10’].map(parseInt) 的输入值是什么?
可转化为:
['10', '10', '10', '10', '10'].map((num, index) => parseInt(num, index))
// [10, NaN, 2, 3, 4]
'10' 0 -> 10: 进制为 0,则默认 10 进制
'10' 1 -> NaN: 1 进制不存在
'10' 2 -> 2: 2 * 1 + 2 * 0
'10' 3 -> 3: 3 * 1 + 3 * 0
'10' 4 -> 4: 4 * 1 + 4 * 0
2、你们当初的技术栈是什么?为什么要应用 ts?
typescript
是 JavaScript
的超集,它实质其实是是在 JavaScript
上增加了 可选动态类型
和 基于类的面向对象编程
typescript 的特点
- 能够在编译期间发现并纠正错误
- 进步可维护性
- 进步协同开发的效率
- 反对强类型、接口、泛型、模块
3、setTimeout 的执行过程(事件循环,同步、异步)?
事件循环
- 1、执行同步代码
- 2、
1
中生成的微工作
先执行 - 3、
1
中生产的宏工作
再执行
同步
简略来说就是:排队。代码有前后程序,必须依照程序去执行
异步
异步工作能够不阻塞前面的代码执行,而是能够同时进行,并且执行完后会有一个异步的回调。
想起一个故事能够很好的解释 同步
和 异步
- 同步:你打电话去书店借书,老板接电话时让你等着,他去找书,你只能守着电话干等着
- 异步:你打电话去书店借书,老板接电话后说等他找到书再打回给你,而后挂电话了,这段找书的工夫你能够自在流动
4、对 Promise 的了解,与 async、await 的区别,async、await 是怎么实现的?
Promise 的了解
顾名思义, Promise
就是 承诺
的意思,体现在了 Promise 的状态一旦扭转则不会再变了,如果状态为 fulfilled
则执行 then
,如果状态为 rejected
则执行 catch
,Promise 也反对 链式调用
。我感觉 Promise 最大的用途就是 解决了回调天堂,进步了代码的可读性
。罕用的办法有 resolve、reject、then、catch、race、all、allSettled、any、finally
async await async/await
的作用是 用同步的形式执行异步的操作
,它的实现原理,我集体了解就是利用了 Promise
的一直嵌套,再加上 generator 函数
的步骤管制,实现了按程序执行异步操作的成果
补充:async 函数返回的是一个 Promise
5、解释 requestAnimationFrame/requestIdleCallback,别离有什么用?
-
requestAnimationFrame:
- 个别距离是
16ms
,因为大部分电脑都是 每秒 60 帧,所以1000 / 60 ≈ 16ms
- 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中实现,且工夫距离紧紧追随浏览器的刷新频率
- 如果有暗藏或不可见的元素,将不会进行重绘或回流,缩小了 cpu、gpu 的内存使用量
- 如需勾销则应用
cancelAnimationFrame
- 个别距离是
- requestIdleCallback:我的了解就是找浏览器闲暇工夫去执行传入的回调,具体也没在我的项目中应用过
6、react 性能优化?
7、说说对 flex 的了解?
弹性布局
,设置了 display: flex
的盒子为 弹性盒子
,子元素会主动变成 弹性我的项目
,盒子有一根主轴,默认是程度,并且有一个穿插轴(跟主轴垂直)。
弹性盒子的款式:
- flex-direction:定义主轴方向
- flex-wrap:是否容许换行
- flex-flow:flex-direction 和 flex-wrap 的简写
- justify-content:主轴方向上的对齐形式
- align-items:穿插轴方向的对齐形式
- align-content:多根轴线的对齐形式
弹性我的项目的款式:
- order:定义我的项目的排列程序,数值越小排列越靠前,默认 0
- flex-grow:定义我的项目的放大比例,默认为
0
- flex-shrink:定义我的项目的放大比例,默认为 1
- flex-basis:定义了在调配多余空间之前,我的项目占据的主轴空间,默认 auto
- flex:flex-grow、flex-shrink、flex-basis 的简写
-
align-self:容许单个我的项目设置不同的穿插轴对齐形式
8、回流、重绘是什么?如何缩小回流和重绘?
重绘回流
- 回流:尺寸、布局扭转时,引起页面从新构建
- 重绘:元素外观、格调扭转时,不影响布局,则为重绘
- 区别:回流肯定引起重绘,重绘不肯定引起回流
- 浏览器帮忙:浏览器保护一个队列,把所有引起回流、重绘的操作放入这个队列,等队列到了肯定数量或者到了肯定的工夫距离,浏览器就会清空队列,进行批量解决。
防止重绘、回流
- 1、批量批改 DOM 或者款式
- 2、简单动画应用相对定位让它脱离文档流,不然会印日分元素或后续元素的频繁回流
-
3、GPU 减速:transform、opacity、filters、will-change 等款式
9、判断一个对象是数组的办法?
- Object.prototype.toString.call(xxx)
- Array.isArray(xxx)
-
xxx instaceOf Array
10、怎么寻找 react 页面卡顿的起因?
11、编程题:实现一个对象的 flatten 办法,如下:
const obj = {a: { b: 1, c: 2, d: { e: 5} }, b: [1, 3, {a: 2, b: 3}], c: 3 }
flatten(obj){} 后果返回如下:
// {// 'a.b': 1, // 'a.c': 2, // 'a.d.e': 5, // 'b[0]': 1, // 'b[1]': 3, // 'b[2].a': 2, // 'b[2].b': 3 // c: 3 // }
解题
const isObject = (target) => {return typeof target === 'object' && target !== null} const flatten = (obj) => {if (!isObject) return const res = {} const dfs = (cur, prefix) => {if (isObject(cur)) {if (Array.isArray(cur)) {cur.forEach((item, index) => dfs(item, `${prefix}[${index}]`)) } else {for(let key in cur) {dfs(cur[key], `${prefix}${prefix ? '.' : ''}${key}`) } } } else {res[prefix] = cur } } dfs(obj, '') return res }
二面
1、说说对 web worker 的了解
- 1、开启一个子线程,并在此子线程进行一些大数据处理或者耗时的操作
- 2、应用
postMessage
和onmessage
,实现主线程和子线程之间的通信 - 3、应用
onerror
监听子线程挂了没 - 4、
web worker
并没有扭转 JavaScript 单线程的事实
2、service worker 和强缓存相比,有哪些劣势?
service 缓存没用过。。
3、说说对堆栈溢出的了解?
常见的状况产生在 大数量递归
或 死循环
时,就会造成 栈溢出
,因为每次执行代码都须要调配肯定空间的内存,以上两种状况都会使执行空间超出最大限度,从而报错
4、position 中的 sticky 是什么,还有哪些其余的?
- static:默认
- relative:绝对定位,绝对于本身定位
- absolute:相对定位,绝对于非 static 的第一个祖宗元素定位
- fixed:绝对于浏览器窗口进行定位
- inherit:规定应该从父元素继承 position 属性的值
- sticky:吸顶定位
5、ts 中,any 和 unknown 别离是什么意思?泛型怎么应用?
- any:变量如果是 any 类型,绕过所有类型查看,间接可应用
- unknown:变量如果是 unknow 类型,须要判断完是什么类型之后能力应用
6、bind 有什么用?间断多个 bind,最初 this 指向是什么?
bind
的作用是扭转函数执行的指向,且不会立刻执行,而是返回一个新的函数,能够自主调用这个函数的执行(此函数不可当做构造函数)
间断多个 bind 之后 this 指向始终指向第一个
7、webpack 的 plugin 怎么实现?
一个 plugin 就是一个类,类里有一个 apply 办法
,每次打包时都会调用这个 apply,而这个 apply 办法承受一个参数对象,其中有一个 plugin
办法,此办法中有许多 钩子函数
,且能够决定动态文件的生成,批改等等
8、编程题:
现已知一个字符串是由正整数和加减乘除四个运算符 (+ – /) 组成。例如存在字符串 const str = ’11+2-34+5/24+10/5’,当初须要将高优先级运算,用小括号包裹起来,例如后果为 ’11+2-(34)+(5/2*4)+(10/5)’。留神可能会呈现间断的乘除运算,须要包裹到一起。请用 javascript 实现这一过程
解答
我比拟菜,用的办法也是长期想进去的,没有优化,大家将就着看吧:
const checkType = (str) => {if (['*', '/'].includes(str)) return 'high'
if (['+', '-'].includes(str)) return 'low'
return 'number'
}
const addBrackets = (formula) => {const strs = formula.split('')
let i = 0, j = 1, high = false, res = []
while(j < strs.length) {const jType = checkType(strs[j])
if (jType === 'low' && !high) {
i = ++j
j++
}else if (jType === 'low' && high) {res.push(j++)
i = j++
high = false
}else if (jType === 'high') {
j++
!high && res.push(i)
high = true
}else {j++}
}
if (high) res.push(strs.length)
let add = 0
for(let i = 0; i < res.length; i++) {const index = res[i]
strs.splice(index + add, 0, add % 2 ? ')' : '(')
add++
}
return strs.join('')
}
三面
1、手写体:应用 TypeScript 实现一个 get 函数来获取它的属性值
const data = {name: 'tom', age: 18, address: 'xxx'}
解答:
const get = <T extends object, K extends keyof T>(obj: T, key: K): T[K] => {return obj[key]
}
2、ts 中的 any、unknown 的区别?
- any:变量如果是 any 类型,绕过所有类型查看,间接可应用
-
unknown:变量如果是 unknow 类型,须要判断完是什么类型之后能力应用
3、有用过 ts 中的 keyof 吗?
将一个 interface 的所有 key,汇聚成一个联结类型,能够用来对传入 key 的限度,比方:
interface Target { name: string, age: number } const fn = (obj: Target, key: keyof Target) => {} const obj: Target = {name: 'sunshine', age: 18} fn(obj, name) // 胜利 fn(obj, age) // 胜利 fn(obj, height) // 报错
4、for in/for of 的区别?
- for in:遍历对象的 key 或者数组的索引
-
for of:遍历可迭代对象的值,如数组、Set
5、Promise 值穿透
then 或 catch 没有传入函数的话,会产生值穿透,原理是 Promise 外部检测如果传入的是非函数,则会拿上一次的后果包装成一个返回 Promise 的函数,达到穿透成果
例如:
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(function(result){console.log(result) // foo
})
然而如果传入的是函数的话:
Promise.resolve('foo')
.then(() => Promise.resolve('bar'))
.then(function(result){console.log(result) // bar
})
结语
因为自己 React 太菜,所以不敢答题无关 React 的题目
我是林三心,一个热心的前端菜鸟程序员。如果你上进,喜爱前端,想学习前端,那咱们能够交朋友,一起摸鱼哈哈,摸鱼群,加我请备注【思否】