企业面试四

218 阅读13分钟

太平人寿-线下二面

1、本地存储以及区别

答:本地存储包括:cookie localStorage sessionStorage indexDB,区别如下:

Snipaste_2023-07-09_14-16-39.png

2、css中隐藏元素的方法

  • opacity:0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定一些事件,如 click 事件,那么点击该区域,也能触发点击事件的。
  • visibility:hidden,该元素隐藏起来了,但不会改变页面布局,还占据位置,该元素绑定的事件不会生效。
  • display:none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删除掉,不占位置。

3、js事件循环机制

js是单线程执行的,页面加载时,会自上而下执行主线程上的同步任务,当主线程代码执行完毕时,才开始执行在任务队列中的异步任务。具体如下:

  • 所有同步任务都在主线程上执行,形成一个执行栈。
  • 主线程之外,还存在一个"任务队列(eventloop队列或者消息队列)"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  • 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。哪些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  • 主线程不断重复上面的第三步。

4、浅拷贝和深拷贝;实现深拷贝的方法

  • 浅拷贝:拷贝对象的一层属性,如果对象里面还有对象,拷贝的是地址, 两者之间修改会有影响,适用于对象里面属性的值是简单数据类型的。

  • 深拷贝:拷贝对象的多层属性,如果对象里面还有对象,会继续拷贝,使用递归去实现。

// 浅拷贝实现方法

    拷贝对象: 
        1、Object.assign()   
        2、展开运算符{...obj}
    拷贝数组:
        1、Array.prototype.concat()    
        2、展开运算符 [...arr]
        
// 深拷贝实现方法
    1、JSON序列化实现
    const obj = {
      name: '佩奇',
      family: {
        father: '猪爸爸'
      },
      hobby: ['跳绳','唱歌']
    }
    const newObj = JSON.parse(JSON.stringify(obj))
    newObj.family.father = '猪老二'
    console.log(newObj)
    
    2、lodash实现
    const person = {
        name: "西精品",
        age: 10000,
        hobby: ["爱折腾", "目无法纪", "狂妄自大"],
        family: {
            daughter: "席明哲",
            wife: "评论员",
        },
    };
    const baozi = _.cloneDeep(person)
    baozi.hobby.push('123')
    console.log(baozi)
    
    3、递归实现 (这是重点)
    function cloneDeep(target, source) {
        for (let k in source) {
          if (source[k].constructor === Object) {
            target[k] = {};
            cloneDeep(target[k], source[k]);
          } else if (source[k].constructor === Array) {
            target[k] = [];
            cloneDeep(target[k], source[k]);
          } else {
            target[k] = source[k];
          }
        }
    }

    const obj = {};
    cloneDeep(obj, person);
    obj.hobby.push('abroad')
    console.log(obj, person);
    

5、网页请求到渲染经历了什么

  • 输入url地址后,首先进行DNS(Domain Name System域名系统)解析(域名服务器负责解析),将相应的域名解析为IP地址。
  • 根据IP地址去寻找相应的服务器。
  • 与服务器进行TCP的三次握手,建立连接。
  • 客户端发送请求,找到相应的资源库,拿到数据后,进行相应的渲染。

6、Vue2中v-if和v-for的优先级?

v-for优先级高于v-if;如果同时出现v-for和v-if,无论判断条件是否成立,都会执行一遍v-for循环,这样浪费性能,所以要尽可能的避免两者一起使用。

7、computed和watch区别

  • 计算属性computed

    • 支持缓存,只有依赖数据发生改变,才会重新进行计算
    • 不支持异步,当computed内有异步操作时无效,无法监听数据的变化。(异步操作包括:axios,回到函数,promise,async/await,事件监听)
    • 如果computed需要对数据修改,需要写get和set两个方法,当数据变化时,调用set方法。
    • computed擅长处理的场景:一个数据受多个数据影响,例如购物车计算总价。
  • 侦听器watch

    • 不支持缓存,数据变,直接会触发相应的操作;
    • watch支持异步;监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
    • immediate:组件加载立即触发回调函数执行
    • deep:true的意思就是深入监听,任何修改obj里面任何一个属性都会触发这个监听器里的handler方法来处理逻辑
    • watch擅长处理的场景:一个数据影响多个数据,例如搜索框当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。例如ajax请求,复杂的业务逻辑处理等。

8、Vue的核心:数据驱动用什么实现;Vue中数组的方法可以实现响应式是为什么?(源码中加入数组的方法)

  • 数据驱动绑定的实现

    • vue2的双向数据绑定指的是v-model
    • v-model本质上是个语法糖,真正的实现靠:
      • v-bind绑定响应式数据
      • 触发input事件并传递数据(核心和重点)

    等价于

    <input v-bind:value="searchText" v-on:input="searchText = $event.target.value">

    其中:

    • $event指当前触发的事件对象
    • $event.target 指代当前触发的事件对象的dom
    • $event.target.value 当前dom的value值
    • 在@input方法中,value => searchText
    • 在value中,searchText => value

