很久以前面的,第一次面试,失败原因总结:过于紧张,基础不扎实。
自我介绍。
讲一讲Vue的源码。我直接从Vue实例的创建开始,打算一步步的解释,面试官打断说让我讲一下Vue的架构(应该是指Observer, Compiler, Watch这三部分吧?)
浏览器的渲染原理。
我以为问的是生成DOM渲染树,布局和绘制,以及回流和重绘的知识点。结果面试官又问我:HTML是怎么转换成DOM树的,然后我就蒙圈了...
之后谷歌了一下,大概的步骤如下
Script标签对浏览器的阻塞问题。参考
Script脚本的执行会阻塞html的解析,外链script的下载也会阻塞。
那么多个script标签代码的执行,也必然是顺序执行,多个script外链的请求到底是串行还是并行的呢?
比较容易混淆,所以容易会认为请求是串行的,因为
<script type="text/javascript" src="./test.js"></script>
<script type="text/javascript" src="./test2.js"></script>
可能会认为下面html的文档的解析要等到上面script执行完才会开始,因为会阻塞。
但其实请求是并行的。
因为html解析成dom的时候会先进行预解析。
前端常见加密,比如MD5。
当初面的时候很紧张,没答好。
问:闭包会造成什么?
结果我脱口而出,会造成内存泄漏...但实际上闭包会造成内存泄漏这种说法是由于以前IE浏览器的BUG。现在很多人都说闭包会造成内存泄漏,实在是以讹传讹,瞎写代码才会(逃
性能优化手段。
以下是春招的面试经历
一面过了,感谢面试官
介绍一下自己,什么时候开始学习前端的,学习前端的方式,平时都看过哪些书?
大概讲了一下自己的学习经历,看过什么书籍。
问高程上我觉得印象深刻的地方?
我随便说了两个,原型链和this,面试官随便问了点相关知识。
实现私有的方法/属性,我只回答了两种,一个是提前约定好的私有变量,比如_
开头的变量;或者用闭包实现。
不过答的时候说的有点混乱。
闭包:
// 闭包一,实例的私有属性
class Person {
constructor() {
let value = 233
this.getValue = function() {
return value
}
}
}
// 闭包二,原型对象的私有属性
const Person = (function () {
let value = '111'
class Person {
getValue() {
return value
}
}
return Person
})()
也可以使用Symbol来实现(比较推荐)
const Person = (function () {
let s = Symbol()
class Person {
constructor() {
this[s] = '111'
}
getValue() {
return this[s]
}
}
return Person
})()
ES5实现继承的方法,构造继承,原型链继承,组合继承,寄生组合继承。
this的原理,call,apply,bind的区别。
垂直居中的几种方法。
为什么下面的margin可以居中?不太清楚,只知道有这种技巧。
.outer {
display: flex;
}
.inner {
margin: auto;
}
块级元素中,当margin-top和margin-bottom为auto时,他们的值为0,所以无法实现垂直居中。
而在flex内部,当我们设置margin: auto
的时候,会将剩下的空闲空间(水平与垂直)分配给该元素的margin。
因此,借由flex
和margin
,我们可以更加简单的实现某些功能,比如一个普通的导航栏
<ul class="g-nav">
<li>导航A</li>
<li>导航B</li>
<li>导航C</li>
<li>导航D</li>
<li class="g-login">登陆</li>
<li>注册</li>
</ul>
<style>
.g-nav {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.g-nav li {
padding: 0 20px;
}
.g-login {
margin-left: auto;
}
</style>
margin-top为负值,除了绝对定位还有哪些地方碰到过?说了个双飞燕/圣杯布局,不太清楚其他的应用场景。
问我平时写不写HTML标签/CSS,这问题有点懵。然后面试官解释说,有的地方写项目代码分工明确,有的人只写CSS,有的人只写JS。
问前端语义化标签,什么时候你会用到这些标签,语义化标签的好处/作用?
这个不太清楚怎么回答,我说了个对搜索引擎友好?晚点查一查。
怎么用正则判断当前域名是否为qq.com,或者xxx.qq.com。
我用的split做的...很繁琐,边界条件的检查也很麻烦,还是正则好。
正则的写法
function isUrl(url) {
return /^https?:\/\/(.+\.)?qq\.com/.test(url)
}
isUrl(location.href)
响应状态码,200(from disk cache),200(from memory cache),304的区别。
见本博客的Http缓存一节。
至于from disk cache 和 from memory cache的区别。
比如第一次打开新页面(有缓存),资源会从硬盘中读取;而如果在已经打开的页面刷新,资源会从内存中读取。
Etag是什么?
通常就是一个资源的哈希值吧。
try...catch...中如果异步代码出错怎么办?如:
try {
throw new Error('111')
} catch (e) {
console.log(1)
}
// 捕捉到异常,输出1
try {
setTimeout(() => {
throw new Error('222')
})
} catch (e) {
console.log(2)
}
// 未能捕捉到异常,不输出2
怎么办?
async function A() {
try {
await new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('222'))
})
})
} catch (e) {
console.log(e)
}
}
当await后面的promise状态为reject时,会抛出错误。所以我们可以搭配async/await和promise来进行异步的错误捕捉。
为什么移动端以前有300ms的问题,原理?fastclick的原理答不上来。
移动端,当用户点击屏幕的时候,先后触发touchstart, touchmove, touchend, click。其中touchend和click的间隔为300秒,这是为了处理移动端的双击缩放。
fastclick原理。在touchend阶段,会调用e.preventDefault,然后使用document.createEvent创建一个事件,并在目标元素上触发。
XSS的原理,如何防御XSS?为什么换成实体字符就好了?
虽然我大概了解一些,但总感觉答的不是很好。
字符<会被当成标签,而实体字符只会当成纯粹的文本。
找出数组中n项,n项的和为m。
解答过程我写在编程题一节。
分享屏幕,写代码。
问我的前端是怎么学习的...问了挺多,各种细枝末节的东西。
创建一个 Person 类,其包含公有属性 name 和私有属性 age 以及公有方法 setAge ;创建一个 Teacher 类,使其继承 Person ,并包含私有属性 studentCount 和私有方法 setStudentCount 。
const Person = (function () {
let s = Symbol('age')
class Person {
constructor(name) {
this.name = name
}
setAge(newAge) {
this[s] = newAge
}
getAge() {
return this[s]
}
}
return Person
})()
const Teacher = (function () {
let studentCount
const setStudentCount = (count) => {
studentCount = count
}
return class Teacher extends Person {
constructor(name) {
super(name)
}
set(count) {
setStudentCount(count)
}
}
})()
以上是我面试的时候写出来的,但写的挺怪的...回来想了想,可以改写如下代码。
const [Person, Teacher] = (function () {
const s = Symbol('age')
const c = Symbol('studentCount')
const setStudentCount = Symbol('setCount')
class Person {
constructor(name, age) {
this.name = name
this[s] = age
}
setAge(age) {
this[s] = age
}
}
class Teacher extends Person {
constructor(name, age, count) {
super(name, age)
this[c] = count
}
[setStudentCount](count) {
this[c] = count
}
set(count) {
this[setStudentCount](count)
}
}
return [Person, Teacher]
})()
输入框输入值后,数组内找值, 返回匹配的字符串。类似百度输入框的效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
margin: 0;
position: relative;
height: 100vh;
}
.center {
box-sizing: border-box;
width: 400px;
padding: 4px 6px;
position: absolute;
top: 35%;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
}
.input {
padding: 10px 4px;
}
.list div {
padding: 10px 6px;
border: 1px solid #ddd;
}
.list div:hover {
background: grey;
cursor: pointer;
}
.list div:not(:last-child) {
border-bottom: 1px solid #ddd;
}
</style>
</head>
<body>
<div class='center'>
<input type="text" class='input'>
<div class='list'>
</div>
</div>
<script>
const dataList = ['航空母舰','航空吴六', '无关数据', '航x空', '航空飞行器']
const el = document.querySelector('.input')
const list = document.querySelector('.list')
function debounce(fn, wait) {
let timer
return function () {
timer && clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this)
}, wait)
}
}
function getData() {
list.innerHTML = ''
let value = el.value
let ret = []
for (let i of dataList) {
if (i.includes(value)) {
ret.push(i)
}
}
ret.forEach(item => {
let el = document.createElement('div')
el.innerText = item
list.appendChild(el)
})
}
el.addEventListener('input', debounce(getData, 1000))
</script>
</body>
</html>
请写一个函数,计算一篇英文文章中出现次数最多的单词及出现次数。
function getWordAnd(str) {
let arr = str.split(/[,\.\s]/)
let map = new Map()
let ret = [[], 0]
arr.forEach(word => {
if (word !== '') {
if (map.has(word)) {
let times = map.get(word)
map.set(word, times + 1)
} else {
map.set(word, 1)
}
}
})
for (let [i, j] of map) {
if (j > ret[1]) {
ret[0] = [i]
ret[1] = j
} else if (j === ret[1]) {
ret[0].push(i)
}
}
return ret
}
问我有什么上线的项目,我说以前的现在都下线了...只有源码。简历是你自己写的吗,答我是fork的...博客呢?我用vuepress搭的,以前写过ghost的主题。(总之就是被吐槽项目经历太少
有什么想问的吗?
面试官觉得我哪里需要提高,面试官说我还行,不过项目经历太少了。然后我说所以我想去实习提高项目经历,被教训了一顿...真正的高手自己一个人随便写项目,根本不用实习。(唉,我太菜了
希望能过吧,我一定好好写项目...
表现贼差,结果等了一个星期竟然过了!我爱腾讯!
自我介绍
印象深刻的项目,负责怎样的角色
考不考研
有没有女朋友
还有哪些在流程中
实习的情况,课多不多,可不可以远程实习
想问什么
口头offer
感谢腾讯!
自我介绍
移动端和PC端的一些区别,技术的选型的区别,兼容性方面的问题。Iphone5和Iphone6的像素不同应该如何处理。
Webpack的按需加载 imort() 和 代码分割
单页应用和多页应用的优缺点对比。
单页应用有SEO问题,那么前端通常如何解决SEO的问题呢,没答上来(服务端渲染和同构)
单页应用的好处有哪些,有点忘了(囧),前后端分离。
三列布局,flex,圣杯布局,双飞燕布局。
虚拟DOM的优缺点
当我们使用虚拟DOM,虚拟DOM的Diff后,我们还是要操作真实的DOM。
优点:保证性能的下限。我们操作虚拟DOM后,框架底层会自动操作真实的DOM,因此我们可以保障性能的最下限
缺点:正是同样的道理,虚拟DOM可以保障性能的最下限的前提,是其失去了性能的最上限。如果有一个大牛,代码非常牛逼,它操作真实DOM就可以实现性能最大化。但是虚拟DOM却失去了最大化的可能性。
编程题
//评测题目: 无
/*
题目
实现一个方法,用于比较两个版本号(version1、version2)
如果version1 > version2,返回1;如果version1 < version2,返回-1,其他情况返回0。版本号规则`x.y.z`,xyz均为大于等于0的整数,至少有x位
输入输出样例
(如果题目不是实现函数,可以用其他更直观的方法描述预期行为)
样例一
输入:compareVersion('0.1', '1.1.1')
输出:-1
样例二
输入:compareVersion('13.37', '1.2 ')
输出:1
样例三
输入:compareVersion('1.1', '1.1.0')
输出:0
*/
function compareVersion(v1, v2) {
let arr1 = v1.split('.')
let arr2 = v2.split('.')
let maxLength = Math.max(arr1.length, arr2.length)
for (let i = 0; i < maxLength; i++) {
let a = arr1[i] || 0 // 开始给他看的时候没加这个。因为长度不一定一样,可能是undefined
let b = arr2[i] || 0
if (a > b) return 1
if (a < b) return -1
}
return 0
}
唠嗑。阿里钉钉最近的情况。
阿里插曲1:面的时候表现很不错,结果之后发现流程上变成“已回绝”,失眠,难受了一晚上。
结果第二天的时候和我说,只是流程打回去了而已,对我没影响(???难受一晚上也叫没影响?)
阿里插曲2:电话打过来问我有没有注册钉钉,我说没有(= =事后发现自己是有注册过的,囧)...
自我介绍。问成绩。
计算机网络七层介绍,TCP和UDP使用场景的区别,DNS的TTL是什么。
说我用谷歌,那我是怎么翻墙的... 。我说我之前搭过,后来挂了就用别人的。面试官问我为什么我的就挂了,其他人的就没挂(谁的机场这么稳定?)。
键盘和CPU是如何相连的,交互原理。
市面上硬盘主要是什么接口。
一个CPU是怎么进行进程直接的切换,以及资源是如何分配的,CPU怎么知道切换哪个进程。
网卡是如何做到同时听音乐和下载东西的。
图论,有限图中两点最小距离的算法。
常见排序算法
前端性能优化
印象深刻的项目
兴趣爱好。我说动画啥的,他说他指的是硬件相关的爱好...有没有写过什么机器人呀之类的。然后被吐槽我的爱好优点像文科生(这算不算刻板印象?我这个人爱好广泛的很,小说、动画、漫画、音乐、游戏、电影我都很喜欢,我还想学好英语/日语,想学好画画,围棋业余三段,热爱日麻,还玩过一阵子德州扑克,经常看新闻,甚至开始对历史感兴趣了,特别爱任天堂游戏。难道理科生就一定要喜欢硬件吗?)
阿里插曲3:交谈中大概谈到我写的博客,很明显感觉到面试官的轻视,他用了“从其他地方复制过来”这一词,仿佛这些东西都是没过我脑子就复制过来一样,服了。
二面很久后突然说要面,这一面是我面试凉的最快的一次,随便写点。
自我介绍,介绍完直接发个我五道题(太吓人了)
用CSS实现一个开关样式,hover时触发,滑块为正方形。
写出来了,但不够优雅...以下是修改后的代码
<div class='contain'>
</div>
<style>
.contain {
--padding: 4px;
--height: 40px;
position: relative;
width: 100px;
height: var(--height);
padding: var(--padding);
background: grey;
transition: all .3s;
}
.contain:hover {
background: green;
}
.contain::before {
content: '';
position: absolute;
z-index: 1;
right: calc(100% - 40px - var(--padding));
height: var(--height);
width: var(--height);
transition: all .3s;
background: #fff;
}
.contain:hover::before {
right: var(--padding);
}
</style>
// 写出下面这段代码打印的结果
var result = [];
var a = 3;
var total = 0;
function foo(a) {
var i = 0;
for (; i < 3; i++) {
result[i] = function() {
total += i * a;
console.log(total);
}
}
}
foo(1);
result[0]();
result[1]();
result[2]();
// 3, 6, 9
// 因为i始终是3,差点上钩
// 写出下面这段代码打印的结果
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
});
console.log('script end');
// 答案
script start
async1 start
async2 start
async2 promise
promise1
script end
promise2
promise3
async1 end
setTimeout
// 但我之前把async1 end写在promise2前面了,想了想才发现自己错在哪里。
function A() {
return new Promise((r) => {
r()
})
}
console.log(A())
// Promise<resolved>
async function A() {
return new Promise((r) => {
r()
})
}
console.log(A())
// promise<pending>
// 可以看到async函数和普通函数最终返回的promise状态是不同的。
// 我们可以再做一个实验。
let p
function A() {
p = new Promise((r) => {
r()
})
return p
}
let a = A()
console.log(a === p) // true
let p
async function A() {
p = new Promise((r) => {
r()
})
return p
}
let a = A()
console.log(a === p) // false
// 个人观点:
async function A() {
return new Promise() {}
}
// 执行函数A返回的promise初始状态是pending,并且会生成一个微任务放进微任务队列,当执行该任务后状态才会改变
// 现在终于可以回答下面两个代码为什么输出顺序不同了
async function A() {
await B()
console.log('a')
}
function B() { // 此时不是async函数
return new Promise((r) => {
r()
})
}
A()
new Promise((resolve) => {resolve()}).then(() => {
console.log('b')
})
// a b
// ------------ 分割线 --------------
async function A() {
await B()
console.log('a')
}
async function B() {
return new Promise((r) => {
r()
})
}
A()
new Promise((resolve) => {resolve()}).then(() => {
console.log('b')
})
// b a
// 实现一个二进制加法,输入输出均为二进制字符串
function binaryAdd(num1: string, num2: string): string {
// TODO
}
//Example
binaryAdd('1010', '111') // '10001'
// 其实就是两数相加,只是换成了二进制而已
function binaryAdd(num1, num2) {
let carry = 0
let ret = []
const [len1, len2] = [num1.length, num2.length]
const maxLen = Math.max(len1, len2)
num1 = num1.padStart(maxLen, '0')
num2 = num2.padStart(maxLen, '0')
let arr1 = num1.split('')
let arr2 = num2.split('')
for (let i = maxLen - 1; i >= 0; i--) {
let [n1, n2] = [arr1[i], arr2[i]].map(i => Number(i))
let sum = n1 + n2 + carry
carry = sum >= 2 ? 1 : 0
sum %= 2
ret.unshift(sum.toString())
}
if (carry) ret.unshift('1')
return ret.join('')
}
// 实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
class Scheduler {
add(promiseCreator) {
// TODO
}
// TODO
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler();
const addTask = (time, order) => {
scheduler.add(() => timeout(time))
.then(() => console.log(order))
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4
// 一开始,1、2两个任务进入队列
// 500ms时,2完成,输出2,任务3进队
// 800ms时,3完成,输出3,任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4
// 没做出来,讲了个大概思路,面试官说这道是附加题,写法比较巧妙。
浏览器/Node的事件循环
浏览器的渲染机制
CSRF的原理和防御
有什么想问的吗
自我介绍
把数字用千分位分割,如15000000分割为15,000,000
function transform (num) {
let arr = []
while (num >= 1000) {
let value = num % 1000
num = num / 1000
if (value >= 100) {
value = '' + value
} else if (100 > value && value >= 10 ){
value = `0${value}`
} else {
value = `00${value}`
}
arr.unshift(value)
}
num = '' + num
arr.unshift(num)
console.log(arr.join(','))
}
transform(15000000)
问:除了用数字,还有什么方便的方法吗?
之后查了资料,发现可以通过toLocaleString
或正则来实现
正则: num.toString().replace(/\d{1,3}(?=(?:\d{3})+$)/g, '$&,')
HTTP2.0和1.1的对比,优点?缺点?
服务端推送的资源放在哪里?
浏览器缓存,你的JS资源是如何使用不同的缓存策略的呢?不太清楚,我说的用webpack进行代码分割,然后分别使用不同的策略
通常过期时间设置为多久?
了解哪些跨域。其中cors,post方法算简单请求吗,带cookie需要预检吗?
之前有提到service worker,有了解吗(没,自己给自己挖坑...)
Node有了解吗,一些Node的优缺点(密集型),Node是单进程还是多进程。
node是i/o密集型,但处理cpu密集型任务很吃力。面试的时候好像答反了。。。
Koa,Egg有了解过吗。
算法题:
实现js方法,查找第一个缺失的正整数。 时间复杂度O(n) ,空间复杂度 O(1)
Example 1:
Input: [1,2,0] Output: 3
Example 2:
Input: [3,4,-1,1] Output: 2
Example 3:
Input: [7,8,9,11,12] Output: 1
假设传入的数组长度在10000以内。
提示:数学上最小正整数是1
不会,哭了。
前端方向你希望往哪边发展?我说了webgl,因为比较花哨。
有什么想问的吗?
问自己的不足。
问:你们阿里的后端用的哪些(面试官惊了:同学你串台了,这里不是阿里...)
自我介绍
介绍项目,觉得有哪些难点。
Node事件循环,Koa的洋葱圈模型,Express和Koa的区别
Node中有哪些全局变量
项目通常如何部署,pm2,很久以前用过。
Vue和React对比
Vuex的单向流动是如何实现的,我说熟悉Redux,扯到了redux的一些东西。
HTML5有哪些新属性,Doctype的作用,Storage和cookie的对比,localStorage和sessionStorage的对比。
Flex有哪些属性
ES5的继承
跨域的介绍
sameSite属性,最近默认值变成了lax
linux用过吗,如何删某端口上的进程,ps aux | grep node查看node的进程。
git有用过吗,git rebase知道吗
一个有序,可重复的数组[1, 2, 3, 3, 3, 3, 4, 5, 5, 6],找到5最后的位置,空间复杂度logn
二分,不让用lastIndexOf
有什么想问的吗? 问技术栈,美团主要用的vue,也有rn,小程序。
都是些基础问题。
面累到了,就算能让我过我也不想面了...
二面过了挺久,突然打个电话说我过了,不过offer被我拒了。
六个小时结束了akara的秋招。
自我介绍,介绍项目。
几个小题目,看代码说结果
[] == ![]
和[] == []
的值(事后发现自己完全答反,万恶的隐式转换,神奇的JS)
new 操作符原理
盒模型 content-box
和border-box
display:none;visibility: hidden;opacity:0
的区别
事件模型,看代码说结果
状态码301,302,304
知道referer
头部吗,直接请求服务器时referer
是多少,顺便聊了其在CSRF中的作用
后端怎么让浏览器中的Cookie过期
CSS动画,transition
和animation
,哪一个性能更好
Koa和Express的区别,以及Koa中间件的原理
Vue的v-model
的原理,Vue实例是怎么拿到data属性的
Hash和History模式的区别,原理
React的Fiber的原理,知道怎么实现的吗,是否了解Hook的实现原理
聊了一下Redux和React-Redux
给定一个字符串,输出该字符串所有排列的可能。如输入“abc”,输出“abc,acb,bca,bac,cab,cba”。
function fullpermutate(str) { var result = []; return result;}
多行字符串转二维数组
const str=`
1 21 3
4 5 6
7 8 9
`
[
['1', '12', '3'],
['4', '5', '6'],
['7', '8', '9'],
]
获得页面所有节点数
有什么问我的吗
聊了一下项目
算法题。判断堆栈的出栈顺序是否合理。
validStack(inArr, outArr)
两个同域的页面之间的通信。postMessage,storage(之前没了解过,原来这个可以监听的?)
如何统计用户的浏览时长,要考虑到用户会切换页面,缩放页面等。多个点进行上报时,如何区分这些数据来自于同一个会话?
实现一个sleep方法
函数防抖,函数节流
HTTP和Websocket的联系
重排,重绘,合成层
了解过puppeteer吗
算法比较拉跨...
自我介绍
聊项目的一些东西,难点啥的,解决方案。
写代码。倒计时 截止时间 2020年11月11日 0点
显示 “剩余XX天XX时XX分XX秒”
每秒刷新一次
怎么学习前端的,聊一下接下来的打算
基本都是聊家常了。