2024前端面试题Vue+基础(Html+JS+CSS)

337 阅读12分钟

Html+JS+CSS面试题

1.new运算符做了什么事情?

1.创建一个空对象

2.构造函数的this指向这个空对象

3.执行构造函数

4.返回类型:分为引用类型和值类型。如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型 的对象

2.介绍一些ES6的新特性

1.箭头函数

2.Promise (async await)

3.let、const、symbol

4.Map、Set....等等

3.前端跨域如何处理(什么是同源策略)

同源策略是浏览器的一种安全机制,当端口、域名、地址都相同时,允许访问。当端口、域名、地址中有一个不同,不允许访问。

当端口、域名、地址不同时,就要进行跨域

处理前端跨域问题的方法包括:

1.代理服务器:通过在自己的服务器上设置代理,将请求发送给目标服务器,然后将响应返回给前端,绕过同源策略限制

2.JSONP(JSON with Padding):利用<script>标签的跨域特性,动态创建<script>标签来请求数据,服务器返回的数据会被当做JavaScript代码执行,从而实现跨域请求。

3.CORS(Cross-Origin Resource Sharing):在服务器端设置响应头,允许指定源的请求访问资源,从而实现跨域请求

4.typeof 和 instanceof的区别

typeof类型的检测

image.png

instanceof查对象是否属于特定的类或类型

image.png

5.var、let和const的区别

var存在变量提升,一个变量可以声明多次,后面会覆盖前面的变量。

作用域:在函数里声明是局部变量,不在函数里中声明则是全局变量

let不存在变量提升,必须先声明后使用。暂时性死区:就是先声明后使用

作用域:块级作用域,不能在同一个作用域中重复声明,在不同的作用域中可以重复声明

const是一个常量一个只读的变量,值不会改变,必须先声明后使用。暂时性死区:就是先声明后使用

作用域:块级作用域,不能在同一个作用域中重复声明,在不同的作用域中可以重复声明

const要想改变值:不可以改变内存地址就可以改变值

简单点说:如果是一个对象,然后对象里套对象,我们只要保证第一个对象不变,其他的对象都可以进行改变

6.常用的数组方法有哪些

写出一部分

1.push、pop、shift、unshift

2.forEach、Map、reduce、every、some、filter、find、findindex

3.keys、values、entries

4.sort

5.slice、splice、join、reverse、indexof、includes

7.介绍下原型和原型链

原型:每个函数中都有prototype属性,该属性指向原型对象。

在这我们应该都有听过一句话一切皆对象这句话而我们的原型链的最上面它就是一个对象,硬要说对象上面是什么,那我只能告诉是null

原型链:每个对象都有一个__proto__属性,指向它的原型对象。

当我们访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会沿着原型链(prototype chain)向上查找直到找到对应的属性或方法(我们自己写的属性优先级高于原型里的属性或方法

8.介绍下闭包

闭包:在JavaScript中,当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量时,就形成了闭包。

闭包的好处:

  1. 可以创建私有变量和私有函数,保护数据安全性。
  2. 可以实现数据封装,使代码模块化和可重用性更强。
  3. 可以延长变量的生命周期,使得函数可以访问定义时的上下文。

闭包的坏处:

  1. 可能会导致内存泄漏,因为闭包中的变量不会被释放。
  2. 可能会增加代码复杂性和理解难度,特别是在处理多层嵌套的闭包时。
  3. 可能会影响性能,因为闭包会增加函数的作用域链的长度,导致访问变量时需要更多的时间。

闭包使用的场景:防抖

function debounce(func, wait) {
    let timeout;
    return function() {
        const context = this;
        const args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            func.apply(context, args);
        }, wait);
    };
}

9.transition和animation的区别

transition:过渡动画,用于简单的动画操作

animation:帧动画,用于复杂的动画操作

如何区分什么是复杂的动画操作:

