面试题实战笔记宝典

508 阅读7分钟

面试题 题目总共6个:

题1.熟悉的前端技术栈

答: react | vue | angular

相同: ...

不同: ...

题2: 实现一个深拷贝

1. JSON.parse(JSON.stringify())

通过JSON.srtingfy()将数据转为json格式,通过JSON.parse()解析josn数据 弊端:

  1. 无法对Date、RegExp、Error对象进行深拷贝
  2. 无法对函数
  3. 无法解决循环引用
  4. 如果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
}

实现图片:

2d7fb5e358f070fb13cfda8f3570634.png

题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: 三道正则题

  1. ???
  2. ???
  3. ??? 答: 寄!

前端工程师的素养

分割线 问答题

陆陆续续一些js、浏览器问题、性能优化、坑......记不到了.

1.跨域的解决方案

答:分析什么是跨域,如何实现跨域。

跨域: 浏览器采用同源策略协议端口号domain相同),同源策略是一个重要的安全策略,它能帮助阻隔恶意的文档,减少可能被攻击的媒介。

实现跨域:协议端口号domain任意一个不同

  1. webpack中devServer配置proxy设置代理
  2. Nginx反向代理
  3. JSONP (利用了 script 标签没有跨域限制的这个特性来完成的)
  4. node中间件,
  5. 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指向

  1. 普通函数 this -> Window对象
  2. 构造函数 this -> 构造函数的实例对象
  3. 定时器 (非严格模式)this -> Window对象 (严格模式)=> undefined

大多数都可以将this的指向为谁调用就指向谁

6.重绘、重排

答:

页面渲染: 渲染引擎将HTML文档解析成DOM树,将CSS文档解析成CSSOM树,将DOM树和CSSOM树合并成渲染树。浏览器再进行页面渲染。

问: 什么是重绘? 哪些会导致重绘? 有哪些优化方案?

答:

  1. 重绘: 页面样式变化,重新绘制局部页面
  2. 重排: 页面布局发生变化,进行重新排列渲染树
  3. 关系: 重绘不一定重排丶重排一定重绘

优化方案及详解见-> 直达链接

分割线..............................

其余项目问题全靠积累.......

逻辑思路题

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 
    })
}

测试结果:

image.png

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)

实现

image.png

3.JSON Key 驼峰转换

比如:
{ 'a_bb_cc': 1}  => { 'aBbCc': 1 }
{ 'a_bb_cc': 1, { 'c_bb_cc': 'c_b_c'}} => { 'aBbCc: 1', {'cBbCc' : 'cBC'}}

解:

  1. js实现
  2. 库实现:lodash库中的camelCase方法

答: 待完善

补充题目

题一

满足 a == 1 && a == 2 && a == 3

if(a == 1 && a == 2 && a == 3) {
        console.log('我满足了以上条件,并执行了。')
}
  1. 解一:如果a是对象 会自动调用toString() 获取到默认值1 ++1 = 1 a == 1 满足 ,第二个判断 a == 2 又会调用tostring方法 依次类推......
var a = {
        _default: 0,
        toString() {
                return ++this._default
        }
}
  1. 解二:将toString()改为valueOf() 同样可以实现,亲测.
  2. 解三:这里做了三次判断 相当于获取到了三次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

  1. ({} + {}).length: 对象与对象没有能直接相加的 这里涉及到了类型得隐式转换 利用对象的toString()转换为字符串进行相加.原理为: Object.prototype.toString() + Object.prototype.toString() // "[object Object]" 求这个字符串得长度

  2. ([] + []).length: 数组与数组相加 直接拼接得 没有进行隐式转换 [] + [] = ""

  3. function(){}).length: MDN明确说明:length 属性指明函数的形参个数

  4. arguments.length: arguments.length 函数调用时传入函数的实参数量.

  5. foo.length: 函数形参个数

Vue

1. 双向绑定的原理

vue2.0 Object.prototype
vue3.0 Proxy

React

1. react的数据不可变是什么

2. 可以在条件、循环中调用HOOK吗?

答案: React官网讲述

webpack

1. 优化打包体积

  1. gizp 压缩 -> 在webpack的devServer配置 cpmpress: true

  2. loader切片

总结

以上回答仅记录自己目前记忆及自己总结的回答题目,答案不一定正确,后续慢慢改正.希望各位大佬能够提出跟好的解决方案. 提出指正,及时改正.

不断输入,不断输出.共同进步