2023前端面试题总结(一)

146 阅读8分钟

一、vue的双向绑定原理是什么?里面的关键点在哪里

答:采用”数据劫持“结合“发布者-订阅者”模式的方式,通过 ”Object.defineProperty()“方式来劫持各个属性的setter, getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

二、实现水平居中的方式

答: 1、absolute + 负margin

2、absolute + margin auto

position: absolute;; top: 0; left: 0; right: 0; bottom: 0; margin: auto;

3、absolute + calc

position: absolute;; top: calc(50% - 50px); left: calc(50% - 50px);

4、absolute + transform

position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);

5、flex

display: flex; justify-content: center; align-items: center;

6、lineheight

display: inline-block; vertical-align: middle; line-height: initial; text-align: left; /* 修正文字 */

三、常用的伪元素有哪一些?

  • 伪元素主要是用来创建一些不存在原有dom结构树种的元素

  • 伪类表示已存在的某个元素处于某种状态,但是通过dom树又无法表示这种状态,就可以通过伪类来为其添加样式

  • 伪元素的操作对象是新生成的dom元素,而不是原来dom结构里就存在的;而伪类恰好相反,伪类的操作对象是原来的dom结构里就存在的元素。
    伪元素与伪类的根本区别在于:操作的对象元素是否存在于原来的dom结构里

  • 常见的伪类:
    :link 应用于未被访问过的链接
    :visited 应用于被访问过的链接
    :hover 应用于鼠标悬停到的元素
    :first-child 选择某元素第一个子元素
    :last-child 选择最后一个。。。。。
    :disabled 表单元素禁用
    :enabled 匹配没有被禁用的元素

  • 常见的伪元素:
    ::first-letter 选择元素文本的第一个字
    ::first-line 选择元素文本的第一行
    ::before 在元素内容的最前面添加新内容。
    ::after 在元素内容的最后面添加新内容。

四、移动端如何适配不同屏幕尺寸

1、利用媒体查询

2、利用js 控制rem

3、利用vw

五、本地存储有哪一些?他们三者有什么区别

答:本地存储一般有三种:cookie、 sessionStorage、localStorage

1、cookie 可以设置过期时间,cookie 在浏览器和服务器间来回传递。而sessionStorage 和localStorage 不会自动把数据发给服务器,仅在本地保存

2、储存大小限制也不同

cookie 数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很下的数据。如会话标识。而sessionStorage 和 localStorage 虽然也有储存大小的限制,但比cookie大得多,可以达到5M 或者更大

3、数据有效期不同,sessionStorage: 仅在当前浏览器窗口关闭前有效,自然也就不可能持久保存;

localStorage: 始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie在设置的cookie过期时间之前一直有效,即使窗口或者浏览器关闭。

六:cookie有哪些属性?

1、Name/Value

2、Expires属性

3、Path属性

4、Domain属性

5、Secure属性:指定是否使用[HTTPS]安全协议发送Cookie。

6、HTTPOnly 属性

七、js数据类型有哪些?,如何判断js数据类型?

js数据类型:

  • Number
  • String
  • Boolean
  • Null
  • Undefined
  • Object
  • Symbol 判断js 数据类型:

1、typeof typeof 100

2、instanceof instanceof运算符需要指定一个构造函数,或者说指定一个特定的类型,它用来判断这个构造函数的原型是否在给定对象的原型链上。 false instanceof Boolean, //false

3、constructor constructor是prototype对象上的属性,指向构造函数。根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。 var num = 123; num.constructor==Number, true

4、Object.prototype.toString.call()

可以通过toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为thisArg。

var toString = Object.prototype.toString;

toString.call(123); //"[object Number]"

八、说一下ES6的新特性有哪些?

1、es6新增了promise(标题) 需要说什么是promise

2、es6新增了模块化 需要说什么是模块化

3、新增了class关键字 需要解释

4、新增了箭头函数 再说箭头函数与普通函数的区别

5、新增了解构赋值 需要解释什么是解构赋值

6、新增了let const关键字 需要说let const var的区别

7、新增了简单数据类型symbol

九、let const var 三者有什么区别?

  • let 是代码块有效 var是全局有效

  • let 是不能重复声明的 var是可以多次声明

  • let不存在变量的提升 var存在变量的提升

  • const存储简单数据类型存储的是常量

十、数据去重有哪些办法?

1、利用Set结构不能接收重复数据的特点