9、原型链:一个对象的实例原型指向的是哪

null

10、节流、防抖(鼠标滚动事件用什么比较好)

  • 防抖:就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

    • 应用场景:1、用户在搜索框输入文字的时候;2、window触发resize事件的时候。
  • 节流:指连续触发事件但是在n秒中只执行一次函数。

    • 应用场景:1、鼠标不断点击触发;2、鼠标滑过;3、监听滚动事件,比如是否滑到底部自动加载更多。
  • 代码实现

    // 防抖
      const div = document.querySelector("div");
      let i = 1;
      function mouseMove() {
        div.innerHTML = i++;
      }
      function debounce(fn, time) {
        let timer;
        return function () {
          if (timer) clearTimeout(timer);
          timer = setTimeout(function () {
            fn();
          }, time);
        };
      }
      div.addEventListener("mousemove", debounce(mouseMove, 500));
      
   // 节流
      const div = document.querySelector("div");
      let i = 1;
      function mouseMove() {
        div.innerHTML = i++;
      }
      function throttle(fn, time) {
        let timer;
        return function () {
          if (!timer) {
            timer = setTimeout(function () {
              fn();
              timer = null;
            }, time);
          }
        };
      }
      div.addEventListener("mousemove", throttle(mouseMove, 500));

11、Promise和Async Await区别?

  • promise:

    • 含义:它是一个对象,也就是说与其他JavaScript对象的用法相似;它使得异步操作具备同步操作的效果,使得程序具备正常的同步运行的流程,回调函数不必再一层层嵌套。
    • 特点
      • 三种状态:pending(执行中)、resolve(成功)、rejected(失败)
      • 可以进行链式调用
      • 能错误捕获:Promise。prototype.catch用于指定Promise状态变为rejected时的回调函数,可以认为是.then的简写形式,返回值和.then一样。
  • async await

    • 含义
      • async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
      • await 只能出现在 async 函数中。
      • async 函数返回的是一个 Promise 对象,后面可以用then方法。
    • 错误捕获:若是reject状态,可以用try-catch捕获
    let p = new Promise((resolve,reject) =&gt; {
        setTimeout(() =&gt; {
            reject('error');
        },1000);
    });

    async function demo(params) {
        try {
            let result = await p;
        }catch(e) {
            console.log(e);
        }
    }
    demo();

  • 两者区别:
    • promise是ES6,async/await是ES7
    • async/await相对于promise来讲,写法更加优雅
    • reject状态:
      • promise错误可以通过catch来捕捉,建议尾部捕获错误
      • async/await既可以用.then又可以用try-catch捕捉

12、axios二次封装做了什么?配置基地址直接在原型上加或创建实例加有什么区别?应用上哪个更合理?

  • 为什么要封装:
    • 随着项目规模增大,如果每发起一次HTTP请求,就要把这些比如设置超时时间、设置请求头、根据项目环境判断使用哪个请求地址、错误处理等等操作,都需要写一遍。这会让代码变得冗余不堪,难以维护。为了提高我们的代码质量,我们应该在项目中二次封装一下 axios 再使用。
  • 如何封装:
    • 新建一个request.js文件用于调用接口的时候使用

    • 在文件中通过axios的create方法新建一个实例,里面配置请求地址和相应的时间,最后将这个配置的实例用instance进行接收

    • 通过instance配置请求拦截器和相应拦截器

    • 最后将instance默认导出

13、跨域

  • 同源策略:同源策略是浏览器的一种安全策略, 所谓同源是指域名、协议、端口完全相同,不同源则跨域。
  • 解决跨域的方案: - CORS跨域资源共享 - nginx代理跨域

14、常用git命令

    git init        // 初始化
    git clone 地址  // 克隆远程仓库
    git clone -b 分支名 地址  // 克隆分支代码到本地
    git status     //查看状态
    git add 文件名 // 将某个文件存入暂存区
    git checkout -- file   // 撤销工作区的修改  例如:git checkout -- readMe.txt 将本次readMe.txt在工作区的修改撤销掉
    git add b  c  //将b和c存入暂存区
    git add .     //将所有文件提交到暂存区
    git commit -m '提交的备注信息'  // 提交到仓库
    git remote add origin git@gitee.com:lzwan/smart-mall-2.git   // 将远程仓库的名字改为 origin
    git push -u origin "master"  // 将本地库的所有内容推送到远程库里面

15、webpack熟悉吗?

含义: webpack是一种静态编译工具(预编译),它能把各种资源,例如JS(含JSX)、样式(含less/sass)、图片等都作为模块来使用和处理。 优势:

  • 支持异步模块加载
  • 扩展性强,插件机制完善
  • 开发便捷