1.一个动画用到2个以上的属性可以定义为复杂动画,推荐使用帧动画。 2.用到2个属性或者以下推荐使用过渡动画,将像这个名字一样过渡从一边过渡到另一边

10.hash路由和history路由的区别

hash路由(哈希):

  1. 以 '#' 符号开头:example.com/#/about
  2. 通过监听 URL 中的 hash 变化来切换页面内容
  3. 支持所有现代浏览器,包括 IE8 及以上版本
  4. 不需要服务器端配置

history路由:

  1. 不带 '#' 符号:example.com/about
  2. 通过 HTML5 History API 来实现页面内容的切换
  3. 需要服务器端配置,否则会出404,通常会重定项到404页面

总的来说,Hash 路由简单易用,兼容性好;而 History 路由更加友好,但需要额外的配置和对浏览器的支持。

11.浏览器缓存

1.http缓存的好处

  1. 减少网络传输:通过缓存,客户端可以直接从本地缓存中获取资源,减少了网络传输的次数,节省了带宽和减少了网络延迟。
  2. 提高网站性能:缓存可以减少服务器的负载,加快网站的加载速度,提高用户体验。
  3. 节省服务器资源:缓存可以减少服务器的请求处理次数,节省服务器资源,提高服务器的响应速度。
  4. 减少数据传输成本:通过缓存可以减少数据传输的成本,尤其是在大流量网站上,节省了数据传输费用。
  5. 提高用户体验:网站加载速度快了,用户体验也会得到提升,用户更愿意访问和使用网站。
  6. 降低网络拥堵:缓存可以减少网络传输的数据量,降低网络拥堵的可能性,提高网络的稳定性和可靠性。

浏览器缓存分为:强缓存和协商缓存

2.强缓存

  1. Expires:response header里的过期时间
  2. Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存
  3. -no-cache:不使用本地缓存,需要使用协商缓存

强缓存总结:当浏览器请求一个资源时,服务器会在响应头中设置缓存控制字段,比如Cache-Control或者Expires,告诉浏览器这个资源的有效期。如果浏览器再次请求这个资源,并且在有效期内,浏览器会直接从本地缓存中获取资源,而不会向服务器发送请求

3.协商缓存

设置协商缓存:cache-control:-no-cache(不强制缓存)

  1. Last-Modify/if-Modify-Since:Last-Modify是一个时间标识该资源最后修改时间(是1秒,有点bug的),但不过服务器修改的时间如果是300ms,那服务器也会返回304,让浏览器走缓存。当服务器收到if-Modify-Since后,根据资源的最后时间判断是否命中缓存。
  2. Etag/if-None-Match:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识符(hash哈希)。if-None-Match:发现资源具有Etag声明,则再次向服务器请求时带上头if-None-Match。服务器收到if-None-Match后则与被请求资源的相应校验进行比对,判断是否命中缓存。

Last-Modify的值是资源最后修改时间

Etag的值是文件对应的hash值(hash是需要生成,会加大开销服务器)

协商缓存总结:当强缓存失效时,浏览器会向服务器发送一个请求,服务器会根据请求头中的If-Modified-Since或者If-None-Match字段判断资源是否有更新。如果资源没有更新,服务器会返回一个304 Not Modified的响应码,告诉浏览器可以继续使用缓存中的资源

总结:强缓存和协商缓存都可以有效减少网络请求,提高页面加载速度。强缓存适用于那些不经常变化的资源,而协商缓存适用于经常变化的资源

12.箭头函数和普通函数的区别

函数构造函数this指向
箭头函数匿名函数没有捕获上下文的this
普通函数具名函数指向调用它的对象,用构造函数,this指向创建的对象实例

13.call、apply、bind的区别

指向传参方式执行机制
call列表传参,一次性传入立即执行
bind列表传参,可以分开多次传入不立即执行而是生成一个修改 this 之后的新函数
apply数组传参,一次性传入立即执行

13.Promise

