面试题系列:一

1.Vue2和Vue3的区别至少说5点 

1. 速度更快

  • Vue3 相比 Vue2 来说,Vue3 重写了虚拟 Dom 实现,编译模板的优化,更高效的组件初始化,undate性能提高 1.3 ~ 2 倍,SSR 速度提高了 2 ~ 3 倍。

2. 体积更小

  • 通过 webpack 的 tree-shaking 功能,可以将无用模块“剪辑”,仅打包需要的模块。

3. 更易维护

  • compositon Api 可与现有的 Options API 一起使用。
  • 灵活的逻辑组合与复用。
  • Vue3 模块可以和其他框架搭配使用。

4.vue2 的双向数据绑定是利用ES5 的一个 API Object.definePropert()对数据进行劫持 结合 发布订阅模式的方式来实现的。

vue3 中使用了 es6 的 ProxyAPI 对数据代理。

5.vue3就是说在组件可以拥有多个根节点,vue2不可以

6.Vue2与Vue3 最大的区别 — Vue2使用选项类型API(Options API)对比Vue3合成型API(Composition API) 

2.Vue3中组件通信的流程【父传子,子传父】

实现父子通信

首先在父组件中定义数据,然后通过:将数据通过子组件标签进行传递,子组件中通过defineProps和数据类型来获取父组件。

<template>
    <div>
        home
        <My_title :arrun="arrun"></My_title>
    </div>
</template>
<script setup>
import My_title from './components/my_title.vue'
import { ref, reactive } from 'vue'

// 传递的数据
const arrun = reactive({
    title:'张三',
    age:18,
    sex:'男'
})

</script>
复制代码
<template>
    <div>
        title
    </div>
</template>
<script setup>
import { ref } from 'vue'

// 通过props来接收
const props = defineProps({
    arrun: Object//指定接收的数据类型
})
console.log(props.arrun);

</script> 

子传父

首先在子组件定义defineEmits 触发的事件名,父组件通过@来触发方法默认值就是传递的数据。

<template>
    <div>
        home
        <My_title @add="add"></My_title>
    </div>
</template>
<script setup>
import My_title from './components/my_title.vue'
import { ref, reactive } from 'vue'

const add = (v) => {
    console.log(v.title);
}

</script>
复制代码
<template>
    <div>
        title
        <button @click="ONtil">点我传递数据给父组件</button>
    </div>
</template>
<script setup>
import { ref, reactive } from 'vue'

const emit = defineEmits(['add'])
const shuzu = reactive({ title: '123' })

const ONtil = () => {
    emit('add', shuzu)
}


</script> 

2.Apply/call/bind的原理是什么?

我们知道函数中的call,apply,bind都是可以修改函数的this指向。

当我们将普通函数改写为箭头函数时,箭头函数的 this 会在书写阶段(即声明位置)就绑定到它父作用域的 this 上。无论后续我们如何调用它,都无法再为它指定目标对象 —— 因为箭头函数的 this 指向是静态的,“一次便是一生”

call、apply 和 bind 之间的区别比较大,前两者在改变 this 指向的同时,也会把目标函数给执行掉;后者则只负责改造 this,不作任何执行操作

call 和 apply 之间的区别,则体现在对入参的要求上。前者只需要将目标函数的入参逐个传入即可,后者则希望入参以数组形式被传入

call 表现出的特性,我们首先至少能想到以下两点:

  • call 是可以被所有的函数继承的,所以 call 方法应该被定义在 Function.prototype 上
  • call 方法做了两件事:
    • 改变 this 的指向,将 this 绑到第一个入参指定的的对象上去;
    • 根据输入的参数,执行函数。

apply方法的模拟

apply的实现和call非常相似,区别只是在参数的处理上

bind方法的模拟

bind的实现稍微麻烦点,因为需要返回一个函数,需要判断些边界条件

3.说说你对原型和原型链的理解?

原型:JS声明构造函数(用来实例化对象的函数)时,会在内存中创建一个对应的对象,这个对象就是原函数的原型。构造函数默认有一个prototype属性,prototype的值指向函数的原型。同时原型中也有一个constructor属性,constructor的值指向函数对象。
通过构造函数实例化出来的对象,并不具有prototype属性,其默认有一个__proto__属性,__proto__的值指向构造函数的原型。在原型对象上添加或修改的属性,在所有实例化出的对象上都可共享。 

