一年前端都问啥?

707 阅读2分钟
  • 一年工作经验,杭州岗

人事面

  • 能接受的加班情况
  • 为什么想换工作
  • 期望工资范围
  • 未来有什么规划

技术面

  • 自我介绍,在学校时的情况,最近做的什么项目,使用技术栈?

正题,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,烂大街的问题)
  1. 要解析域名转换成对应的公网 IP;
    • 域名解析为 Ip 地址,依靠 DNS 服务,DNS 服务将该域名解析为 IP 地址,如果这一级的 DNS 服务器上找不到,则会进步向上一级的 DNS 发去查询请求。直到根域名服务器为止。如果中间找到了对应的公网 Ip,则再一级一级返回,如果找不到则返回错误信息,域名解析失败。
  2. 根据公网 IP 通过庞大的互联网路由到对应的服务器上;
    • 获取公网 IP 地址,就可以通过路由器找到对应的服务器所在。
  3. 建立可靠的 TCP 数据连接;
    • 这里使用的 TCP 协议在使用前需要首先在客户端和服务端之间,建立起一条数据链路。分为三步,也叫作 TCP 三次握手。
  4. 服务器对该 URL 中的请求进行处理分发,返回一个 html;
    • 客户端根据 URL 携带的不同信息,对其进行分发和数据处理。通过对应的 URL 格式 dispatch 到不同的后台处理 Java 类中,进行数据处理后,再返回对应的 HTML 文件给客户端
  5. 浏览器或者客户端对该 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语法及相关开发技巧问的会比较少。