定义:是异步编程的一种解决方案,解决了异步问题和回调地狱 promise的三个状态:pending(等待)、fulfilled(成功)、rejected(失败)

注:(如果确定了状态,则结果将永远不变,不能再次更改)

原型:

  1. 成功Promise.then()→fulfilled
  2. 失败Promise.carchc()→rejected
  3. 成功/失败都执行Promise.finally()→fulfilled/rejected

静态方法:

  1. 成功Promise.resolve()→fulfilled
  2. 失败Promise.reject()→rejected

不是静态方法:

  1. Promise.all:接受一个Promise对象的数组作为参数,当这个数组里面的promise对象都为fulfilled时,all的状态为fulfilled。当这个数组里面的promise对象至少有一个为rejected时,all的状态为rejected
  2. Promise.allSettled:接受一个Promise对象的数组作为参数,当这个数组里面的promise对象状态都发生改变,allSettled状态为fulfilled
  3. Promise.race:接受一个Promise对象的数组作为参数,找到最先改变状态的promise,race的状态和最先改变状态的promise一致
  4. Promise.any:接受一个Promise对象的数组作为参数,找到最先改变状态为fulfilled的promise,any的状态和该状态一致。如果都没有fulfilled状态的promise,any为失效

14.深拷贝和浅拷贝

深拷贝:将一个对象完全的复制一份,复制之后是一个独立的对象,修改原对象不会影响复制的对象

浅拷贝:只复制对象的引用,修改原对象会影响复制的对象

实现方式:

  1. JSON.parse(JSON.stringify()) 注:有bug,比如时间的转换会有问题
  2. 手动递归
  3. 使用插件

15.数据类型

基本数据类型:string、number、boolean、undefined、null、symbol

引用数据类型:object、Array、Function、Date

16.事件传播方式

分为捕获、目标、冒泡 三阶段

  1. 事件捕获:先由文档的根节点document往事件触发对象,从外向捕获事件对象;
  2. 目标:到达目标事件位置,触发事件逻辑;
  3. 事件冒泡:再从目标事件往文档的根节点方向回溯,从内向外冒泡事件对象;

捕获从外向内到目标, 目标从内向冒泡出去

image.png

Vue 面试题

1.Mixin覆盖的逻辑

首先我们分成两个块,一个是对象,另一个是数组

对象:例如可以分为 datamethodscomponents 和 directives

var mixin = {  
    methods: {  
     foo: function () { console.log('foo') },  
     conflicting: function () {  console.log('from mixin')  }  
   }  
}

var vm = new Vue({
    mixins: [mixin],
>     methods: {
        bar: function () {
            console.log('bar')
        },
        conflicting: function () { 
            console.log('from self')
        }
    }
})

vm.foo() // => "foo"  
vm.bar() // => "bar"  
vm.conflicting() // => "from self"

对象的总结:组件有先使用组件,没有使用mixin,当同时引入多个mixin同名时后摄覆盖先摄

数组:同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。

var mixin = {  
  created: function () {  
  console.log('mixin对象的钩子被调用')  
 }  
}  
  
new Vue({  
  mixins: [mixin],  
  created: function () {  
   console.log('组件钩子被调用')  
  }  
})  
  
// => "mixin对象的钩子被调用"  
// => "组件钩子被调用"

钩子函数的总结:把组件的钩子函数和mixin钩子函数,合并成一个数组,mixin钩子函数比组件钩子函数先执行。如果有组件的先使用组件,没有组件的使用mixin,当同时引入多个mixin时,同名时《后摄覆盖先摄》用这个词可能不太对,但不过后面的比前面的优先级要高,优先使用后面的

2.Vue2和Vue3的生命周期

vue3vue2生命周期
setup()beforCreate()创建前
setup()ceatd()创建后
onBeforeMount()beforMount()挂载前
onMounted()mounted()挂载后
onBeforeUpdate()beforUpdate()更新前
onUpdated()updated()更新后
onBeforeUnmount()beforDestroy()销毁前
onUnmounted()destroyed()销毁后