原型链:当在实例化的对象中访问一个属性时,首先会在该对象内部(自身属性)寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向原型中__proto__指向的上级原型中寻找,直至找到或Object.prototype.__proto__为止(值为null),这种链状过程即为原型链。

4.说说你对ES6中Generator的理解

generator 函数可以理解为异步函数的容器,这些异步任务执行过程中可被任意时刻暂停,任务暂停可以由函数外部控制。

执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态

形式上,Generator函数是一个普通函数,但是有两个特征:

  • function关键字与函数名之间有一个星号
  • 函数体内部使用yield表达式,定义不同的内部状态

执行 generator 函数返回的是一个 suspended 状态的 generator 对象。有点类似对象实例化,但不是实例化哈,他就是返回了一个 generator 对象

5.说说你对Event Loop的理解

首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环

JavaScript中,所有的任务都可以分为

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行

  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

js执行顺序是什么?

  1. JS是从上到下一行一行执行。
  2. 如果某一行执行报错,则停止执行下面的代码。
  3. 先执行同步代码,再执行异步代码

事件循环的执行过程

  1. JS是从上到下一行一行执行。
  2. 如果某一行执行报错,则停止执行下面的代码。
  3. 先执行同步代码,再执行异步代码
  4. 微任务在DOM渲染前触发,宏任务在DOM渲染后触发

事件循环的整体流程

  1. 先清空call stack中的同步代码
  2. 执行微任务队列中的微任务
  3. 尝试DOM渲染
  4. 触发Event Loop反复询问callbackQueue中是否有要执行的语句,有则放入call back继续执行

6.说说Promise和async/await 的区别?

什么事promise

Promise是ES6中的一个内置对象,实际是一个构造函数,是JS中进行异步编程的新的解决方案。

  • 特点:
    ① 三种状态:pending(进行中)、resolved(已完成)、rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都不能改变这个状态。
    ② 两种状态的转化:其一,从pending(进行中)到resolved(已完成)。其二,从pending(进行中)到rejected(已失败)。只有这两种形式的转变。
    Promise构造函数的原型对象上,有then()和catch()等方法,then()第一个参数接收resolved()传来的数据,catch()第一个参数接收rejected()传来的数据
  • 作用:
    ① 通常用来解决异步调用问题
    ② 解决多层回调嵌套的方案
    ③ 提高代码可读性、更便于维护

  什么是async/await

① async/await是ES7新特性
② async/await是写异步代码的新方式,以前的方法有回调函数和Promise
③ async/await是基于Promise实现的,它不能用于普通的回调函数
④ async/await与Promise一样,是非阻塞的
⑤ async/await使得异步代码看起来像同步代码,这正是它的魔力所在

async function 用来定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果,。如果你在代码中使用了异步函数,就会发现它的语法和结构会更像是标准的同步函数。

await 操作符用于等待一个 Promise 对象。它只能在异步函数 async function 中使用。

async 函数

async 函数的返回值为 Promise 对象,async 函数返回的 Promise 的结果由函数执行的结果决定

await 表达式

await 右侧的表达式一般为 promise 对象, 但也可以是其它的值

  1. 如果表达式是 promise 对象, await 返回的是 promise 成功的值
  2. 如果表达式是其它值, 直接将此值作为 await 的返回值

await 必须写在 async 函数中, 但 async 函数中可以没有 await,如果 await 的 Promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理。

区别:

1)简洁的代码

使用async函数可以让代码简洁很多,不需要像Promise一样需要些then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。

2) 错误处理:

Promise 中不能自定义使用 try/catch 进行错误捕获,但是在 Async/await 中可以像处理同步代码处理错误

3)条件语句

条件语句也和错误捕获是一样的,在 Async 中也可以像平时一般使用条件语句

7.说说浏览器事件循环和nodeJs的事件循环的区别? 

区别的话

其中一个主要的区别在于浏览器的 event loop 和 nodejs 的 event loop 在处理异步事件的顺序是不同的,nodejs 中有 micro event;其中 Promise 属于 micro event 该异步事件的处理顺序就和浏览器不同.nodejs V11.0 以上 这两者之间的顺序就相同了.

 浏览器事件循环:

