22年前端西安面试纪实

813 阅读12分钟

Hello朋友们,俗话说金三银四,各位大神也开始了重新集赞提桶跑路之旅,那作为不是大神的我自己,那肯定也得自告奋勇提桶跑路了!(实际是因为公司凉了)。

由于最近西安疫情又开始爆发,辞职之后面试的基本都是电话面试,于是我总结出面试官问我的问题吧。 大多数问题还是比较常见的。在我的上一篇文章基本都整理在内了整理前端面试遇到的问题

一 自我介绍

这是老生常谈的问题了,基本上就是介绍自己什么时候毕业,工作经历,项目经历,使用的技术栈等等问题。

二 技术面试

html相关

对,你没有看错,现在面试还有这个html相关的。我听到题也是一脸懵逼(并不是说这个不好的意思)

  1. srchref的区别

    src是可以嵌入当前的资源到当前文档元素定义的位置,在浏览器下载,编译,执行这个文件之前页面的加载和处理会被暂停,

    href是网络资源的位置

  2. 请描述一下 cookiessessionStorage 和 localStorage 的区别?

    • cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)

    • cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递

    • sessionStoragelocalStorage不会自动把数据发给服务器,仅在本地保存

    • 存储大小:

      • cookie数据大小不能超过4k
      • sessionStoragelocalStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
    • 有期时间:

      • localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据
      • sessionStorage 数据在当前浏览器窗口关闭后自动删除
      • cookie 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
  3. cookie设置过期时间

    expires属性设置过期时间, 正数为过期时间,相反,取消过期时间则为0或者负数

css相关

  1. 重构和回流

    重构: 当元素的某些属性发生变化,这些属性又只影响元素的外观和风格,而不改变元素的布局、大小比如颜色、背景。此时触发的浏览器行为称作重构。

    回流: 当元素的布局、大小规模和显示方式发生改变时,触发的浏览器行为叫回流。而且,每个页面都会在第一次加载时触发回流。

    注意:回流必将引起重绘,而重绘不一定伴随回流。同时,回流对性能的影响要大于重构。

    出现的原因

    1. 添加、删除元素(回流+重绘)
    2. 隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)
    3. 移动元素,比如改变top,left(jqueryanimate方法就是,改变top,left不一定会影响回- 流),或者移动元素到另外1个父元素中。(重绘+回流)
    4. style的操作(对不同的属性操作,影响不一样。比如,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;)
    5. 还有一种是用户的操作,比如改变浏览器大小,改变浏览器的字体大小等(回流+重绘)

    如何减少回流,重绘

    1. 直接改变className,如果动态改变样式,则使用cssText(考虑没有优化的浏览器)

    2. 让要操作的元素进行”离线处理”,处理完后一起更新:

      • 使用DocumentFragment进行缓存操作,引发一次回流和重绘;
      • 使用display:none技术,只引发两次回流和重绘;
      • 使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
    3. 不要经常访问会引起浏览器flush队列的属性,如果你确实要访问,利用缓存

    4. 让元素脱离动画流,减少回流的Render树的规模(即让动画的元素脱离文档流,使用absolute定位等等)

JS相关

  1. js的闭包 闭包是指有权访问另⼀个函数作用域中的变量的函数。创建闭包的⽅式就是在⼀个函数内部创建另⼀个函数。

    闭包三个特性:

    1. 函数嵌套函数
    2. 函数内部可以引起函数外部的参数和变量
    3. 参数和变量不会被垃圾回收机制回收
  2. js原型链

    js的所有对象中都包含了一个 [__proto__] 内部属性,这个属性所对应的就是该对象的原型,js函数对象,除了原型 [__proto__] 之外,还预置了 prototype 属性。

    当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [__proto__]

    每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念

  3. js的事件循环机制

    Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

    先执行宏任务队列,然后执行微任务队列,然后开始下一轮事件循环,继续先执行宏任务队列,再执行微任务队列。

    宏任务:script/setTimeout/setInterval/setImmediate/ I/O/ UI Rendering

    微任务:process.nextTick()/Promise

    上诉的 setTimeoutsetInterva 等都是任务源,真正进入任务队列的是他们分发的任务。

    优先级 setTimeout = setInterval 一个队列 setTimeout > setImmediate process.nextTick > Promise

  4. js同步与异步区别

    (1)同步任务

    同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务

    (2)异步任务

    异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务

  5. callapply的区别

    这个参考我之前写的

  6. 打印问题

    为什么打印出这些,原因这个大概都明白吧


  // 第一题
    let s1 = new String("hello");
    let s2 = new String('hello')
    console.log(s1 == s2)  
    // false
 // 第二题
    console.log(Number(true))
    // 1
    console.log(Number(null))
    // 0
    console.log(Number(undefined))
    // NaN
    console.log(typeof NaN)
    // number
    console.log(0.1 + 0.2 == 0.3)
    // false
    console.log(true == 1)
    //true
    console.log(true === 1)
    // false

