字节跳动企业应用前端日常实习一二面面经
(零基础开始自学前端4个月,一面试就千疮百孔)
(每一场问题都不多,主要是写代码,感觉是我太墨迹了的关系)
一面(7.05)
1. 你说你读过vue的源码,对什么比较熟悉,有过自己的理解和实践(我回答了响应式那块,然后就让我写一下,这个我当时自己有准备过所以还好)
// 发布者,订阅中心
class Dep {
constructor() {
this.subs = [];
}
// 此处的sub是依赖收集时,Dep.target也就是当前正在执行的watcher
addSubs(sub) {
if (this.subs.indexOf(sub) < 0) {
this.subs.push(sub);
}
}
notify() {
this.subs.forEach(item => item.update())
}
}
Dep.target = null;
// 订阅者
class Watcher {
constructor(obj, key, updateCb) {
this.data = obj;
this.key = key;
this.updateCb = updateCb;
this.value = this.get()
}
get() {
Dep.target = this;
this.value = this.data[this.key];
Dep.target = null;
return this.value;
}
update() {
const oldValue = this.value;
const newValue = this.get();
this.updateCb(newValue, oldValue);
}
}
// observer类 劫持数据
class Observer {
constructor(obj) {
this.data = obj;
if (this.data == null || typeof this.data !== "object") {
return;
}
if (Array.isArray(this.data)) {
this.observeArray();
} else {
this.walk();
}
}
walk() {
for (let i in this.data) {
this.defineReactive(this.data, i);
}
}
observeArray() {
for (let i = 0; i < this.data.length; i++) {
observe(this.data[i]);
}
}
defineReactive(obj, key) {
let val = obj[key];
observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSubs(Dep.target)
}
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
observe(val);
dep.notify();
}
})
}
}
// 数据监测方法
function observe(data) {
new Observer(data);
}
// 写一个最简单的例子
const obj = {
a: 1
};
observe(obj);
new Watcher(obj, "a", (newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal + '\n');
});
obj.a = 2;
obj.a = 3; 2. 你的项目里有实现过分页组件么,写一个试试(我一开始理解错了以为是下拉加载后端的分页请求,意识到错了后改提了吸顶组件和懒加载图片等)然后就写了下吸顶组件:(当时写的时候很紧张,复盘的时候都代码看不下去了)
<template>
<div class="sticky" :style="Position">
<div class="sticky-wrap">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "sticky",
data() {
return {};
},
computed: {
Position() {
var position = this.support("position", "sticky")
? "sticky"
: "relative";
return "position:" + position;
},
},
mounted() {
if (this.support("position", "sticky")) {
return;
}
const sticky = this.$el;
const Wrap = this.$el.querySelector(".sticky-wrap");
const util = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
Wrap.style.position = 'absolute'
} else {
Wrap.style.position = 'fixed'
}
})
}, {
// 这个参数忘记了,大概是目标元素的可视比例
})
util.observe(Wrap)
},
methods: {
support(attr, value) {
// 主要是测试是否兼容sticky
}
},
};
</script> 3. setValue(obj, key, value) 输入 a.b.c.d (当时原题的key是“a[0].b[1].c”这种,因为key是字符串,看我一下没头绪就简化了一下,我太菜了,简化后就比较简单实现了,key做split处理,for循环遍历下去,像前缀树一样最后赋值) 输出:
var a = {};
setValue(a, "b.c", 10);
a = {
b: {
c: 10
}
} 4. 什么是BFC,如何触发BFC,应用场景 5. 实现debounce函数(终于来了道秒杀题,分立即执行和延迟执行)
function debounce(fn, wait, immediate) {
let timer = null
return function () {
let context = this
let args = arguments
if (timer) clearTimeout(timer)
if (immediate) {
let call = !timer
timer = setTimeout(() => {
timer = null
}, wait)
if (call) fn.apply(context, args)
} else {
setTimeout(() => {
fn.apply(context, args)
timer = null
}, wait)
}
}
} 6. 遍历dom树并打印相应路径
<div id="root">
<p>p1</p>
<span>
<span>span2</span>
</span>
<p>p2</p>
</div>
traverse(document.getElementById('root'));
=>
["DIV"]
["DIV", "P"]
["DIV", "SPAN"]
["DIV", "SPAN", "SPAN"]
["DIV", "P"]
function traverse(root) {
let res = []
function dfs(root, queue) {
queue.push(root)
console.log(queue)
if (!root.childNodes) {
res.push(queue)
queue = [root]
return
}
const childs = root.childNodes
for (let item in childs) {
dfs(item, queue)
}
}
dfs(root, [])
return res
} 最后时间来不及了,提问环节我征求了下面试官小哥的建议,鼓励了一下我,代码能力还需要加强
-----------------------------------------------------------------------------------------------------------------------------二面(7.06 严重怀疑自己)
1. 笛卡尔乘积: fn([['a', 'b'], ['n', 'm'], ['0', '1']]) => ["an0", "an1", "am0", "am1", "bn0", "bn1", "bm0", "bm1"] (我拘泥于用reduce来写,结果忘了API格式一直报错,下来复盘时稍微一改就对了)
function fn(arr) {
return [].reduce.call(arr, (pre, next) => {
let res = []
pre.forEach(a => {
next.forEach(b => {
let temp = '' + a + b
res.push(temp)
})
})
return res
})
}
console.log(fn([
['a', 'b'],
['n', 'm'],
['0', '1']
])) 2. 实现u.console("breakfast").setTimeout(3000).console("lunch").setTimeout(3000).console("dinner") (这道题卡了一会儿,被提示用promise后又写了一会儿,直接用Promise.prototype.setTimeout这样写一直没实现延时功能,无奈先跳过了,下来复盘才开窍)
4. 原型链题,看输出
// 其实也就是链式调用延时器实现sleep效果:breakfast -> 3s后 -> lunch -> 3s后 -> dinner
class U {
constructor() {
this.queue = Promise.resolve()
}
console(str) {
this.queue = this.queue.then(() => {
console.log(str)
})
return this
}
setTimeout(wait) {
this.queue = this.queue.then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, wait)
})
})
return this
}
}
let u = new U() 3. tcp和udp的区别(常见八股文)4. 原型链题,看输出
var A = function () {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
n: 2,
m: 3
};
var c = new A();
console.log(b.n); // 1
console.log(b.m); // undefined
console.log(c.n); // 2
console.log(c.m); // 3 5. 事件循环,将async await改写为promise形式,再判断输出顺序async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeOut')
}, 0)
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
// 改写:
function async1() {
return new Promise((resolve, reject) => {
console.log('async1 start')
async2()
resolve()
}).then(() => {
console.log('async1 end')
})
}
function async2() {
return new Promise((resolve, reject) => {
console.log('async2')
resolve()
})
}
// 输出顺序:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeOut 反问环节同样也是建议我多去写项目锻炼代码能力,问了下部门是属于B端还是C端,解释说是B端,但实际从工作角度上来讲应该属于C端今天二面结束时我的内心是平静的,一个小时时间就只进行了5道问题,应该是凉了,7点HR发来消息称二面通过,希望好运吧,因为面试经验少太容易紧张,需要再磨砺自己

科大讯飞公司氛围 472人发布