执行第一个宏任务:全局Script脚本。产生的宏任务和微任务进入各自的队列中。执行完Script后,把当前的微任务队列清空(进行处理)。完成一次事件循环。
再取出一个宏任务,把在此期间产生的回调入队。再把当前的微任务队列清空,往复如此。
宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列,每轮循环都是由一个宏任务+多个微任务组成。

node事件循环

node事件循环比浏览器复杂,由6个宏任务队列+6个微任务队列组成。

 宏任务按照优先级从高到低依次是:

其执行规律是:在一个宏任务队列全部执行完毕后,去清空一次微任务队列,然后到下一个等级的宏任务队列,以此往复。

一个宏任务队列搭配一个微任务队列。六个等级的宏任务全部执行完成,才是一轮循环。

timers阶段: 用来执行timer(setTimeout()、setInterval())的回调
I/O callbacks阶段: 处理上一轮循环中少数未执行的I/O回调
idle prepare阶段:仅node内部使用,我们用不到
poll阶段:获取新的I/O时间,适当的条件下node将阻塞在这里
check阶段:执行setImmediate()的回调
close callbacks:执行socket的close时间回调 

总结:事件循环中的任务被分为宏任务和微任务,是为了给高优先级任务一个插队的机会:微任务比宏任务有更高优先级。
node 端的事件循环比浏览器更复杂,它的宏任务分为六个优先级,微任务分为两个优先级。
node 端的执行规律是一个宏任务队列搭配一个微任务队列,而浏览器是一个单独的宏任务搭配一个微任务队列。但是在 node11
之后,node 和浏览器的规律趋同。  

8.说说你对浏览器缓存机制的理解

 浏览器缓存

浏览器缓存则主要由前端开发在前端js上进行设置。

浏览器缓存:比如:localStorage,sessionStorage,cookie等等。这些功能主要用于缓存一些必要的数据,比如用户信息。比如需要携带到后端的参数。亦或者是一些列表数据等等。

不过这里需要注意。像localStorage,sessionStorage这种用户缓存数据的功能,他只能保存5M左右的数据,多了不行。cookie则更少,大概只能有4kb的数据

缓存原理

缓存的原理是在首次请求后保存一份请求资源的响应副本,当用户再次发起相同请求后,如果判断缓存命中则拦截请求,将之前存储的响应副本返回给用户,从而避免了重新向服务器发起资源请求

http缓存分类与流程

HTTP缓存应该算是前端开发中最常接触的缓存之一,它又可以细分为强制缓存协商缓存,二者最大的区别在于判断缓存命中时,浏览器是否需要向服务器端进行询问以协商缓存的相关信息,进而判断是否需要就响应内容进行重新请求,下面让我们来看看HTTP缓存的具体机制及缓存的决策策略。

 【注意】:

假设在不考虑客户端缓存容量与服务器算力的理想情况下,我们当然希望客户端浏览器上的缓存触发率尽可能高,留存时间尽可能长,同时还要Etag实现当资源更新时进行高效的重新验证。但实际情况往往是容量与算力都有限,因此就需要制定合适的缓存策略,来利用有限的资源达到最优的性能效果,明确能力的边界,力求在边界内做到最好。

9.说说你对浏览器内核的理解

浏览器内核的理解:

主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。

渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

JS引擎则:解析和执行javascript来实现网页的动态效果。

最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。

常见的浏览器内核有哪些?
Trident 内核:IE,MaxThon,TT,The World,360, 搜狗浏览器等。[又称 MSHTML]
Gecko 内核:Netscape6 及以上版本,FF,MozillaSuite/SeaMonkey 等
Presto 内核:Opera7 及以上。[Opera 内核原为:Presto,现为:Blink;]
Webkit 内核:Safari,Chrome 等。 [ Chrome 的:Blink(WebKit 的分支)] 

10.说说你对Vue的响应式原理的理解

Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。

如何侦测数据的变化

首先有个问题,在Javascript中,如何侦测一个对象的变化? 其实有两种办法可以侦测到变化:使用Object.defineProperty和ES6的Proxy,这就是进行数据劫持或数据代理。这部分代码主要参考珠峰架构课。

方法1.Object.defineProperty实现

Vue通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。

方法2.Proxy实现

Proxy 是 JavaScript 2015 的一个新特性。Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性,Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的。此外**Proxy支持代理数组的变化。**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值