var newArr = [...new Set(arr)]; //利用了Set结构不能接收重复数据的特点

2、利用 filter() + indexOf 去重

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。item是当前元素的值,index是当前元素的索引值。indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。利用indexOf() 查询到数组的下标,看是否等于当前的下标,相等的话就返回,否则不返回值。

  var newArr = arr.filter(function(item,index){
     return arr.indexOf(item) === index;  // 因为indexOf 只能查找到第一个  
  });

3、利用for 循环 搭配 indexOf 去重 原理: 定义新数据,判断新数组中有没有字符串值,如果没有则返回 -1

function noRepeat(arr) {
		//定义一个新的临时数组 
		var newArr=[]; 
		//遍历当前数组 
		for(var i=0;i<arr.length;i++) {
		  //如果当前数组的第i已经保存进了临时数组,那么跳过,
		  //否则把当前项push到临时数组里面 
		  if(newArr.indexOf(arr[i]) === -1) {  //indexOf() 判断数组中有没有字符串值,如果没有则返回 -1 
		     newArr.push(arr[i]);
		  }
    	}
    return newArr
  }

4、借助新数组 通过 indexOf 方法判断当前元素在数组中的索引,如果与循环的下标相等则添加到新数组中,其原理是判断这个元素是数组中第一个元素,添加进去,后续相等的元素九不添加

function noRepeat(arr) {
        var newArr = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr.indexOf(arr[i]) == i) {
              newArr.push(arr[i]);
            }
        }
        return newArr;
    }

5、利用includes实现数组去重

function noRepeat(arr) {
     let newArr = [];
     for(i=0; i<arr.length; i++){
       if(!newArr.includes(arr[i])){
           newArr.push(arr[i])
       }
     }
    return newArr
  }

6、数组对象去重法:

let map = new Map();
for (let item of this.arr) {
   map.set(item.id, item);
}
this.arr = [...map.values()];
console.log(this.arr)

十一、说一下深拷贝和浅拷贝的区别,如何实现一个深拷贝?

1、浅拷贝:一个对象里面的所有的属性值和方法都复制给另一个对象

2、把一个对象的属性和方法一个个找出来,在另一个对象中开辟对应的空间,一个个存储到另一个对象中

区别:浅拷贝只是简单的复制,对对象里面的对象属性和数组属性只是复制了地址,并没有创建新的相同对象或者数组。而深拷贝是完完全全的复制一份,空间大小占用一样但是位置不同!!

十二、vue的生命周期有哪一些?说一下他们每个阶段做了什么操作?

1、创建前(beforeCreate)

在这个阶段:vue实例初始化之后,数据观察和事件机制未形成,无法获取到dom节点

2、创建后(create)

在这个阶段,vue 实例创建后,仍然无法获取到dom节点

3、载入前(beforeMount)

在这个阶段,vue挂载的跟节点已经创建,dom 操作将围绕根元素继续进行。

4、载入后(mounted)

在该节点数据和dom 都已经被渲染,常用于异步请求。

5、更新前(beforeUpdate)

该阶段中,vue遵循数据驱动dom的原则,beforeUpdate 函数在数据更新后,其dom中的数据也回改变

6、更新后(updated)

该阶段中,dom会与更新的内容进行同步

7、销毁前(beforeDestory)

在该阶段,是指清除vue实例与dom的关联,在销毁前,会触发beforeDestory钩子函数

8、销毁后(destory)

在销毁vue实例与dom的关联后,会触发destory

拓展:更新前(beforeUpdate)

当修改Vue实例的data时,Vue就会自动帮我们更新渲染视图,在这个过程中,Vue提供了beforeUpdate的钩子给我们,在检测到我们要修改数据的时候,更新渲染视图之前就会触发钩子beforeUpdate。html片段代码我们加上ref属性,用于获取DOM元素。Dom元素上的数据还没改变。

十三、vue 通讯方式有哪些?

1、父组件向子组件传值 prop

2、子组件向父组件传递emit

3、this.$parent 和 this.children

4、provide 和 inject 依赖注入

5、ref / refs 通过获取组件实例获取子组件数据

6、eventBus 事件总线 vue-bus 原理:new vue 把这个对象挂在在项目中vue的对象上,防止全局变量污染。

7、vuex

8、root在每个newVue实例的子组件中,其根实例可以通过root 在每个 new Vue 实例的子组件中,其根实例可以通过 root property 进行访问

