面试题 题目总共6个:
题1.熟悉的前端技术栈
答: react | vue | angular
相同: ...
不同: ...
题2: 实现一个深拷贝
答
1. JSON.parse(JSON.stringify())
通过JSON.srtingfy()将数据转为json格式,通过JSON.parse()解析josn数据 弊端:
- 无法对Date、RegExp、Error对象进行深拷贝
- 无法对函数
- 无法解决循环引用
- 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
2. 递归实现
function deepClone (obj) {
if (!obj || typeof (obj) !== 'object') { return }
var resultObj = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
resultObj[key] = typeof (obj[key]) === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return resultObj
}
实现图片:
题3: 熟悉的http状态码
答:
200: 成功,
301: 永久重定向,
302: 临时重定向,
403: 服务器资源拒绝访问,
500: 服务器内部错误,
503: 超载或系统维护,服务器暂时的无法处理客户端的请求,
504: 网关或代理的服务器错误,
404: 服务器找不到资源
ps: 举例常见得几种状态码
题4: eventLoop问题,
题目简化为下面的步骤
const p1 = new Promise((resove, reject) => {
console.log('promise1')
setTimeout(() => {
resove(666)
}, 0)
})
const p2 = p1.then(res => {
console.log('promise2', res)
})
setTimeout(() => {
console.log('10ms')
}, 10)
setTimeout(() => {
console.log('0ms')
}, 0)
console.log('go on')
答: 总输出: promise1、go on、 promise2 666、 0ms、 10ms
题5: 火车站班次问题,例如: AB2, DC6, BD7, CD3。
输入 A,B 得到2 输入 A,B,D 得到9 输入 A,B,C 得到 'not find'
答:
const arr = [
{
start: 'A',
end: 'B',
time: 2
},
{
start: 'D',
end: 'C',
time: 6
},
{
start: 'B',
end: 'D',
time: 7
},
{
start: 'C',
end: 'D',
time: 3
},
]
function computeTime () {
var distance = 0;
var statue = false;
for (let key in arguments) {
if (arguments[Number(key) + 1]) {
arr.forEach(item => {
if (item.start === arguments[key] && item.end === arguments[Number(key) + 1]) {
distance += item.time
}
if (item.start === arguments[key] && item.end === arguments[Number(key) + 1] && (key == (arguments.length - 2))) {
statue = true
}
})
} else {
break
}
}
return statue ? distance : 'Not Find'
}
let resultDis1 = computeTime('A', 'B', 'C')
let resultDis2 = computeTime('A', 'B')
let resultDis3 = computeTime('A', 'B', 'D')
console.log(resultDis1, resultDis2, resultDis3) //"Not Find" 2 9
题6: 三道正则题
- ???
- ???
- ???
答:
寄!
前端工程师的素养
分割线 问答题
陆陆续续一些js、浏览器问题、性能优化、坑......记不到了.
1.跨域的解决方案
答:分析什么是跨域,如何实现跨域。
跨域: 浏览器采用同源策略(协议、端口号、domain相同),同源策略是一个重要的安全策略,它能帮助阻隔恶意的文档,减少可能被攻击的媒介。
实现跨域:
协议、端口号、domain任意一个不同
- webpack中devServer配置proxy设置代理
- Nginx反向代理
- JSONP (利用了
script标签没有跨域限制的这个特性来完成的) - node中间件,
- CORS (跨域资源共享):在请求头上新增(Access-Control-Allow-Origin)
推荐一种更详细跨域解决方案的文章:直达链接
2.节流
在一定时间内,多次事件触发,只执行一次.
常见应用: input实时模糊搜索丶防止重复提交表单
2.1 节流时间戳方式实现
function throttle (fn, delay) {
var pre = 0;
return function () {
let now = Date.now();
if (now - pre >= delay) {
fn.apply(this, arguments)
pre = now;
}
}
}
2.2 节流定时器方式实现
function throttle (fn, delay) {
var statue = false;
return function(){
if(statue) {return};
statue = setTimeout(() => {
fn.apply(this, arguments);
statue = false;
}, delay)
}
}
3.防抖
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
常见应用: 输入框搜索,滚动懒加载图片丶onresize监听窗口大小丶onscroll监听滚动条
function debounce (fn, delay) {
var timer = null;
return function () {
if (timer) { clearTimeout(timer) };
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null;
}, delay)
}
}
4.闭包
当函数能够记住并访问所在得词法作用域时,就产生了
闭包(你不知道的Javascript上卷)
举个栗子:
function foo() {
var a = 2;
function bar() {
console.log(a)
}
setTimeout(() => {
console.log(this.a)
}, 1000)
return bar;
}
var baz = foo();
baz()
一般来讲foo()执行后,通常会期待foo()的整个内部作用域都被销毁,因为引擎有垃圾回收机器用来释放不再使用的内存空间
在foo被调用完成后,没有再次使用的,所以很自然的会考虑进行回收.而闭包就可以阻止此类事情发生.
闭包使用过多可能会导致内存泄漏,如何清除呢? 将使用的引用赋值为null。
5.this指向、作用域
既然你说到了this那么这道题输出什么呢?
var b = 2
var a = {
b: 1,
fn: function() {
console.log(this.b)
}
}
a.fn() //输出?
var c = a.fn
c() // 输出?
答: 1 2
分析:
a.fn() 输出的this指向a的作用域,输出 1
c = a.fn 赋值
c() c来调用的fn() 那么this应该指向window,输出 2
this指向
- 普通函数 this -> Window对象
- 构造函数 this -> 构造函数的实例对象
- 定时器 (非严格模式)this -> Window对象 (严格模式)=> undefined
大多数都可以将this的指向为谁调用就指向谁
6.重绘、重排
答:
页面渲染: 渲染引擎将HTML文档解析成DOM树,将CSS文档解析成CSSOM树,将DOM树和CSSOM树合并成渲染树。浏览器再进行页面渲染。
问: 什么是重绘? 哪些会导致重绘? 有哪些优化方案?
答:
- 重绘: 页面样式变化,重新绘制局部页面
- 重排: 页面布局发生变化,进行重新排列渲染树
- 关系: 重绘不一定重排丶重排一定重绘
优化方案及详解见-> 直达链接
分割线..............................
其余项目问题全靠积累.......
逻辑思路题
1. 千分位格式化
1.1 toLocaleString()
缺点精度丢失
function format(num) {
return num.toLocaleString()
}
console.log(format(12345.6789)); //12,345.679
console.log(format(1234567)); //1,234,567
console.log(format(123456)); //123,456
console.log(format(0.1234567));//0.123
1.2 正则
难度系数高:需要熟练掌握正则语法 来源于项目工具函数
var str = "100000000000.123121",
reg = /(?=(\B\d{3})+$)/g;
str.replace(reg, ",")
2.树型结构打印
有以下结构代码: 将扁平化数组转为树型结构
const list = [
{ id: 1001, parentId: 0, name: 'AA' },
{ id: 1002, parentId: 1001, name: 'BB' },
{ id: 1003, parentId: 1001, name: 'CC' },
{ id: 1004, parentId: 1003, name: 'DD' },
{ id: 1005, parentId: 1003, name: 'EE' },
{ id: 1006, parentId: 1002, name: 'FF' },
{ id: 1007, parentId: 1002, name: 'GG' },
{ id: 1008, parentId: 1004, name: 'HH' },
{ id: 1009, parentId: 1005, name: 'II' },
];
2.1 递归实现
function listToTree(arr) {
if(!Array.isArray(arr)) {return []}
var pNodes = arr.filter(item => item.parentId === 0)
var cNodes = arr.filter(item => item.parentId !== 0)
toTree(pNodes, cNodes)
return pNodes
function toTree(pArr, cArr) {
pArr.map(p => {
cArr.map((c, i) => {
if(c.parentId === p.id){
let _children = JSON.parse(JSON.stringify(cArr))
_children.splice(i, 1) //删除遍历过的元素 减少遍历次数
toTree([c], _children)
if(p.children){
p.children.push(c)
}else{
p.children = [c]
}
}
})
})
}
}
2.2 filter
function printTree(treeNode) {
var _data = JSON.parse(JSON.stringify(treeNode));
return _data.filter(p => {
var _arr = _data.filter(c => c.parentId === p.id)
_arr.length && (p.children = _arr)
return p.parentId === 0
})
}
测试结果:
3.树形结构扁平化
var sourceData = [
{
"id": 1,
"text": "Node 1",
"state": "closed",
"children": [
{
"id": 11,
"text": "Node 11",
"children": [
{
"id": 13,
"text": "Node 13"
},
{
"id": 14,
"text": "Node 14"
}
]
},
{
"id": 12,
"text": "Node 12"
}
]
},
{
"id": 2,
"text": "Node 2",
"state": "closed"
}
];
function treeToArr(arr) {
if(!Array.isArray(arr)) return []
var midArr = []
function getArr(arr) {
for( let k in arr) {
if(arr[k]?.children){
var childNodes = JSON.parse(JSON.stringify(arr[k]))
let result = childNodes.children.map(item => {
return {...item, pid: childNodes.id}
})
delete childNodes.children
midArr.push(childNodes)
getArr(result)
}else{
var childNodes = JSON.parse(JSON.stringify(arr[k]))
midArr.push(childNodes)
}
}
}
getArr(arr)
return midArr
}
treeToArr(sourceData)
实现
3.JSON Key 驼峰转换
比如:
{ 'a_bb_cc': 1} => { 'aBbCc': 1 }
{ 'a_bb_cc': 1, { 'c_bb_cc': 'c_b_c'}} => { 'aBbCc: 1', {'cBbCc' : 'cBC'}}
解:
- js实现
- 库实现:lodash库中的camelCase方法
答: 待完善
补充题目
题一
满足 a == 1 && a == 2 && a == 3
if(a == 1 && a == 2 && a == 3) {
console.log('我满足了以上条件,并执行了。')
}
- 解一:如果a是对象 会自动调用toString() 获取到默认值1 ++1 = 1 a == 1 满足 ,第二个判断 a == 2 又会调用tostring方法 依次类推......
var a = {
_default: 0,
toString() {
return ++this._default
}
}
- 解二:将toString()改为valueOf() 同样可以实现,亲测.
- 解三:这里做了三次判断 相当于获取到了三次a的值 在这里可以联想到 数据的双向绑定 当获取a的值的时候修改a。这里是运行再浏览器中的 默认最顶级是window对象
var _default = 0;
Object.defineProperty(window, 'a', {
get() {
return ++_default
}
})
题二
console.log(({} + {}).length)
console.log(([] + []).length)
console.log((function(){}).length)
function foo (a, b, c) {
console.log(arguments.length)
}
foo(1, 2, 3, 4)
console.log(foo.length)
答: 30 0 0 4 3
-
({} + {}).length: 对象与对象没有能直接相加的 这里涉及到了类型得隐式转换 利用对象的toString()转换为字符串进行相加.原理为: Object.prototype.toString() + Object.prototype.toString() // "[object Object]" 求这个字符串得长度
-
([] + []).length: 数组与数组相加 直接拼接得 没有进行隐式转换 [] + [] = ""
-
function(){}).length: MDN明确说明:
length属性指明函数的形参个数 -
arguments.length: arguments.length 函数调用时传入函数的实参数量.
-
foo.length: 函数形参个数
Vue
1. 双向绑定的原理
vue2.0 Object.prototype
vue3.0 Proxy
React
1. react的数据不可变是什么
2. 可以在条件、循环中调用HOOK吗?
答案: React官网讲述
webpack
1. 优化打包体积
-
gizp 压缩 -> 在webpack的devServer配置 cpmpress: true
-
loader切片
总结
以上回答仅记录自己目前记忆及自己总结的回答题目,答案不一定正确,后续慢慢改正.希望各位大佬能够提出跟好的解决方案. 提出指正,及时改正.
不断输入,不断输出.共同进步