前言
笔试是清明前写的,节后通知过了约面试,面的是百度的搜索部门,部门很大,人也很多,所以面试里会着重围绕着项目和 js 来问,框架基本不问。
因为隔了实在太久有些问题忘记了,所以就把印象深的列举出来,其他感兴趣的uu可以自己查阅或跳转😋😋
一面面试官: 是一个看上去很精神的人,一看就是商业精英的那种,最主要的是,他居然会自我介绍欸,什么???🐭🐭没听错吧???面试官居然会自我介绍???打败全世界99.9%的面试官!!!
二面面试官: 是一个很温柔的小姐姐,第一印象居然是长得非常像我班长,真的太像了,问的很简单,题目聊完后就纯聊天了(应该不是kpi吧,应该吧......
一面
1.jwt + token
2.语义化标签
3.块级、行内、行内块元素
1. 块级(display:block): 例如 div、p、h、ul、li等,可以设宽高,会占据一整行;
2. 行内(display:inline): 例如 span、a、strong等,是按标签内容来决定宽高,不可设置宽高但可设置边距
3. 行内块(display:inline-block): 例如 img、input、button等,可设宽高,可以并排放置不会占据整行。
题外话: display 还可设 flex、grid、table-cell、none 等。
4.数组有哪些方法
5.原始类型和引用类型
6.js有哪些类型判断方法
typeof、instanceof、Array.isArray()、Object.prototype.toString.call()
7.bfc(你说的太详细了 - [全是背的,实际用起来不会,面试官不好点破我😭😭])
8.事件循环
9.js阻塞html解析(define 和 async 属性)
<script>
标签阻塞浏览器对HTML解析,如获取JS脚本网络请求迟迟得不到响应或JS脚本执行时间过长,都会导致白屏,用户看不到页面内容
<script defer>
获取该脚本的网络请求的不阻塞浏览器解析 HTML ,一旦网络请求完成之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析HTML,而是等待 HTML 解析完毕再执行 JS 代码。
<script async>
网络请求是异步的不会阻塞浏览器解析HTML ,旦网络请求完成之后(也就是脚本加载完成之后)如HTML还没解析完浏览器会暂停解析,先让执行JS脚本代码,执行完毕后再解析HTML。
10.js有哪些获取dom结构的方法
-
getElementById(id):通过元素的唯一标识符(id)获取DOM元素。
-
getElementsByClassName(className):通过类名获取DOM元素,返回一个包含所有指定类名的元素的 HTML集合。
-
getElementsByTagName(tagName):通过标签名获取DOM元素,返回一个包含所有指定标签名的元素的 HTML集合。
-
querySelector(selector):通过 CSS 选择器选择一个匹配的元素。
-
querySelectorAll(selector):通过 CSS 选择器选择所有匹配的元素,返回一个 NodeList。
-
getElementsByName(name):通过元素的name属性获取DOM元素,返回一个包含所有指定name属性值的元素的 NodeList。
11.var、let、const
12.输出题
js
var a = 1
function a(){}
console.log(a)
js 具有声明提升这个功能,函数声明会被提升到作用域的顶部 ,而 var
变量声明也会被提升,但是只有声明会被提升,赋值操作不会提升。
虽然函数 a
在变量 a
之后被声明,但是由于函数声明会被提升到作用域的顶部,所以在 console.log(a)
执行时,变量 a
已经被赋值为 1
,而函数声明 function a(){}
被提升至作用域的顶部,但是由于变量 a
已经被赋值为 1
,所以函数声明不会对变量 a
产生影响。因此,console.log(a)
输出的是 1
,而不是函数体。
简单可以这么理解:
13.手写图片懒加载
一开始前面回答获取dom结构的方法不尽人意,所以面试官想用这题考考我,但是这题我太熟了呀,听到这题的时候🐭🐭心里别提多开心了。
html
<body>
<!-- 图片容器 -->
<div class="container">
<!-- 初始时使用空src,并在data-src中指定真实图片路径 -->
<img src="" data-src="../image/saibo.png">
<img src="" data-src="../image/saibo.png">
<img src="" data-src="../image/saibo.png">
<img src="" data-src="../image/saibo.png">
<img src="" data-src="../image/saibo.png">
<img src="" data-src="../image/saibo.png">
</div>
<script>
// 获取所有图片元素
var images = document.querySelectorAll('img');
// 懒加载函数
function lazyLoad() {
// 获取滚动条距离顶部的距离
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条距离顶部的距离' + scrollTop);
// 获取窗口高度
var winHeight = window.innerHeight;
console.log('窗口高度' + winHeight);
console.log('可视窗口的底部到最顶部的距离' + (scrollTop + winHeight));
// 遍历所有图片元素
for (var i = 0; i < images.length; i++) {
// 如果图片距离顶部的距离小于窗口高度
if (images[i].offsetTop < scrollTop + winHeight) {
// 将 data-src 中的真实图片路径赋值给 src,触发图片加载
images[i].src = images[i].getAttribute('data-src');
}
}
}
// 监听滚动事件,当页面滚动时执行懒加载函数
window.onscroll = lazyLoad;
</script>
</body>
详细原理看这 -> 一眼就能明白的懒加载实现原理!
面试官:js基础还需要继续加强,我不会问你框架问题,但你会js底层就一定会框架,就这样吧,后续等通知。
我:😥😥
我本想着百度无望后就去滴滴了,但两天后hr通知我二面了😚😚
🐭🐭好像有细糠吃了~
二面
1.怎么自定义的全局懒加载指令(本人项目中所用到的优化方式)
看这 -> 面试官:会用自定义指令来实现懒加载吗?
2.IntersectionObserver 和 ResizeObserver
当时聊实现原理聊到了 IntersectionObserver,然后面试官又给我介绍了一个 ResizeObserver。
IntersectionObserver -> mdn:IntersectionObserver
IntersectionObserver
用于监测目标元素与其祖先元素或 viewport(视口)之间交叉状态的API。它允许我们在元素进入或退出视口时执行相应的操作,而无需使用传统的事件监听器或轮询来检测元素的位置。特别适用于实现无限滚动加载、懒加载图片和元素动画等场景。通过指定阈值(thresholds)和根元素(root element),可以灵活地定义何时触发交叉状态的变化。
js
const intersectionObserver = new IntersectionObserver((entries) => {
// 如果 intersectionRatio 为 0,则目标在视野外,
// 我们不需要做任何事情。
if (entries[0].intersectionRatio <= 0) return;
loadItems(10);
console.log("Loaded new items");
});
// 开始监听
intersectionObserver.observe(document.querySelector(".scrollerFooter"));
ResizeObserver -> mdn:ResizeObserver
ResizeObserver
用于监听元素的尺寸变化,与传统的resize事件不同,ResizeObserver 可以一次观察多个元素的尺寸变化。我们可以在元素尺寸发生变化时立即获得通知,并且可以针对性地执行相应的操作,而无需像以前那样依赖于resize事件或者定时器来检查元素的尺寸变化。
3.讲讲 pinia 和 vuex
4.怎么封装组件(着重说的复用性和耦合性)
5.创建几个仓库实例以及怎么调用的(这问题问的挺怪的,应该着重考你思维)
这里我本人的仓库是先封装一个通用基类(例如存储用户信息等常见信息),剩余的仓库是基于基类来创建各自的仓库(例如基于通用基类分别创建购物车仓库、订单仓库等),这种仓库对于通用数据方便管理且模块化分明,最后再引入到一个总仓库,需要用到某个仓库时直接调用总仓库再解构对应仓库就行。
6.vue 组件通信(对比vue2和vue3来讲,vue3事件总线EventBus已被剔除,需mitt插件才能用)
7.js有哪些类型判断方法(被问俩次了)
8.知道哪些可以遍历的方法
1. for...in 和 for...of (看这 -> 那些 for in 和 for of 以及遍历迭代踩过的坑)
2. forEach:......
3. map: 创建一个新数组,其中的每个元素都是原始数组元素调用回调函数的结果。它接受一个回调函数作为参数,该回调函数会被传入当前元素、当前索引和数组本身。
4. set: 它存储唯一值,并且可以迭代这些值
5. filter: 创建一个新数组,其中包含原始数组中所有满足条件的元素。它接受一个回调函数作为参数,该回调函数会被传入当前元素、当前索引和数组本身,并返回一个布尔值表示是否保留当前元素。
6. reduce: 对数组中的每个元素执行一个提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。它接受一个回调函数和一个初始值作为参数,回调函数接收累加器和当前元素作为参数。
7. every: 用于检测数组中的所有元素是否都符合指定条件。它会对数组中的每个元素调用一个提供的回调函数,直到找到一个返回 false
的元素,此时停止遍历,并返回 false
。如果所有元素都通过了测试,则返回 true
。
9.着重问了 reduce 方法以及使用场景
reduce()
是一个高阶函数,它用于对数组中的每个元素执行一个指定的函数,并将结果累积起来,最终返回一个单一的值,它方法接受两个参数:
-
回调函数 (callback):这个函数接受四个参数:
accumulator
:累加器,累积器累积回调函数的返回值。它是上一次调用回调函数时的返回值,或者是提供的初始值(如果提供了)。currentValue
:当前元素,数组中正在处理的元素。currentIndex
:当前索引,数组中正在处理的当前元素的索引。array
:原始数组。(这个我当时是真的不知道,一直以为只有三个参数......)
-
初始值 (initialValue) :作为第一次调用回调函数时的第一个参数
accumulator
的初始值。如果没有提供初始值,则将使用数组的第一个元素作为初始值,并从数组的第二个元素开始调用回调函数。
js
.reduce((accumulator, currentValue, currentIndex) => (), initialValue);
常见reduce()
方法的使用场景:
- 数组求和:计算数组中所有元素的总和。
javascript
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 输出 15
- 操作数组:将数组中的元素按照一定规则转换成其他数据结构或者格式。
javascript
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.reduce((accumulator, currentValue) => {
accumulator.push(currentValue * 2);
return accumulator;
}, []);
console.log(doubled); // 输出 [2, 4, 6, 8, 10]
- 查找最大值或最小值:找出数组中的最大值或最小值。
javascript
const numbers = [10, 5, 20, 3, 15];
const max = numbers.reduce((accumulator, currentValue) => Math.max(accumulator, currentValue), -Infinity);
console.log(max); // 输出 20
- 对象转换:将数组中的元素转换成对象。
javascript
const fruits = ['apple', 'banana', 'cherry'];
const fruitObject = fruits.reduce((accumulator, currentValue, currentIndex) => {
accumulator[currentValue] = currentIndex;
return accumulator;
}, {});
console.log(fruitObject); // 输出 { apple: 0, banana: 1, cherry: 2 }
10.手写深拷贝(面到这的时候真的太简单了,被kpi的感觉愈发强烈)
js
// 浅拷贝
// 创建一个对象的浅拷贝,只复制对象的直接属性,不递归复制嵌套对象的属性。
function shallowclone(obj){
// 创建一个空对象
let newobj = {}
// 遍历原对象的直接属性
for(let key in obj){
// 确保只复制对象的直接属性,排除原型链上的属性
if(obj.hasOwnProperty(key))
newobj[key] = obj[key]
}
// 返回新对象
return newobj
}
// 深拷贝
// 创建一个对象的深拷贝,递归复制对象的所有嵌套属性,包括数组和函数等复杂对象。
function deepclone(obj){
// 对null值特殊处理
if(obj === null) return null
// 特殊处理Date和RegExp对象
if(obj instanceof Date) return new Date(obj)
if(obj instanceof RegExp) return new RegExp(obj)
// 非对象类型直接返回
if(typeof obj !== 'object') return obj
// 创建新对象
let newobj = new obj.constructor()
// 遍历所有属性,递归复制嵌套对象
for(let key in obj){
if(obj.hasOwnProperty(key)){
newobj[key] = deepclone(obj[key])
}
}
// 返回深拷贝后的对象
return newobj
}
11.问了学习前端的历程以及怎么看待ai和前端的结合
最后
一面一个小时,二面还不到45分钟...?
预感不妙,实在太简单,跟面试官放大招,放手一搏了,在不搏就没机会搏了😫😫
面试官:最近在学些什么呢?
我:在学习react中,学习完后会重新去把js学习一遍,因为不管框架有多少种最后还是离不开js底层。
面试官:有什么想问的吗?
我:请问什么时候会有结果呢?
面试官:2-3天后吧。
我:我希望能够尽快出结果,因为我手头上有个滴滴的offer了,它那叫我月末过去,但我对于百度的这种顶级互联网公司真的很向往,正好临走之前百度的笔试题过了,来约面试,所以我想来试一试贵公司的招聘,想取得一个更好的学习机会😻😻
面试官:好的我理解了。
最后的最后
上午面完,下午通过我过了......
我真的只是运气好???
二面面试官小姐姐是天使!!!(超大声🎉🎉🎉!!!)
最后的最后的最后
下周三报到了,准备北漂了🎈🎈🎈
真最后了
低开高走还是高开低走?滴滴面了3小时还是有用的嘛~😂😂