- 一年工作经验,杭州岗
人事面
- 能接受的加班情况
- 为什么想换工作
- 期望工资范围
- 未来有什么规划
技术面
- 自我介绍,在学校时的情况,最近做的什么项目,使用技术栈?
正题,Vue 相关(按分类归纳,简单汇总)
一、平时工作Vue.$set()是怎么用的?
- 当把一个普通的 JS 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
官方定义:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。Object.defineProperty 的缺点:无法检测对象属性的添加和删除- 可以使用
Vue.set(object, key, value)方法将响应属性添加到嵌套的对象上
二、 Vue中data为什么必须是一个函数呢?
- 首先data中存放的很多数据,指向同一个地址
- 页面中每个组件的值都是从data中获取的(同一个地址)
- 当data使用一个函数返回时,每个实例的data属性都是独立的,不会相互影响。
- 总结就是避免多个组件共用同一个作用域data。
三、 在Vue生命周期运用、理解及操作?
beforeCreate时,只是始化一个空的Vue实例对象,并未初始化data数据和methods方法。created时,data和methods已经初始化完毕,可以在这里进行数据操作或调用methods方法beforeMount时,模板在内容中编译好了,但未挂载到页面上(此时页面插值展示{{msg}})mounted时,Vue实例已经初始化完毕,浏览器页面正常展示。data数据发生改变时- beforeUpdate:此时data中数据已经更新,但是页面上还是旧数据。
- updated:此时页面上数据已经与data数据保持一致,都是最新的。
beforeDestroy时,未进入真正销毁过程,实例身上挂载的data和methods都可以继续使用destroyed时,组件已被完全销毁,组件数据及方法指令都不可在用。
四、 当Vue中组件嵌套时,它们生命周期关系是怎样的 ?
- 组件正常加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted - 子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated - 父组件更新过程
父beforeUpdate->父updated - 销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed - 可知子组件挂载完成后,父组件还未挂载。所以组件数据回显的时候,在父组件mounted中获取api的数据,子组件的mounted是拿不到的
五、 在Vue中组件常用的通信方式 ?
- 父向子组件传值
- 引用子组件时,在子组件身上bind绑定指定数据名
- 子组件实例属性props里进行声明获取(只读不可修改)
- 传值数据的类型标注都可
- 子组件向父组件传值
- 自定义事件函数绑定在子组件身上
this.$emit('childFun',data);- data为子组件想像父组件传递的值
<child @childFun="parFun"></child>- 父组件中需要定义函数parFun(data)获取
- 自定义事件函数绑定在子组件身上
- 兄弟组件间传值
- 利用eventBus事件总线实现
- 当然,还有更合适的方案是使用vuex来做数据管理。
六、v-if与v-show的区别与使用场景
- v-if为条件语句,不满足则不会存在于DOM中,通过移除和创建达到显示效果
- 适合不怎么改变当前元素的状态
- v-show会持续存在于dom中,不满足条件则会设置
display:none- 适合频繁操作元素的显示与隐藏
七、Vue中$nextTick()干嘛使得,应用场景是什么?
- 定义:在下次DOM更新循环结束后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。
- 理解:nextTick()是将回调函数延迟在下一次dom更新数据后调用,即当数据更新了在DOM中渲染后自动执行该函数。
- 背景:created时,data和methods初始完毕,但DOM未渲染。
- mounted时,页面的DOM节点才被初始化后挂载。
- 因此在created操作DOM,都需要将js代码放进Vue.nextTick()的回调函数中。
八、Vue中有进行过什么性能方面的优化吗?
- 开放性回答:
- 比如组件for循环时,设置唯一key值,便于vue更快定位该数据
- 使用keep-alive进行组件缓存
- 三方库按需引入,避免无用加载等
九、Vue中对于data中定义的数据,但不需要监听怎么处理,比如Object.freezze()
- 定义:Object.freeze() 方法可以冻结一个对象。这个对象不能被修改添加和删除
- 在确定不会操作此对象时,使用Object.freeze()可以让性能大幅提升
十、V-html怎么用的?
- 后台传来的json数据中包含html标签,避免其被当作字符来展示
- 需要使用v-html进行标签解析
感受:Vue方面,更注重生产上的实际使用和场景解决,不过路由和Vuex没问挺意外。
HTTP相关
一、常见的请求方法
- GET方法,从指定资源中请求数据。(GET请求有长度限制,可缓存不可修改)
- POST方法,数据传输用于创建或更新资源(POST无长度限制,不可缓存)
- PUT方法,它和post方法很相似,不过请求相同时PUT会覆盖前一个,post不会
- DELETE方法,用来删除指定资源
二、通信时常用的状态码
- 200 OK :表示客户端请求在服务器端正常处理。[直接用的浏览器缓存]
- 3XX 重定向:表明浏览器需要执行某些特殊的处理以正确处理请求。
- 301 Moved Permanently(永久性重定向)
- 302 Found(临时重定向)
- 304 Not Modified 客户的缓存资源是最新的, 要客户端使用缓存。
- 4XX 客户端错误
- 400 Bad Request:该状态码表示请求报文中存在语法错误
- 403:资源不可用,服务器理解客户端的请求但拒绝处理,一般是权限问题
- 404 Not Found(未找到)表明服务器上无法找到请求的资源
- 5XX 服务器错误
- 500 Internal Server Error(内部服务器错误)
- 503 Service Unavailable(暂停服务)
三、HTTP中在头部中怎么设置缓存多久
- 缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
- Expires=max-age + 请求时间,需要和Last-modified结合使用
- 通常是Cache-Control,相对时间
- 例如设置 Cache-Control:max-age=31536000
- 有效期为(31536000 / 24 / 60 * 60)天
四、强缓存与协商缓存
- 强缓存需要服务端设置expires和cache-control。
- 强缓存是性能优化中缓存方面最有效的方法,可极大提升性能
- 强缓存不会向服务端发起请求,因此服务端的压力也大大减小
- 强缓存命中显示:200 ok
(from disk cache 磁盘缓存)或(from memory cache 内存缓存)
- 协商缓存
- 协商缓存需要向服务器发起请求,来判定是否过期,类似一个缓存校验
- 强缓存没有命中时,则会发起协商缓存。
- 协商缓存并不会减少发起的请求次数,但是会减少返回的正文大小
- 协商缓存命中显示:304 Not Modified
五、目前主流的HTTP版本?
- 主流的是HTTP 1.0/ HTTP 1.1 / HTTP 2.0(未广泛运用)
- 内容过多,不赘述
六、 从浏览器地址栏输入 url 到显示页面的步骤(QAQ,烂大街的问题)
- 要解析域名转换成对应的公网 IP;
- 域名解析为 Ip 地址,依靠 DNS 服务,DNS 服务将该域名解析为 IP 地址,如果这一级的 DNS 服务器上找不到,则会进步向上一级的 DNS 发去查询请求。直到根域名服务器为止。如果中间找到了对应的公网 Ip,则再一级一级返回,如果找不到则返回错误信息,域名解析失败。
- 根据公网 IP 通过庞大的互联网路由到对应的服务器上;
- 获取公网 IP 地址,就可以通过路由器找到对应的服务器所在。
- 建立可靠的 TCP 数据连接;
- 这里使用的 TCP 协议在使用前需要首先在客户端和服务端之间,建立起一条数据链路。分为三步,也叫作 TCP 三次握手。
- 服务器对该 URL 中的请求进行处理分发,返回一个 html;
- 客户端根据 URL 携带的不同信息,对其进行分发和数据处理。通过对应的 URL 格式 dispatch 到不同的后台处理 Java 类中,进行数据处理后,再返回对应的 HTML 文件给客户端
- 浏览器或者客户端对该 HTML 进行渲染;
- 网页多是动态 HTML。里面包含了大量的 JS 代码。目前的浏览器为了尽快加载页面,普遍采用了一边解析一边加载的原则
七、浏览器的解析再详细描述一下
- 表述了下获取的HTML和CSS怎么边解析边加载的规则及过程。
感受:HTTP很基础很简单,过程穿插了TCP握手时SYN数据及ACK确认包及对称与非对称加密原理(是否共享秘钥)
JavaScript相关
一、JavaScript大致的两种数据类型,在内存存储上有什么区别?
- 指的就是基本数据类型与引用数据类型
- 基本数据类型
- 保存在栈内存中,它占据空间小、大小固定,便于频繁使用。
- 引用数据类型
- 引用数据类型是保存在堆内存中
- 栈内存中保存一个对堆内存中实际对象的引用(其实就是地址)
二、localStorage相关及tab通信
- 背景:localStorage相对于sessionStorage是持久化存储(关闭对话不会删除)
- localStorage的局限
- 浏览器隐私模式下不可见,即不可被读取
- 值的类型为string,则存取的时候需要借助
JSON.stringify()或JSON.parse() - 由于是H5新特性,所以低版本浏览器不支持(如IE8)
- 如何通信
- 背景:storage事件,是localStorage特有的事件,只要localStorage中的内容发生改变就会触发该事件(非己页面才能触发)
- localStorage是浏览器存储数据的容器,为多页面共享
- 利用storage事件,实时监听localStorage中的变化,无需再使用定时器
三、JavaScript中两个数组如何求交集?
- filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
var arr1 = [6,7,8];
var arr2 = [7,8,9];
var arr3 = arr1.filter(function(num) {
return arr2.indexOf(num) !== -1;
}); //[7, 8]
- includes的数组方法,用于返回一个数组是否包含指定元素
let intersection = arr1.filter(v => arr2.includes(v))
四、JavaScript中快速创建数组?
- while循环填充数组
- 使用
‘[ ]’或者new Array() - 使用
fill( )结合map( )- fill() 方法用于将一个固定值替换数组的元素
- map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值
- map() 不会对空数组进行检测。
let newArr = new Array(10).fill('').map( (item, index) => index+1);
五、JavaScript中怎么求数组和?
- 普通for循环实现
- 利用
evel( )解析字符串eval( )函数可计算某个字符串arr.join('+')数组拼接,然后eval()解析计算(性能低)
- 利用reduce( )
- reduce( ) 方法接收一个函数作为累加器
- reduce( ) 不会对空数组执行回调函数。
var sum = arr.reduce(function (prev, cur) { return prev + cur},0);
六、怎么判定一个对象是不是数组?
- 利用Array.isArray()方法。
- 利用构造函数,obj instanceof Array
- 从原型入手,Array.prototype.isPrototypeOf(obj);
- isPrototypeOf()方法,判定Array是不是在obj的原型链中
淘特供前端笔试
- 限时一个小时,四道题
一、 找出数组中第K大和第M大的数字之和 ( 15min )
/**
*
* 示例:
* let arr=[1,2,4,4,3,5],k=2,m=4
* findTopSum(arr,k,m)
* 第二大为4,第四大为2
* 所以得 4*2+2*1=10
*/
var arr = [1, 2, 4, 4, 3, 5], k = 2, m = 4;
function findTopSum(arr, k, m) {
var hash = {};
//记录出现的频率
for (const item of arr) {
if (hash[item])
hash[item] = hash[item] + 1
else
hash[item] = hash[item] = 1
}
//排序后去重
arr = [...new Set(arr.sort((x, y) => { return x - y }))];
//第K、M 大的值
var paramK = arr[arr.length - k];
var paramM = arr[arr.length - m];
//第K大的值*频率 【下方判定当时笔试忘了写,所以当K、M超出数组范围会有bug】
var sumK = paramK * hash[paramK] ? paramK * hash[paramK] : 0;
var sumM = paramM * hash[paramM] ? paramM * hash[paramM] : 0;
return sumK + sumM;
}
findTopSum(arr, k, m)
二、 清除对象中值为undefined或null的key并且返回新对象,不修改原对象。( 15min )
- 如果不是使用reduce完成的,那么使用reduce如何完成?
- 如果该规则允许自定义如何做?
- 如果不允许有任何副作用,即不能修改reduce回调函数内的参数如何做?
- 如果你可同时完成以上要求,那么你可以只使用一个函数
/** 题目 */ function clean(target){} var target={a:undefined,b:null,c:false,d:'',e:0}; function cleanCust(/*自定义规则*/){ } function cleanUseReduce(target){/*使用reduce完成*/ } - 普通方法实现
/** 解答 clean(target); */
var target = { a: undefined, b: null, c: false, d: '', e: 0 };
var rule=['string','boolean'];
function clean(target) {
let obj = {};
for (const index in target) {
var rule = typeof target[index] == 'undefined' || typeof target[index] == 'object';
if (!rule)
obj[index] = target[index]
}
return obj;
}
- 带自定义规则方法实现
/** 解答 cleanCust(target); */
function cleanCust(target,rule) {
let obj = {};
//校验规则
for (const i in target) {
var filter= rule.some(function(item,index,array){
return typeof target[i]==item;
})
if (!filter)
obj[i] = target[i]
}
console.log(obj);
return obj;
}
cleanCust(target,rule);
- reduce方法实现
- 这个笔试写不出来,后来想了好久,写的非常丑陋,不知道怎么实现才好
/**
* @params
* target 存放原始对象
* rules 存放验证规则,可自定义,不传默认过滤undefined、null
* transArr 对象转为数组 [{x:'23'}, {a: undefined}..... {e: 0} ]
* newobj 存放符合条件的对象
*/
var target = {x:'23', a: undefined, b: null, c: false, d: '', e: 0 };
function cleanUseReduce(target,rules){
rules=rules?rules:['undefined','object']
var transArr = [];
var newobj={};
for (let i in target) {
let obj = {};
obj[i] = target[i];
transArr.push(obj)
}
transArr.reduce((currentValue, currentIndex, arr)=>{
for (var index in currentIndex) {
var falg=rules.includes(typeof currentIndex[index])
}
if(!falg)
newobj[index]= target[index]
},[])
return newobj;
}
cleanUseReduce(target)
三、 防抖节流 ( 15min )
- 连续事件触发,每隔一段时间,只执行一次
- 连续事件触发,事件触发停止后,执行一次。
btn.addEventListener('click',throttle(submit,3000))
function submit(e){
console.log('---');
}
function throttle(cbk,delay){
// 设置初始值
var begin=0;
return(...args)=>{
var cur=new Date().getTime();
//每次点击的时间和上次的时间大于定义间隔则执行
if(cur-begin>delay){
cbk.apply(this,args)
//更新初始值
begin=cur;
}
}
}
btn = document.getElementById('btnId');
btn.addEventListener('click',debounce(submit,2000))
function submit(e){
console.log('---');
}
function debounce(cbk,wait){
let timer=null;
return(...args)=>{
if(timer) clearTimeout(timer)
//避免函数自身污染外部arguments
timer=setTimeout(() => {
timer=null ;
cbk.apply(this,args)
}, wait);
}
}
四、 数据筛选 ( 15min )
- 一群学生,考语文数学两门,筛选出总分第一的同学
- 数据类型自定义,总分相同且第一,则筛选出并列
/**
* @params
* math,chinese 数学语文分数
* Grades 最高分
* pickName 最高分学生
*/
var math = { xiaoMing: 66, xiaoHua: 66, xiaoHong: 95, xiaoLan: 99, xiaoBai: 68 };
var chinese = { xiaoMing: 66, xiaoHua: 66, xiaoHong: 88, xiaoLan: 87, xiaoBai: 95 };
function pick(M, C) {
var Grades = 0;
var pickName = '';
for (const key in M) {
M[key] = M[key] + C[key];
if (M[key] > Grades)
//后边总分比前大,则重设最高总分
[Grades, pickName] = [M[key], key]
else if (M[key] == Grades)
//与最高分相同则一同记录
pickName += key;
}
console.log(`最高分是${Grades}--${pickName}`);
}
pick(math, chinese)
拓展
一、看你简历上自己也写过PHP,那了解过什么API设计规范吗?比如RESTful API
- 得,给自己挖了一个大坑,就写过增删改查,设计规范这玩意我哪知道
- 含含糊糊随便说了下,最后说自己对这方面不太擅长。
- 阮一峰老师RESTful API 设计规范
二、前端项目一般是怎么部署的?
- 这个我没有在生产上部署过,根据自己的demo回答的很烂
- webpack自动化项目
- 通过npm run build 工程项目生成dist文件
- 打包生成的dist文件移植到Tomcat服务器下
三、工作中Git的使用
- 常用的pull和push的没说
- 表述常用的checkout,以及区分需求节点如何打tag
- 涉及代码merge时,dev及 release开发分支区分
- 冲突如何解决,以及如何选用多个commit的其中几个
- 我回答平时使用cherry-pick解决
- 公司的代码如何保管的(内部搭建私有服务器)
四、拓展内容
- docker linux nginx mock的了解及使用
- 工作中联调是怎么样的,公司API规则
- 平时通过哪些渠道获取技术信息,有哪些学习习惯
想了解的东西?
- 公司目前的项目业务发展方向及技术栈,技术成员情况怎样,工作所在地。
总结
- 感觉是以解决生产实用性为主,原生的JS语法及相关开发技巧问的会比较少。