面试造航母,工作拧螺丝。
如果我是一个面试官,我会问些啥问题呢。我想应该也不会问造航母那么难的问题,应该是一些基础问题和一些实际的业务问题吧。
基础问题能过得去,说明起码准备了,态度是好的😄。当然我说的基础问题,不是说知道js,html,css这几样东西,而是要熟练应用。至于框架,如今最流行的vue/react起码要很精通一个,这样即使你会的和我们当前使用的不同,也可以快速的上手,不至于要花费很多的时间去学习,学习的成本是很高的。
下面简单模拟一场面试。
一、自我介绍
大约介绍一下自己的资料(很多时候面试官都是拿到你的简历直接就来面试你了,所以对你很不了解),说一说你做了哪些项目,你又负责了哪些部分(有亮点最好,没有亮点也无妨)。这部分只要没有太多的问题就好,如果你捏造了项目,说的时候让人一听就很别扭,那就不太好了。
二、前端基础
1、html基础
常用标签、文档流、h5
展开查看
常用标签:块级元素 div, ul, li, p等;行内元素span , a , b , i , u , em;行内块元素img、input、textarea
文档流:页面排布按照文档流进行排布,position:absolute和浮动会使元素脱离标准文档流
h5:参考https://juejin.cn/post/6921886428158754829
2、css基础
行内样式、内联样式、外部引入样式、样式权重、布局(尤其flex布局模式)、怪异模式(box-sizing)、响应式
展开查看
样式权重:important > 内嵌样式 > ID > 类 > 标签 > 通配符
布局:三栏布局(父级元素display:flex,子级元素切分三栏,宽度可通过flex: 1 0 100px这样调控)
怪异模式:box-sizing: border-box | content-box
响应式布局:通过媒体查询,对应不同的页面大小进行样式适配;通过flex布局进行自适应布局
3、js基础
基础类型、原型链、es5 callback、es6、宏/微任务、promise
展开查看
基础类型: boolean, string, number, object, array, symbol, null
原型链:js通过原型链实现继承,调用方法可以通过__proto__指向的对象,调用其上挂载的方法,原型链的顶端是null
es5 callback:在es5中通过回调解决异步问题,随着逻辑的复杂程度增加,会导致回调地域(嵌套层级太深,逻辑难以理清),解决方案:promise/async await
eventloop:js任务可以划分为同步任务,微任务,宏任务,在一段代码中,将所有的代码分别划归到同步任务,微任务,宏任务,然后按照顺序执行(整个执行过程会分成一次一次的tick执行)
手写算法(call\apply\bind\快排\二分\其他)
Function.prototype.myCall = function(ctx, ...args) {
if (typeof this !== 'function') {
throw new TypeError('not function')
}
ctx = ctx || window
let result
ctx.fn = this
result = ctx.fn(...args)
delete ctx.fn
return result
}
Function.prototype.myApply = function(ctx, args) {
if (typeof this !== 'function') {
throw new TypeError('not function')
}
ctx = ctx || window
ctx.fn = this
let result
if (args && args.length) {
result = ctx.fn(...args)
} else {
result = ctx.fn()
}
delete ctx.fn
return result
}
Function.prototype.myBind = function(ctx, ...args) {
ctx = ctx || windiow
ctx.fn = this;
return function() {
const newArgs = args.concat(...arguments)
let res = ctx.fn(...newArgs)
delete ctx.fn
return res
}
}
// 快排
function quickSort (arr, start, end) {
if (start >= end) {
return
}
let left = start
let right = end
let mid = arr[start]
while (left < right) {
while (left < right && arr[right] > mid) {
right--
}
arr[left] = arr[right]
while (left < right && arr[left] <= mid) {
left++
}
arr[right] = arr[left]
}
arr[left] = mid
quickSort(arr, start, left - 1)
quickSort(arr, left + 1, end)
}
var arr = [1,3,5,9,2,4,6]
quickSort(arr, 0, arr.length - 1)
// 二分
function binarySearch (arr, num) {
let left = 0
let right = arr.length - 1
while (left <= right) {
let mid = parseInt((left + right) / 2)
if (arr[mid] > num) {
right = mid - 1
} else if (arr[mid] < num) {
left = mid + 1
} else {
return mid
}
}
return -1
}
设计模式(策略模式、工厂模式、建造者模式、单例模式、发布/订阅模式、观察者模式等)
/* 发布/订阅模式(手写eventEmitter)*/
class EventListener {
constructor (counts) {
this.events = {}
}
// 订阅
on (eventName, fn) {
if (this.events[eventName]) {
this.events[eventName].push(fn)
} else {
this.events[eventName] = [fn]
}
}
// 发布
emit (eventName) {
this.events[eventName] && this.events[eventName].forEach(fn => fn())
}
// 取消订阅
remove (eventName, fn) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(item => item !== fn)
}
}
// 订阅一次
once (eventName, fn) {
let newFn = () => {
fn()
this.remove(eventName, newFn)
}
this.on(eventName, newFn)
}
}
/* 观察者模式 */
// 被观察者
class Observed {
constructor () {
this.subs = []
}
addSub (watcher) {
this.subs.push(watcher)
}
notify () {
this.subs.forEach(item => {
item.update()
})
}
}
// 观察者
class Observe {
constructor (info) {
this.info = info
}
update () {
console.log(this.info)
}
}
let observed = new Observed()
let observe1 = new Observe('观察者1')
let observe2 = new Observe('观察者2')
observed.addSub(observe1)
observed.addSub(observe2)
observed.notify()
/* 单例模式 */
function instance () {
var instance
return function () {
if (instance) {
return instance
} else {
return new Model('这是一个新的实例')
}
}
}
4、vue基础
模板编译、数据驱动、diff更新、手写双向绑定原理、v-model语法糖、父子组件通信、父子组件渲染生命周期顺序、vuex的使用、vue-router原理
展开查看
1、vue三大块:模板编译、数据驱动、diff更新
2、双向绑定核心:Object.defineProperty
3、v-model语法糖:是input事件和value属性的合并语法糖(.sync也是语法糖)
4、父子组件通信:props/emit; $attrs/$listeners; provide/inject; $parent/$children; $refs; vuex; eventBus
5、父子组件生命周期顺序:父beforeCreate - 父created - 父beforeMount - 子beforeCreate - 子created - 子beforeMount - 子mounted - 父mounted(父要把子包在中间,更新相同)
6、vuex中mutations和actions区分:mutations中为同步任务,actions中可以有异步任务
7、vue-router核心:对使用者核心其实可以理解为一个列表/对象(要看如何去理解)
手写vue双向绑定
class Vue {
constructor (options = {}) {
this.data = options.data
this.init()
}
init () {
if (!this.data || typeof this.data !== 'object') {
return
}
return new Observer(this.data)
}
}
class Observer {
constructor (data) {
this.data = data
this.walk(this.data)
}
walk (obj) {
if (typeof obj !== 'object') return obj
Object.keys(obj).forEach(key => {
this.defineReactive(obj, key, obj[key])
})
}
defineReactive (obj, key, value) {
this.walk(value)
let dep = new Dep()
Object.defineProperty(obj, key, {
enumrable: true,
configurable: true,
get () {
Dep.target && dep.addSub(Dep.target)
return value
},
set (newValue) {
if (value === newValue) return
if (typeof newValue === 'object') this.walk(newValue)
value = newValue
dep.notify()
}
})
}
}
class Dep {
constructor() {
this.subs = []
}
addSub(watcher) {
this.subs.push(watcher)
}
notify(data) {
this.subs.forEach(sub => sub.update(data))
}
}
class Watcher {
constructor (vm, key, cb) {
this.vm = vm // vm:表示当前实例
this.key = key // key:表示当前操作的数据名称
this.cb = cb // cb:表示数据发生改变之后的回调
Dep.target = this; // 全局唯一
this.oldValue = this.vm.$data[key] // 保存变化的数据作为旧值,后续作判断是否更新
Dep.target = null
}
update () {
let oldValue = this.oldValue
let newValue = this.vm.$data[this.key]
if (oldValue != newValue) { // 比较新旧值,发生变化才执行回调
this.cb(newValue, oldValue)
}
}
}
5、react基础
只会写业务,不太了解react有哪些基础😄
6、webpack基础
webpack概念、loader、plugin、用过哪些、loader执行顺序、是否可以手写简单的loader\plugin
7、备注
上述内容应该掌握,面试应该经常问吧,有关算法的内容,应该能手写。
三、项目相关
1、承担了项目的哪些部分
2、在项目中遇到了哪些难点
3、对项目做了哪些优化(如速度/大小/用户体验)
......
上面的内容基本上是如果我作为一面面试官可能会问到的一些问题吧,你觉得呢,欢迎补充,如果上面的有问题,也可以提出来😄