前言
身为一名大三前端练习生的我,抱着对金三银四期望,开始在boss上疯狂的投简历。只为求一个前端实习生的岗位。终于在我一个星期不懈的努力下,约到了杭州一个中厂的面试,我暂且把该公司称为 A。下面我就是分享一下我的首次面试经历吧。
流程
1. 自我介绍
A 公司的人事约的早上十点开始,由于是我的第一次面试,所以我还是非常紧张的。那天我在寝室忐忑的等到了十点,面试也正式开始了。首先面试官进行了自我介绍。接下来该我了。我巴拉巴拉的说了我的基本信息、技术栈、项目经历等。就这样,自我介绍在我结结巴巴的介绍中结束了。
2. 项目经历
在做完自我介绍后,面试官开始对着我的简历问我自己做的项目的问题,一些比较基础的问题我在这里就略过了。给大家分享一个比较经典的问题。
2.1 你项目中的markdown是怎么使用的?
由于我在自己写的项目中用到了markdowm文档,所以面试官顺势就问了这个问题。
我的回答: 我现在我的项目中安装了用于做markdown的包,然后在项目中引入,在将该包的函数调用将markdown转化为html结构展示在页面上。(但我忘了那个函数叫啥。。。)。
补充:具体用法如下:
// html
<a-textarea v-model:value="writeContent" placeholder="输入文章内容. . ." class="input" />
<div class="markdown-body show content" v-html="showContent">
// js
import { marked } from 'marked'
const showContent = computed(() => {
return marked(data.writeContent, { sanitize: true })
})
3. 关于Vue的问题
由于我学的框架Vue,所以面试官也毫不留情的对我vue的知识进行炮轰。
3.1 说说 v-if 和 v-show 的区别
我的回答: v-if在渲染时,v-if的dom的不会被加载,v-show的dom结构会被加载,然后再用 css属性 display: none 对该dom结构隐藏。
补充:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换。(果然就是比我说得好。)
3.2 Vue的双向数据绑定的实现
我的回答:(额,当时说的实在太结巴了, 所以我们就直接来看补充吧。)
补充:
- 实现一个监听器Observer ,对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者;
- 实现一个解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
- 实现一个发布订阅模型Watcher+Dep,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图。
具体代码实现(简洁版):
let _Vue = null
class Store {
constructor (options) {
// state
this.vm = new _Vue({
data: {
state: options.state
}
})
// getters
let getters = options.getters || {}
this.getters = {}
Object.keys(getters).forEach(getterName => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return getters[getterName](this.state)
}
})
})
// mutations
let mutations = options.mutations || {}
this.mutations = {}
Object.keys(mutations).forEach(mutationName => {
this.mutations[mutationName] = (arg) => {
mutations[mutationName](this.state, arg)
}
})
// actions
let actions = options.actions || {}
this.actions = {}
Object.keys(actions).forEach(actionName => {
this.actions[actionName] = (arg) => {
actions[actionName](this, arg)
}
})
}
dispatch(method, arg) {
this.actions[method](arg)
}
commit = (method, arg) => {
// console.log(this);
this.mutations[method](arg)
}
get state() {
return this.vm.state
}
}
let install = function(Vue) {
_Vue = Vue
Vue.mixin({
beforeCreate() {
if (this.$options && this.$options.store) { // this.$options读取到根组件
this.$store = this.$options.store
} else { // 子组件
this.$store = this.$parent && this.$parent.$store
}
}
})
}
let Vuex = {
Store,
install
}
export default Vuex
3.3 VueRouter的使用方法
我的回答: 首先在vue项目安装一个 VueRouter包,然后我们创建一个router文件专门配置项目的路由。然后再将 routes 数组抛出,在选择路由模式,然后在被Vue实例对象use一下就可以用了。
补充:我们还要new一下VueRouter 创建一个路由实例,传入 routes、mode、base在将这个实例抛出给 Vue实例use。
3.4 接口请求可以写在Vue项目那些生命周期
我的回答:可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
补充: 在实际应用中也可以放在 beforeCreate中, 因为接口请求是异步操作,执行需要时间。
3.5 怎么做移动端的适配
我的回答:(当时第一时间想到的是媒体查询)在css对该容器进行媒体查询,根据不同的页面宽度来调节容器的大小。
补充:
- 媒体查询 + rem
- rem + flexbox(插件)
- vm、vh、%
4. 关于JS的问题
在问完Vue的问题之后,面试官开始了对 JS的炮轰。
4.1 数组加值的方法
我的回答: 前后加值: unshift、 push、 中间加值:splice(), 如果数组是排好序的也可以用for循环。
补充: 无
4.2 阶乘实现的方法
我的回答:我们可以采用for循环,或者递归的方法。
补充: 代码实现:
let jiehcheng = (n) =>{
if( n === 2) {
return 2
}
return n * jiehcheng(n-1)
}
4.3 计算字符串a 在 b中出现了几次
我的回答: (当时我没想起来滑动窗口,所以回到的不是很好,我们直接来看补充吧。)
补充:
let appear = (a, b) => {
let len = a.length
let conut =0
for(let i=0; i <b.length -len; i++) {
if(a === b.slice(i, i+len)) {
conut ++;
}
}
return conut
}
4.4 如何实现给字符串加上前缀。
我的回答; 写一个方法,在方式中接收一个字符串为参数, 再将这个字符串添加前缀并返回。(显然这个方法是很笨的,也不太行)
补充:
function newString(string) {
return new String(string) + 'qianzhui..'
}
let string = newString('adda')
4.5 你做过榜单, 怎么实现榜单更新。
我的回答: 如果榜单的数据发生变化,我们可以先在前端做好榜单的排序,返回给数据库,然后再将数据请求回来重新渲染。
补充: 如果前端点击事件导致榜单数据发生改变,我们先让页面数据发生变化,再向后端发送数据已经更新的请求去更新数据库。
4.6 1. 面对十万条数据,你怎么渲染
我的回答: (当时想到的就是懒加载)我们使用懒加载去加载一步的数据,在页面发生滚动时,我们再根据滚动的距离去加载其他的数据。(回答不够全面)
补充:
// 分页渲染(使用定时器的方式实现)
const renderList = async(parent) => {
const list =await getList()
const page = 0
const limit = 200
const totalPage = Math.ceil(list.length / limit)
const render = (page) => {
if(page >= totalPage) return
setTimeout(() => {
for(let i =page * limit; i < page * limit +limit; i++) {
const item = list[i]
const div = document.createElement('div')
div.className = 'item'
div.innerHTML = `<img src="${item.src}" /> <span>${item.text}</span>`
parent.appendChild(div)
}
render(page + 1)
},0)
}
render(page)
}
// 文档碎片 + 分页渲染
const renderList = async(parent) => {
console.time('渲染时间');
const list =await getList()
const page = 0
const limit = 200
const totalPage = Math.ceil(list.length / limit)
const render = (page) => {
if(page >= totalPage) return
requestAnimationFrame(() => { //减少重排
//创建一个文档碎片
const fragment = document.createDocumentFragment()
for(let i =page * limit; i < page * limit +limit; i++) {
const item = list[i]
const div = document.createElement('div')
div.className = 'item'
div.innerHTML = `<img src="${item.src}" /> <span>${item.text}</span>`
fragment.appendChild(div) // 不会触发重排
}
parent.appendChild(fragment)
render(page + 1)
},0)
}
render(page)
console.timeEnd('渲染时间')
}
5. 关于ES6的问题
5.1 介绍一下Promise和 async/await。
我的回答:Promise 是异步编程的一种解决方案,是一个构造函数,自身有all、reject、resolve方法,原型上有then、catch等方法。Async/Await 代码看起来简洁一些,使得异步代码看起来像同步代码,async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
补充: Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。一旦状态改变,就不会再变,任何时候都可以得到这个结果。async/await的实现使用了Generator 函数。
5.2 解构怎么使用
我的回答:
- 数组的使用[...array]
- 对象的使用{key, key , ...other}
5.3 你有用过Decorator嘛
我的回答:(因为第一时间没有反应出Decorator是什么,所以我就说没用过)
补充: 装饰器(Decorator)用来增强 JavaScript 类(class)的功能。装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。
优点:
- 代码可读性变强了,装饰器命名相当于一个注释
- 在不改变原有代码情况下,对原来功能进行扩展
6. 其他问题
6.1 你怎么做项目的分支管理
我的回答: 因为我们写的项目比较小,所以一般只会使用一个分支,所以不会做分支的管理。(到这里已经感觉GG了。。。)
补充: 分支管理指的是从当前主分支(master)中创建分支(branch),然后每个人负责在自己的分支上进行开发、提交,最后所有功能都开发完成之后,再合并到主分支(master)上。
6.2 Myqsl 和mongodb的区别
我的回答:Mysql对数据库进行增删改查是使用sql语句,mongodb对数据库进行增删改查是使用js语句映射。
补充: 1、MySQL是关系型数据库,而mongodb是非关系型数据库;
2、MySQL中支持多种引擎,不同引擎有不同的存储方式,而mongodb以类JSON的文档的格式存储;
3、MySQL使用传统SQL语句进行查询,而mongodb有自己的查询方式(类似JavaScript的函数);
4、MySQL占用空间小,支持join,而mongodb占用空间大,不支持join。
总结
整个面试过程持续了半小时左右,我自己对我的评价是面的有些糟糕。因为总结下来,A公司的面试官问的问题还是比较简单的,可惜自己语言组织能力不够好,再加上第一次面试的紧张情绪,让我发挥的有些糟糕。革命尚未成功,同志还需努力呀。