(零基础开始自学前端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); // 35. 事件循环,将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发来消息称二面通过,希望好运吧,因为面试经验少太容易紧张,需要再磨砺自己
全部评论
(6) 回帖