ES6相关

  1. 简述一下箭头函数和普通函数的区别

    • 写法上的不同
    • 普通函数存在变量提成的情况
    • 箭头函数不能作为构造函数使用
    • this指向不同
    • 箭头函数的arguments指向它的父级函数所在作用域的arguments
    • 箭头函数没有new.target
  2. SetMap的区别

    • SetMap 主要的应用场景在于 数据重组 和 数据储存。

    • Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构。

    • Set本身是一种构造函数,允许储存任何类型的唯一值,无论是原始值或者是对象引用,并且成员唯一,无序且不重复

    • Map 本质上是键值对的集合,可以遍历,

  3. promise你知道多少 (答案来自《ES6标准入门》)

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

    promise对象有两种特点:

    • 对象的状态不受外接的影响。

      promise对象有三种状态:

      • Pending(进行中): 初始状态,不是成功或失败状态。
      • fulfilled(已成功): 意味着操作成功完成。
      • rejected(已失败): 意味着操作失败。
    • 状态一旦改变就不会再变,任何时候都可以得到这个结果。

      Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

  4. promise打印顺序问题(优先级)

     const promise = new Promise((resolve, reject) => {
         console.log(1)
         setTimeout(() => {
             console.log("timeStart");
             resolve("success")
             console.log("timeEnd")
         }, 0);
         console.log(2)
     })
     promise.then((res) => {
         console.log(res)
     });
     console.log(4)
     
     // 1 2 4 timeStart  timeEnd success
    
  5. 新增的特性 (《ES6标准入门》目录)

    • 新增symbol类型 表示独一无二的值,用来定义独一无二的对象属性名;
    • const/let 都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const一般用于声明常量);
    • 变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);
    • 模板字符串(${data});
    • 扩展运算符(数组、对象);;
    • 箭头函数;
    • SetMap数据结构;
    • Proxy/Reflect;
    • Promise;
    • async函数;
    • Class;
    • Module语法(import/export)。

Vue相关

  1. Vue生命周期函数

    每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码:

    • beforeCreate: 进行数据和方法的初始化
    • created: 已经完成数据和方法的初始化
    • beforeMount: 开始渲染dom
    • mounted:可以渲染dom
    • beforeUpdate: data中的数据即将被更新;
    • updated: data中的数据更新完毕;
    • beforeDestroy: 实例即将销毁;
    • destroyed:实例已被销毁;
  2. Vue组件传值

    • 父子组件
      • 父传子: 父组件中通过v-bind绑定一个属性,子组件中通过props接收父组件中的绑定的属性
      • 子传父: 子组件通过广播的方式$emit将值传递给父组件,父组件中通过一个函数去接收子组件中传递过来的值
    • 爷孙组件: ref或者eventBus
    • 兄弟组件: 可以通过中间媒介父组件进行传递值的中转
    • 用于父组件向子孙组件传递数据: provide和inject
  3. 浅谈一下Vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上

    • state:存储数据

      Vuex使用单一状态树,即每个应用将仅仅包含一个store实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。

    • mutations:更新数据的方法

      定义的方法动态修改Vuexstore 中的状态或数据。

    • actions: 调用mutations方法,更新state数据

      actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action

    • getters: 对state中的数据进行预处理

    • modules: 项目特别复杂的时候,可以让每一个模块拥有自己的statemutationactiongetters,使得结构非常清晰,方便管理。

  4. 路由权限

    有两种方式:第一种后台返路由,第二种后台返权限

    1. 相同点

      • 都可以实现需求,
      • 前端都要维护一份路由与模块文件地址的映射
      • 后端返回的数据一般都要遍历做第二次处理
      • 关于页面内的按钮元素都要重新处理
      • 技术点都会涉及到路由守卫和鉴权
    2. 不同点

      • 默认路由列表:
        • 1 只维护homelogin等无权限需求路由,其他路由需要后续通过接口和路由apiaddRoutes动态添加;
        • 需要维护一个全量的路由列表,不需要额外添加路由,通过配置每个路由的access数组来做鉴权。
      • 路由跳转: 因为方法一返回的就是该用户权限下的路由,所以不需要再做权限鉴权;方法二需要。
      • 路由的自定义程度: 方法一可以通过修改数据库的路由数据来自定义前端的菜单结构,因此也需要做一个实现路由重组的递归函数,拓展性更好; 方法二针对的是菜单结构相对稳定的项目,一般不支持结构变动。
      • 返回报文: 一般来说,返回报文大小 方法一比方法二要大
  5. 按钮权限

    v-if 自定义指令

  6. v-for中key的作用

    • key的作用主要是为了更高效的对比虚拟DOM中每个节点是否是相同节点;
    • Vue在patch过程中判断两个节点是否是相同节点,key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,Vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能; - 从源码中可以知道,Vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永远认为这是两个相同的节点,只能去做更新操作,这造成了大量的Dom更新操作,明显是不可取的。
  7. vue-router

    有 3 种路由模式:hashhistoryabstract

    • hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
    • history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
    • abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

期望薪资以及后续自身问题等等

写在最后

我就是普通小菜鸡,很开心能够认识你们

以上就是我面试这一周以来总结的高频问题,希望能帮助到你们吧~

如果能帮助到你们,欢迎关注和点赞,文章中不足之处还望在评论区指正哈~

感恩💌