16、Vue项目中怎么解决跨域问题?前端怎么配置?后端怎么配置?

  • 开发阶段:通过vue-cli解决跨域问题,具体是在config.js里面的webpack的属性proxy里面配置跨域的地址,前端即可通过改地址获取接口里面的数据
  • 上线阶段:通过Nginx服务器实现,这个主要是后端操作

17、Vue中data为什么是个函数?

组件是用来复用的,组件中的data写成一个函数,数据以函数返回值形式定义,函数有独立的作用域,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。

18、Vue中父子组件生命周期渲染顺序?更新父子组件顺序?在哪个发送请求?mounted发送请求有什么区别

  • 父子组件生命周期渲染顺序

    • 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
  • 更新父子组件顺序

    • 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  • 在哪里发送请求:

    • created
  • mounted发送请求有什么区别

    • 在created时期请求接口拿到页面数据,DOM 还没有渲染,最后数据和DOM会同步渲染
    • 在mounted时期请求接口拿到页面数据,DOM 已经渲染到页面中,拿到数据后再染数据。

19、组件通讯方式;Vuex主要内容

  • 组件的通讯方式
    1、父传子
        通过props传递
        父组件: <child :list = 'list' />
        子组件: props['list'],接收数据,接受之后使用和data中定义数据使用方式一样

    2、子传父
            在父组件中给子组件绑定一个自定义的事件,子组件通过$emit()触发该事件并传值。
        父组件: <child @receive = 'getData' />
                getData(value){value就是接收的值}
        子组件: this.$emit('receive',value)

    3、兄弟组件传值 provide  inject7 
            通过中央通信 let bus = new Vue()
        A组件:methods :{t
                sendData(){
                    bus.$emit('getData',value)
                  } 发送
        B组件:created(){
            bus.$on(‘getData’,(value)=>{value就是接收的数据})
        } 进行数据接收
  • vuex的主要内容
    • Vuex 是专为Vue设计的状态管理工具,采用集中式储存管理 Vue 中所有组件的状态。
    • 属性
      • state属性:基本数据
      • getters属性:从 state 中派生出的数据,相当于state的计算属性
      • mutation属性:更新 store 中数据的唯一途径,其接收一个以 state 为第一参数的回调函数
      • action 属性:提交 mutation 以更改 state,其中可以包含异步操作,数据请求
      • module 属性:用于将 store分割成不同的模块。

20、keep-alive;怎么捕获状态(actived钩子函数)

  • keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染
  • 一般结合路由和动态组件一起使用,用于缓存组件
  • 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated
  • 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;

21、插槽;插槽和组件的区别

  • 插槽:插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
  • 组件:可复用的vue实例,把一些公共的模块抽离出来,写成单独的工具组件或页面,在需要的时候直接引入。

22、Vue中常用的修饰符

事件修饰符:
    .prevent  阻止事件默认行为
    .stop     阻止事件冒泡
    .capture  设置事件捕获机制  多用于遇到事件冒泡时控制触发顺序。
    .self     只有点击元素自身才能触发事件
    .once     事件只触发一次
 按键修饰符:
    .tab
    .enter
    .esc
    .space
    .delete(捕获"删除"和"空格"键)
    .up
    .down
    .left
    .right

23、v-model原理

  • v-model本质上是个语法糖,真正的实现靠:
    • v-bind绑定响应式数据
    • 触发input事件并传递数据(核心和重点)
    • 等价于
    • <input v-bind:value="searchText" v-on:input="searchText = $event.target.value">
      • $event指当前触发的事件对象
      • $event.target 指代当前触发的事件对象的dom
      • $event.target.value 当前dom的value值
      • 在@input方法中,value => searchText
      • 在value中,searchText => value
    • 主要提供了两个功能,view层输入值影响data的属性值,属性值发生改变会更新层的数值变化。
  • 其底层原理就是(双向数据绑定原理):
    • 一方面model层通过Object.defineProperty()来劫持每个属性,一旦监听到变化通知相关的页面元素进行更新。
    • 另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值。

24、动态路由什么时候会用到

动态菜单

25、导航守卫有几种?常用哪个?防越权具体如何实现?导航守卫设置

  • 导航守卫种类:

    • 全局路由守卫
    • 组件路由守卫
    • 路由独享守卫
  • 常用全局路由守卫

  • 防越权具体实现

    • 前后端同时对用户输入信息进行校验,双重验证机制
    • 执行关键操作前必须验证用户身份,验证用户是否具备操作数据的权限
    • 特别敏感操作可以让用户再次输入密码或其他的验证信息。
    • 可以从用户的加密认证 cookie 中获取当前用户 id,防止攻击者对其修改。 或在 session、cookie 中加入不可预测、不可猜解的 user 信息。
    • 直接对象引用的加密资源ID,防止攻击者枚举ID,敏感数据特殊化处理
    • 永远不要相信来自用户的输入,对于可控参数进行严格的检查与过滤
  • 导航守卫如何设置

    • 使用router.beforeEach注册一个全局前置守卫,接收3个参数:to/from/next