十四、vuex 有几个属性及作用

state, getters, mutations, actions, modules

1、state vuex的基本数据,用来储存变量

2、getter 从基本数据state 派生的数据,相当于store的计算属性,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了变化才会被重新计算。

3、mutation:

用于提交更新数据的方法,必须是同步的,如果是异步的需要使用action

4、action:

(1)、这个用于提交的是action 而不是直接变更状态,

(2)、action 可以包含任意异步操作

5、modules:

模块化vuex,可以让每一个模块拥有自己的state,mutation,actio, getters,使得结构非常清晰,管理更为方便。

十五、vue的监听属性和计算属性有什么区别?

总结:

1、计算属性computed在使用时,函数里面的变量都会被监听,只要里面的某一个值变动,便会将整个函数执行一遍,而watch 只是监听某一个值,如果监听的值里面也有很多变量,也会全部监听。

2、计算后的属性可不在data中定义,如果定义会报错,因为对应的computed作为计算属性定义并返回对应的结果给这个变量,变量不可被重新定义和赋值。而watch 监听data中定义的变量变化

各子特点:

conputed特性:

1、是计算值,可用于处理props 或 emit传值

2、具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再执行函数

watch 特性

1、是观察的动作

2、应用:监听 props , $emit 或本组件的值执行异步操作

3、无缓存性,页面重新渲染时值不变化也会执行

十六、说一下防抖和节流,怎么实现?

二者的本质:为了控制函数在高频事件下的触发次数,降低函数执行频率,节省计算资源,提高性能。

防抖:高频事件下,控制执行最后一次(简单来说,就是设置一个毫秒,频繁触发,重置这个毫秒,到了一定时间在执行函数)

实现:利用闭包,在返回的函数体里,通过不断开启和清除定时器,在限定时间内不断点击,仍然只执行最后一次

// 通过闭包实现防抖
      function debounce(fn, delay) {
        let timer = null;
        return function () {
          if (timer !== null) {
            clearTimeout(timer);
          }
 
          timer = setTimeout(() => {
             // 箭头函数没有自己的this,改变指向,使其指向input。同时执行fn函数
            fn.call(this);
          }, delay);
        };
      }

节流:高频事件下,控制(函数)执行的次数,也就是一个单位时间内,只允许执行1次。

实现方式:通过控制锁的状态,实现节流

  // 通过定时器实现节流
      function throttle(fn, wait) {
        let lock = true;
        return function () {
          if (lock) {
            // 关锁
            lock = false;
            // 通过定时器开锁
            setTimeout(() => {
              lock = true;;
              fn();
            }, wait);
          }
        };

十七、vue-router的导航守卫有哪些?

1、全局守卫:router.beforeEach 2、全局解析守卫:router.beforeResolve 3、全局后置钩子:router.afterEach 不会接受 next 函数也不会改变导航本身 4、路由独享守卫:beforeEnter 5、组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

注意:参数的改变并不会触发路由守卫,可以用watch 监听,或者 beforeRouteUpdate

每个守卫都有三个参数:to,form,next to: 表示将要进入的路由目标 from:表示当前导航正要离开的路由 next: ...

image.png

路由独享守卫用法:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的守卫:

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  //不过,你可以通过传一个回调给 next来访问组件实例。
  //在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
  beforeRouteEnter (to, from, next) {
    next(vm => {
      // 通过 `vm` 访问组件实例
    })
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

离开守卫beforeRouteLeave:通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消:

beforeRouteLeave (to, from , next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

十八、你的登陆拦截怎么实现的?

十九、你是如何封装一个组件的,组件封装设计思想

参考:blog.csdn.net/qq449245884…

二十、闭包是什么?如何实现?

定义:闭包是指有权访问另一个函数作用域中的变量的函数

实现闭包:就是函数体内包含一个变量和一个函数,被包含的函数可以访问外部函数体的变量

JS 中闭包的优缺点及特性:

优点:

1、保护函数内的变量安全

2、在内存中维持一个变量

3、逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑

4、方便调用上下文的局部变量。

5、加强封装性,可以达到对变量的保护作用。

缺点:

1、常驻内存,会增大内存使用量,使用不当很容易造成内存泄漏。

2、浪费内存

特性:

1、函数嵌套函数

2、内部函数可以访问外部函数的变量

3、参数和变量不会被回收。