day1
2021.7.2 一面
项目聊了半个小时
1.讲讲浏览器缓存,为什么要设计Etag和modify两种
http缓存分为:
强缓存
- 强缓存通过 Expires 和Cache-control 控制
- 因为expires有服务器和浏览器时间不同步问题,expires是绝对时间,cache-control是相对时间
协商缓存
- 协商缓存通过 Last-modify 和 Etag 控制
- 因为 last-modify 有精度问题到秒,etag 没有精度问题,只要资源改变,etag值就会改变
2.http1.1 http2.0 https
http基础
- get/post/head,head只响应头部
- options是http支持的方法
http1:
- 短连接
- 支持缓存
http2
二进制分帧
多路复用
头部压缩
https
HTTPS采用的是混合方式加密。 服务器拿到这个加密的密钥,解密获取密钥,再使用对称加密算法,和用户完成接下来的网络通信
3.vue双向绑定实现方式
vue2:defineproperty:当set时候就可以触发dom的update
observer、deps、watcher、view
为什么无法监听数组,因为length是length的configurable是false,提供了变异的set方法
vue3:Proxy
转成了类数组,对每个key的监听
4.proxy优点
对数据层做了拦截层,可以实现不同的效果
如sandbox
5.VDOM的优点
避免过度的渲染html,避免浏览器过度回流和重绘,解约开销
缺点就是:首次运算就慢,可以上ssr
如何实现:分了三个步骤
- compile
- dom => vdom
- diff
- 节点比对
- react分别对tree diff、component diff 以及 element diff 进行算法优化
- patch
- 对应的vdom渲染到节点上
6.Promise的优点,特性
避免地狱回调
7.作用域
作用域分为:
- 局部作用域
- 全局作用域
JavaScript 变量生命周期在它声明时初始化。
局部变量在函数执行完毕后销毁。
全局变量在页面关闭后销毁。
8.为什么要设计this
函数的this取决于调用位置。
9.前端工程化有了解过吗
- 前后端分离
- 模块化开发
- 组件化开发
- 规划代码风格及流程
- ci/cd
10.common.js了解吗
应用于node模块的规范,require/module.exports
运行在浏览器端的JavaScript由于也缺少类似的规范,在ES6出来之前,前端也实现了一套相同的模块规范(例如: AMD),用来对前端模块进行管理。
循环引用:
module缓存过,所以不存在循环问题。
自ES6起,引入了一套新的ES6 Module规范,
es-moudle存在于编译时,所以导入导出不能放在代码块
11.tree-shaking原理有看过吗
代码静态分析时,移除未引用的模块
es6较为容易实现,实现未三个阶段
- 收集graph依赖图
- 检测是否有依赖
删除产物
- 实现new操作符
13.
var fullname = ‘1’;
var obj = {
fullname: ‘2’,
prop: {
fullname: ‘3’,
getFullname: () => {
return this.fullname;
}
}
};
console.log(obj.prop.getFullname());
var test = obj.prop.getFullname;
console.log(test());
14.
// 路径总和:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,
// 判断该树中是否存在 根节点到叶子节点 的路径,
// 这条路径上所有节点值相加等于目标和 targetSum 。 ```javascript var searchTargetSum = (root, targetSum) => { if (!root) { return 0 }
// 执行条件 if (targetSum === root.val && !root.left && !root.right) { return true } return searchTargetSum(root.left, targetSum - root.val) || searchTargetSum(root.right, targetSum - root.val) } ```
- 实现new操作符
2021.7.6 二面
1. 如何进行技术学习
吸收:官方文档、订阅、互相cr
输出:文章分享、技术分享
字符串转对象函数是怎么编写的
Json.parse、eval- async/await相比Promise用法的区别
本质是编程Promise.resolve()
- async/await相比Promise用法的区别
- promise.then().then().catch()为什么可以一直then下去
在promimse的执行完成时又返回了一个promise
实现promise timeout 方法,可以选择函数或者原型方法。如果promise对象在ms时间内未fullfilled,则reject。
const promiseTimeout = (promise, timeout) => {const delay = new Promise((resolve, reject) => {setTimeout(reject, timeout)})return Promise.race([delay,promise])}
ws 建立连接的流程
- tcp三次握手
- 客户端传送http基础信息
- ws服务器确认
- onopen
- TCP HTTP区别,在哪一层
http基于tcp
8. 实现instanceof
作用:
- 判断类型
- 是否属于某原型
寻找原型链即可
- 如何实现request cancelation(取消xhr、axios请求)
xhr.abort()
10. dsl -> C端展现(跨端相关,这个不了解)
一面提到的tree shaking原理后来有去看吗
图片的旋转、缩放如何实现
transform:
scale实现图片无限旋转
animation: keyframefunction fn(){
console.log(this.x);
}
fn.bind({x: 1}).bind({x: 2})() // 打印结果?
只会bind第一次的
15. 实现bind
Function.prototype.bind2 = function (context) {
var self = this;
var bindArgs = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fbound = function () {
var args = bindArgs.concat(Array.prototype.slice.call(arguments));
self.apply(this instanceof self ? this : context, args);
}
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
}
- 判断一个单链表中是否有环
```javascript
/**
- Definition for singly-linked list.
- function ListNode(val) {
- this.val = val;
- this.next = null;
- } */
/**
- @param {ListNode} head
- @return {boolean}
*/
// 双指针,i跑1个单位,j跑两个单位。
var hasCycle = function(head) {
var slow = head
var fast = head
while(slow !== null && fast.next !== null && fast.next.next !== null) {
} return false }; ```slow = slow.next fast = fast.next.next if (slow === false) { return true }
- 实现柯里化
const curry = f => a => b => f(a, b)
day2
异步加法
可以分为并行实现、串行实现
function asyncAdd(a, b, callback) {
setTimeout(function () {
callback(null, a + b);
}, 500);
}
var promiseAdd = (a, b) => new Promise((resolve, reject) => {
asyncAdd(a, b, (err, res) => {
if (err) {
reject(err)
} else {
resolve(res)
}
})
})
var serailSum = async function (..args) {
// 串行核心是每上个promise处理完,给下个处理
return args.reduce((task, current) => {
task.then(res => promiseAdd(res, current))
}, Promise.resolve(0))
}
var parallelSum = async (..args) => {
// 全部放在Promise.all里,得到第一组然后,再递归合并为0
var tasks = []
if (args.length === 1) return args[0]
for (let j = 0; j < args.length; j+2) {
tasks.push(promiseAdd(args[j], args[j + 1] || 0))
}
const res = await Promise.all(tasks)
return parallelSum(...res)
}
[] == ![]
为true
整体流程如下
- [] == false
- [] == 0
- ‘’ == 0
- 0 == 0
{} == !{}
- {} == ! {}
- {} == false
- {} == 0
- NaN == 0 -> false
repaint 和 reflow
重绘、回流
重绘是指部分区域style发生变化,如字体颜色,背景会重新绘制
回流是文档流涉及到布局变化重新渲染。回流代价会比重绘更高
用 React 实现一个树型结构
1、核心是维护一个树形数据结构
{
id: 'a',
val: 'a-val',
children: [
{
id: 'b',
val: 'b-val',
}
]
}
function Tree(props) {
conste { data } = props;
return data.map(item => {
return (
<div>
<div>item.val</div>
<div>{(item.children && item.children.length > 0) ? <Tree data={item}>: ''}</div>
</div>
)
})
}
promise顺序
//Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
})
promise.then(() => {
console.log(3);
})
console.log(4);
如果让你来设计一个分页功能, 你会怎么设计? 前后端如何交互?
- 总条数
- 总页数
- 当前页面
- 当前页树数据
前端请求:页数、每页数量
服务端返回的数据
const start = (page - 1) * pageSize
const end = page * pageSize
HTTP 缓存
强缓存
- cache-control 相对,优先级最大
- expires 绝对时间
协商缓存
- etag 文件hash值
- last-modify
TCP/UDP
tcp安全性比udp可靠
- 面向连接
- 面向字节流
- 双工通信
udp:
- 不需要三次握手
- 单播、多播、广播
- 没有拥塞控制不稳定
URL参数解析:
function decode(url) {
let arr = url.split("://");
const protocol = arr[0];
url = arr[1] || '';
arr = url.split('/');
const host = arr.shift();
url = arr.join('/');
arr = url.split('?');
const path = arr.length === 2 ? '/' + arr.shift() : '';
url = arr[0];
arr = url.split('#');
const hash = arr.length === 2 ? arr[1] : '';
const query = {};
arr = arr[0].split('&');
for (let i = 0; i < arr.length; i++) {
const [key, value] = arr[i].split('=');
if (query[key]) {
if (Array.isArray(query[key])) {
query[key].push(value);
} else {
query[key] = [
query[key],
value
]
}
} else {
query[key] = value;
}
}
return {
host,
protocol,
path,
query,
hash
}
}
const url = 'http://www.tiktok.com/a/b?key=1&key=2&key=3&test=4#hehe'
console.log(JSON.stringify(decode(url)));
day3
1、进程和线程
线程是进程最小单位
多进程是指操作系统能同时运行多个任务(程序)。多线程是指在同一程序中有多个顺序流在执行
2、String和new String的区别
String返回的是字符串基本类型
new是引用对象
3、OSI七层模型,当你打开app时发生了什么?
Osi7层理论研究。
在我们应用中更多是tcp-ip的应用层
1、域名请求
2、请求dns服务器拿到ip
3、http请求头给tcp协议,相邻层回话
3、ip寻址
4、以太网帧二进制数据,网络接口层
5、转电信号
4、【手撕】Ajax发起的过程
var getJSON = (url) => {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.send()
xhr.onreadystatechange = (e) => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.readystate === 4 && xhr.status === 200) {
resolve(xhr.responseText)
} else {
reject(e)
}
}
})
}
5、数据库了解吗?讲讲事务(不会)
一致性、原子性、持久性、隔离性
6、DOM和BOM区别,常用api有哪些?
document object、browserObject
JSON如何转换为DOM?虚拟DOM了解吗?
7、【手撕】获取cookie方法
var getCookie = (key) => {
var list = document.cookie.split('; ')
var res = {}
list.map(k => {
var [key, val] = k.split('=')
res[key] = unescape(val)
})
return res[key] || ''
}
8、【手撕算法】最小的K个数
/**
* @param {number[]} arr
* @param {number} k
* @return {number[]}
*/
// 先排序,slice就好
var getLeastNumbers = function(arr, k) {
const list = quickSort(arr, 0, arr.length - 1)
return list.slice(0, k)
};
// 取得中间基址,核心是分区,然后分区处理,递归
function swap(items, leftIndex, rightIndex){
var temp = items[leftIndex];
items[leftIndex] = items[rightIndex];
items[rightIndex] = temp;
}
function partition(items, left, right) {
var pivot = items[Math.floor((right + left) / 2)], //middle element
i = left, //left pointer
j = right; //right pointer
while (i <= j) {
while (items[i] < pivot) {
i++;
}
while (items[j] > pivot) {
j--;
}
if (i <= j) {
swap(items, i, j); //sawpping two elements
i++;
j--;
}
}
return i;
}
function quickSort(items, left, right) {
var index;
if (items.length > 1) {
index = partition(items, left, right); //index returned from partition
if (left < index - 1) { //more elements on the left side of the pivot
quickSort(items, left, index - 1);
}
if (index < right) { //more elements on the right side of the pivot
quickSort(items, index, right);
}
}
return items;
}
9、reduce实现一个filter
// reduce用法
// reduce((total, current, arr, index) => nextVal, [])
var filter = (arr, fn) => arr.reduce((prev, current, index, arr) => {
fn(current, index) ? prev.push(current) : null
}, []))
reduce实现flattern
var flattern = (arr) => {
return arr.reduce((pre, current, index) => {
if (Array.isArray(current)) {
return pre.concat(flattern(current))
} else {
pre.push(current)
return pre
}
}, [])
}
reduce实现map
var map = (arr, fn) => {
return arr.reduce((pre, current, index) => {
pre.push(fn(current))
return pre
}, [])
}
reduce实现max
var max = (arr) => arr.reduce((pre, current, index) => Math.max(pre, current), arr[0])
reducer实现去重
var removeDuplicates = arr => arr.reduce((pre, current, index) => {
if (!pre.includes(current)) {
pre.push(current)
}
return pre
}, [])
day4
1. 实现一个模板语法 underscore _.template:
var tpl = "hello: <%= name %>";
var template = tpl => data => tpl.replace(/<%= (.*?) %>/g, (match, key) => data[key])
// tpl字符串根据变量标识符,直接替换
// 转换成多个str存入数组,最后join
// 最后 new Function('obj', ` with(obj) {${tpl}}`)
var compiled = template(tpl);
compiled({name: 'Kevin'}); // "hello: Kevin"
'hello: Kevin'
2. 压缩字符串 ‘aaaaaabbbbbcccca’ => ‘a6b5c4a1’
/**
* @param {string} S
* @return {string}
*/
// 双指针,第一个j选中字母开始,第二个k选择计数
var compressString = (S) => {
let res = ''
for (let j = 0 ;j < S.length; j++) {
let k = j + 1
while(S[k] === S[j]) {
k++
}
res += (S[j] + String(k - j))
j = k - 1
}
if (res.length >= S.length) {
return S
} else {
return res
}
}
3. 第一题因为优点紧张,忘了api,花了可能有15分钟,第二题秒答,大概过去了20分钟的样子,后来问了很多实习相关,就一直聊实习的工作内容,你做的事情,我当时答的也不算特别好。。。
day5
基础题
1.HTTPS? VS HTTP 知道哪些对称加密/非加密算法
https是带有加密http。
两个协议唯一区别是https使用SSL来加密正常的http请求和响应。
对称加密:用的是同一个密匙,一定条件下可以解决安全问题。
2.HTTPS 详细的SSL/TLS握手过程?
transport layer security
3.React hooks理念, diff原理,为什么要key prop
react节点数
组件对比
再是component的key、props对比
4.插入大量DOM节点,react 、 原生分别怎么处理,原生会用到哪些方法
batching、diff
fragment
5.React使用Fragment,原生使用createDocumentFragment
6.JWT是什么,cookie细节
Json web token
7.模块化了解吗?AMD CMD CommonJS ESMoudle
8.CSRF 详细说,防御方法
- 避免get请求
- 增加refer限制、增加toke
9.CSS position 详细说
relative 相对定位,基于自身原先布局
absolute绝对定位,基于static的第一个父元素偏离位置
fixed固定屏幕
inherit继承父元素
10.UI组件库详细(我的项目)
11.微前端(我的项目)
12.最近学习的新技术
13.编程
手写节流防抖,详细问场景
// if (now - last > delay) setTimeout program || refresh last
// else run program
var throttle = function (func, delay) {
var last,timerId
return function() {
var [context, args] = [this, arguments]
var now = Date.now()
if (last && now - last < delay) {
clearTimeout(timerId)
timerId = setTimeout(function () {
last = Date.now()
func.apply(context, args)
}, delay)
} else {
last = Date.now()
func.apply(context, args)
}
}
}
// 防抖
var debounce = function (func, delay) {
var timer
return function () {
let args = [...arguments]
let context = this
clearTimeout(timer)
setTimeout(function () {
func.apply(context, args)
}, delay)
}
}
function throttle(fn, delay, fristTrigger = false) {
let timer = null; // 定时器默认为null
let last = 0; // 最后一次时间戳默认为0
return function (...arg) {
const context = this;
if (fristTrigger) {
// 一定时间内执行第一次
// 当前时间戳 - 之前时间戳 > delay 再执行
const cur = Date.now();
if (cur - last > delay) {
fn.call(context, ...arg);
last = cur;
}
} else {
// 没有定时器再执行 执行清空定时器 ,一定时间内执行最后一次
if (!timer) {
timer = setTimeout(() => {
timer = null;
fn.call(context, ...arg);
}, delay);
}
}
};
}
promise实现
https://juejin.cn/post/6940531182706622500
// 三个状态:PENDING、FULFILLED、REJECTED
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 默认状态为 PENDING
this.status = PENDING;
// 存放成功状态的值,默认为 undefined
this.value = undefined;
// 存放失败状态的值,默认为 undefined
this.reason = undefined;
// 调用此方法就是成功
let resolve = (value) => {
// 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
// 调用此方法就是失败
let reject = (reason) => {
// 状态为 PENDING 时才可以更新状态,防止 executor 中调用了两次 resovle/reject 方法
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
try {
// 立即执行,将 resolve 和 reject 函数传给使用者
executor(resolve,reject)
} catch (error) {
// 发生异常时执行失败逻辑
reject(error)
}
}
// 包含一个 then 方法,并接收两个参数 onFulfilled、onRejected
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
原生xhr + promise封装
var fetchJSON = (url) => new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onreadystatechange = e => {
if (xhr.readyStatus !== 4) return
if (xhr.readyStatus === 4 && xhr.status === 200) {
resolve(xhr.responseText)
} else {
reject(xhr.responseText)
}
}
xhr.send(null)
})
二面
1.项目深问
2.项目相关,微框架,为什么使用,难点与思考
3.项目相关,websocket兼容性,原生方法,丢包怎么解决
4.项目相关,RN白屏怎么处理,线上问题怎么定位问题
5.项目相关,什么情况下会OOM,怎么解决;JS什么情况下会堆栈溢出,怎么排查,怎么解决
- 递归
- 排查:打点、堆快照、查看栈调用时的内存变化
6.长列表的滚动与刷新,虚拟列表怎么实现,intersectionObserve,getBoundingClientRect
7.JS动画与CSS动画?JS动画怎么不阻塞,CSS动画为什么不阻塞
JS为ui线程
css为gpu渲染
css不会重排、重绘
8.合成线程:transform,filter,opacity,will-change
通常的dom为render layer
视频、webgl这种高消耗资源Graphics Layer
renderlayer可以升级为Graphics layer,但是消耗内存
优化:
- 减少合成层
- 减少层大小
9.webpack热更新是怎么实现的,怎么实现不刷新页面的更新?
- webscocket
- 坚挺文件修改,webpackHotUpdatehmr
- 更新webpack_modules
10.原生JS有哪些不刷新页面的更新
- iframe
- postMessage通信
- parent.window调用父节点方法
- 子调父getElementById.childWindow
- ajax、jsonp
编程:
1.输出随机数数组,不重复的随机整数[a,b),长度为c
2.二叉树,求从父节点到子节点每条路径组成的数字之和
三面
1.项目深度和广度上都有提问
2.最大子序合
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
// 动态规划
var res = nums[0];
var pre = 0;
for(let k of nums) {
pre = Math.max(k, pre + k)
res = Math.max(res, pre)
};
return res
};
3.附加题:如果你是黑客,怎么实现CSRF攻击?
比如在qq空间。可以自定义装修,插入外链图片
day6
https://www.nowcoder.com/discuss/861304
- 介绍项目
jwt在项目中的具体实现,必要性?为什么不用微信API的鉴权?(答官方文档建议开发者自定登录态)
tcp与udp区别?应用场景?
tcp:文件传输ftp、回话
udp:域名转换
get与post区别,应用场景?在项目中的使用?
http缓存控制,协商缓存?
强缓存:cache-control > expires
协商缓存:etag > last modify(精度问题)
https如何保证安全的?加密方式?公私钥交换过程?
跨域是什么?产生条件?在微信小程序中的运用?解决了什么?
不同tab通信
iframe+storechange绑定
- jsonp有了解过吗?(答没有什么了解,之前看到跨域解决方案的时候有看到过对比,没有深入了解,熟悉的是cors和nginx反向代理。然后也没有追问下去了)
利用script没有跨域限制执行了全局函数,同时把变量透传给当前函数
- 看输出,解释原因复制代码
一面题:
1. react hooks解决了什么问题
- service手里
- 数据和ui的解耦
setstate和usestate的区别
3.vue的v-if和v-show
4.浏览器缓存原理
5.script标签的defer和async
6.请求放created里还是mounted里.
7.异步请求延迟完成会导致mounted时并未获取数据,如何解决
watch:this.$nextTick
8.box-sizing相关计算,
border-box:盒模型尺寸Math.max(boder+padding, width)
content-box:盒模型尺寸算上padding、border
9.实现promise.allvar promiseAll = (arr) => { var count = 0 var res = [] return new Promise((resolve, reject) => { arr.map((j, index) => { j().then(res => { count++ res.push(res) if (count === arr.length) { resolve() } }) }) }) }10.实现add(a)(b)(c) ```javascript var add = function (…args) { return args.reduce((a, b) => a + b) }
var currying = function(fn) { var args = [] return function tmp (…currentArgs) { if (currentArgs.length) { args = […args, …currentArgs] return tmp } else { let val = fn.apply(this, args) args = [] return val } } } var add1 = currying(add) console.log(add1(1)(2)())
11.用两个栈实现一个队列
二面题:<br />抖音社区安全部门<br />文件上传的请求body(content-type)<br />cookie和token的区别<br />cookie都有哪些字段
```javascript
path限制可以访问此cooki路径,expires过期时间,httponly
如何清除cookie
代码混淆的作用
如何排查线上问题(fiddler)
工程化问题:首屏加载优化、tree-shaking原理、如何抽离插件库使其只打包一次(CommonsChunkPlugin)、
npm包发布步骤
npm publish
css三角形
width:0px;
height: 0px;
border: 40px solid ;
border-color: #06a43a transparent transparent;