3.watch和computed的区别

名称缓存请求执行其他
watch侦听器异步默认第一次不执行deep: true 深度监听immediate: true立即执行
computed计算属性同步执行在依赖的数据发生变化时才会重新计算

4.v-if和v-show的区别

v-if和v-show都是Vue.js中用来控制元素显示和隐藏的指令

名称场景Dom
v-if条件渲染渲染时就确定,不频繁切换重建/销毁
v-show条件展示频繁切换元素通过CSS的display属性来控制是否显示

5.vue中父子组件的生命周期执行顺序

父:beforCreate() 》》》创建前

父:ceatd() 》》》创建后

父:beforMount() 》》》挂载前

子:beforCreate() 》》》创建前

子:ceatd() 》》》创建后

子:beforMount() 》》》挂载前

子:mounted() 》》》挂载后

父:mounted() 》》》挂载后

6.vue2父子组件传参的方式

props - $emit

// 子组件 ChildComponent.vue
<template>
  <div>
    <p>子组件接收到的参数:{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: {
    message: String
  }
}
</script>

// 父组件 ParentComponent.vue
<template>
  <div>
    <child-component :message="hello"></child-component>
  </div>
</template>

<script>
import ChildComponent from '@/components/ChildComponent.vue'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      hello: 'Hello, Vue!'
    }
  }
}
</script>

7.vue双向绑定原理

原理是基于数据劫持和发布-订阅模式实现的

  1. 数据劫持:Vue通过Object.defineProperty()方法来劫持数据的get和set操作,当数据发生变化时,会触发相应的更新操作。
  2. 发布-订阅模式:Vue使用发布-订阅模式来实现数据和视图之间的响应式更新。当数据发生变化时,会通知所有订阅者进行更新。

当我们在Vue中使用v-model指令进行双向绑定时,Vue会在编译阶段对模板进行解析,找到所有需要双向绑定的数据,并为这些数据创建对应的订阅者。当数据发生变化时,会通知对应的订阅者进行更新,从而实现数据和视图的同步更新

总结:Vue双向绑定的原理就是通过数据劫持和发布-订阅模式来实现数据和视图之间的同步更新

8.vue2和vue3的响应原理

Vue2响应式:核心代码是使用Object.defineProperty()来劫持对象中每一个属性的set和get方法。

当我们直接给对象添加属性时,Vue无法监听到这个属性的变化,因为Vue在实例化时已经对data中的属性进行了响应式处理,而后添加的属性并没有被Vue监听到

通过调用Vue.$set()方法,Vue会将新增的属性变为响应式的,从而实现对新增属性的监听

<template>
  <div>
    <h1>{{ obj }}</h1>
    <button @click="add">添加一个新属性</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      obj:{
        name:'张三',
        age:18,
      } 
    };
  },
  methods: {
    add(){
      // 监听不到
      // this.obj.sex='男'
      
      // 使用Vue.$set()方法进行响应式的添加和删除
      this.$set(this.obj,'sex','男')
    }
  },
  computed: {},
  watch: {},
  mounted() {},
  created() {},
};
</script>


Vue3中使用了ES6的Proxy来实现响应式,当对象的属性发生变化时,Vue3会自动触发相关的更新操作,使视图保持同步。

Vue3中引入了更灵活的reactiveref函数来创建响应式数据

<template>
  <div>
    <h1>{{ obj }}</h1>
    <button @click="add">添加一个新属性</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const obj = ref({
  name: '张三',
  age: 18,
})

const add = () => {
  obj.value.sex = '男';
}
</script>

9.vue3的ref和reactive的区别

ref用于包装基本数据类型(字符串,数值),reactive用于复杂数据类型(对象)

ref使用时要用.value属性获取内容,reactive使用时可以直接使用

10.watch和watchEffect的区别

image.png