咱们啥都不说,直接上笔试题,有面的小伙伴可以提前看一下哈。第一次写掘金文章,有问题见谅哈。
1、手写js数组去重的方法
let arr = [1, 1, "1", "1", true, true, "true", {}, {}, "{}", null, null, undefined, undefined]
// 方法1
let uniqueOne = Array.from(new Set(arr))
console.log(uniqueOne)
// 方法2 利用Map
let uniqueTwo = arr => {
let map = new Map();
let brr = []
arr.forEach((item) => {
if (!map.has(item)) {
map.set(item, true)
brr.push(item)
}
})
return brr
}
console.log(uniqueTwo(arr))
// 方法3 创建一个新数组判断新数组中是否存在该元素,如果不存在则将此元素添加到新数组中
let uniqueThree = arr => {
let brr = []
arr.forEach(item => {
// 使用indexOf 返回数组是否包含某个值 没有就返回-1 有就返回下标
if (brr.indexOf(item) === -1) brr.push(item)
// 或者使用includes 返回数组是否包含某个值 没有就返回false 有就返回true
// if (!brr.includes(item)) brr.push(item)
})
return brr
}
console.log(uniqueThree(arr))
// 方法4 利用数组中的filter方法
let uniqueFour = arr => {
// 使用 filter 返回符合条件的集合
let brr = arr.filter((item, index) => {
return arr.indexOf(item) === index
})
return brr
}
console.log(uniqueFour(arr))
// 方法5 借助indexOf()方法判断此元素在该数组中首次出现的位置下标与循环的下标是否相等。
const uniqueFive = (arr) => {
for (var i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) != i) {
arr.splice(i, 1);//删除数组元素后数组长度减1后面的元素前移
i--;//数组下标回退
}
}
return arr;
}
console.log(uniqueFive(arr))
2、手写防抖和节流函数
/* 防抖函数,只是简单的防抖函数,没有改进 */
let debounce = function (callback, delay) {
// 使用闭包的外部变量来定义定时器
let timer;
return function () {
// 判断是否已经存在定时任务
if (timer) {
/*
如果已有定时任务就将已有任务清楚,
再重新生成新的定时任务
*/
clearTimeout(timer)
}
// 生成定时任务并赋值给timer
timer = setTimeout(() => {
console.log(this);
callback.call(this)
}, delay)
}
}
/* 通过判断flag来实现节流 */
let throttle = function (callback, delay) {
// 判断依据
let flag = true;
return function () {
// 如果flag为false便忽略这次操作
if (flag) {
/* 设定定时器,当任务执行时将flag恢复false,
允许下一次的事件触发
*/
setTimeout(() => {
callback.call(this);
flag = true;
}, delay);
}
//在定时任务执行之前,flag始终为false
flag = false;
};
};
/* 通过时间来判断 */
let throttling = function (callback, delay) {
// 设置一个记录的时间,用以判断是否忽略操作
let time = 0;
return function () {
// 创建当前时间,用以判断是否超过设定好的延迟
let now = new Date();
// 如果两次事件触发时间差大于设定好的毫秒数,则触发新的请求
if (now - time > delay) {
// 执行回调函数
callback.call(this);
// 将记录的时间设置为这一次执行任务的时间
time = now;
}
};
};
3、手写promise.all方法
Promise.all = function (promises) {
//返回结果为Promise对象
return new Promise((resolve, reject) => {
//声明变量
let count = 0
let resArr = []
promises.forEach((item, i) => {
item.then(v => {
//对象的状态是成功
count++
//添加每个promise的成功结果
resArr[i] = v
//判断每个promise对象的状态都是成功
if (count === promises.length) {
resolve(resArr)
}
}, e => {
//修改状态
reject(e)
})
})
})
}
4、vue的响应式原理
vue响应式也叫作数据双向绑定,大致原理阐述:
首先我们需要通过Object.defineProperty()方法把数据(data)设置为getter和setter的访问形式,这样我们就可以在数据被修改时在setter方法设置监视修改页面信息,也就是说每当数据被修改,就会触发对应的set方法,然后我们可以在set方法中去调用操作dom的方法。
此外,如果页面有input用v-model绑定数据,我们需要在这种绑定了data的input元素上添加监听,添加input事件监听,每当input事件被触发时,就修改对应的data。
vue实现数据响应式,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,换句话说是Observe,Watcher以及Compile三者相互配合,
Observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者 Compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数 Watcher作为Observe和Compile中间的桥梁,订阅Observe属性变化的消息,触发Compile更新函数
5、你对webpack或者vite的理解、谈谈你对前端工程化的看法
Webpack 会遍历你的应用程序中的所有文件,并将代码转换成开发服务器,Webpack 将整个代码渲染到一个开发环境中。
webpack从一个entry.js文件开始,将其依赖的所有js或者其他assets通过loader打包成一个的文件, 随后这个打包后的文件将被从server传递到客户端浏览器运行。
因为这样处理规则,当保存文件时,整个 JavaScript 包将由 Webpack 重新构建,这就是为什么更改可能需要长达 10 秒才能反映在浏览器中,更新速度会随着应用体积增长而直线下降。
Vite 的工作方式不同,它不会遍历整个应用程序,Vite 只是转换当时正在使用的文件/模块。
Vite的核心理念:非打包开发构建
Vite 的核心思想:浏览器请求它时,使用 ES 模块转换并提供一段应用程序代码。
开始开发构建时,Vite将首先将的JavaScript 模块分为两类:依赖模块和源码模块。
依赖项模块是第三方依赖的代码,从node_modules文件夹中导入的JavaScript 模块。这些模块将使用esbuild进行处理和捆绑,esbuild是一个用 Go 编写的 JavaScript 打包工具,执行速度比 Webpack 快 10-100 倍。
源码模块是源代码,即业务代码,通常涉及特定库的扩展,如、.jsx或.vue文件.scss。
它使用基于路由的代码拆分来了解代码的哪些部分实际需要加载,因此,它不必重新打包所有内容。
它还使用现代浏览器中的原生 ES 模块支持来交付代码,这让浏览器可以在开发中承担打包工作。
在生产方面,虽然现在所有主流浏览器都支持原生 ES 模块,但它实现了诸如 tree-shaking、延迟加载和通用块拆分等性能优化技术,仍然比非打包应用程序带来更好的整体性能。出于这个原因,Vite附带了一个预先配置的build命令,该命令使用Rollup打包来打包和实现各种性能优化。
Webpack 这样的基于打包器的工作流必须在单个浏览器请求之前处理整个 JavaScript 模块,但 Vite 只在单个浏览器请求之前处理依赖模块。这就是为什么 Vite 能够比 Webpack 更快地处理开发构建。
6、数组中哪些方法会改变原数组
-
改变原数组
push/pop/shift/unshift/sort/reverse/splice
-
不改变原数组
concat/every/some/join/filter/map/toString/slice
7、Vue3组合式Api怎样理解
转自zhuanlan.zhihu.com/p/527488858
组合式 API 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语,涵盖了以下方面的 API:
- 响应性 API:例如
ref()和reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。 - 生命周期钩子:例如
onMounted()和onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑。 - 依赖注入:例如
provide()和inject(),使我们可以在使用响应性 API 时,利用 Vue 的依赖注入系统。
为什么要有组合式 API?
更好的逻辑复用
组合式 API 最基本的优势是它使我们能够通过组合函数来实现更加简洁高效的逻辑复用。它解决了所有mixins的缺陷,那是选项式 API 中一种逻辑复用机制。
更灵活的代码组织
许多用户都喜欢选项式 API,因为在默认情况下就能够写出有组织的代码:任何东西都有其对应的选项来管理。然而,选项式 API 在单个组件的逻辑复杂到一定程度时,也面临了一些无法忽视的限制。这些限制主要体现在需要处理多个逻辑关注点的组件中,在许多 Vue 2 已经上线的生产应用中可以看到这一点。
我们以 Vue CLI GUI 中的文件浏览器组件为例:这个组件承担了以下几个逻辑关注点:
- 追踪当前文件夹的状态,展示其内容
- 处理文件夹的相关操作(打开、关闭和刷新)
- 支持创建新文件夹
- 可以切换到只展示收藏的文件夹
- 可以开启对隐藏文件夹的展示
- 处理当前工作目录中的变更
这个组件最原始的版本是由选项式 API 写成的。而如果用组合式 API 重构这个组件,将会变成:
现在与同一个逻辑关注点相关的代码被归为了一组:我们无需再为了一个逻辑关注点在不同的选项块间来回滚动切换。此外,我们现在可以不费吹灰之力地将这一组代码移动到一个外部文件中,不再需要为了抽象而重新组织代码,大大降低了重构成本,这在长期维护的大型项目中非常关键。
更好的类型推导
近几年来,越来越多的开发者开始使用TypeScript书写更健壮可靠的代码,TypeScript还提供了非常好的IDE 开发支持。然而选项式 API 是在 2013
年创建的,那时并没有想到需要进行类型推导。因此我们做了一些荒谬复杂的类型体操来实现对选项式 API 的类型推导。但尽管做了这么多的努力,选项式 API 的类型推导仍然无法适配混入和依赖注入。
因此,很多想要搭配 TS 使用 Vue 的开发者采用了由vue-class-component提供的Class API。然而,基于 Class 的 API 非常依赖 ES 装饰器,在 Vue 2019 年开发完成后,它仍是一个仅处于 vue 2的语言功能。我们认为将这样一种不稳定的方案作为官方 API 的一种实现形式风险过高,在那之后装饰器提案还进行了一些较大的变动,在书写这篇文档时仍未到达 vue 3。另外,基于 Class 的 API 和选项式 API 在逻辑复用和代码组织方面有相同的限制。
相比之下,组合式 API 主要利用基本的变量和函数,它们本身就是类型友好的。用组合式 API 重写的代码可以享受到完整的类型推导,不需要书写太多类型标注。大多数时候,用 TypeScript 书写的组合式 API 代码和用 JavaScript 写都差不太多!这也同样让许多纯 JavaScript 用户能从IDE 中享受到部分类型推导功能。
生产包体积更小
搭配<script setup>使用组合式 API 比等价情况下的选项式 API 更高效,对代码压缩也更友好。这是由于<script setup>;形式书写的组件模板被编译为了一个内连函数,和<script setup>中的代码位于同一作用域。不像选项式 API 需要依赖this上下文对象访问属性,被编译的模板可以直接访问<script setup>中定义的变量,无需一个代码实例从中代理。这对代码压缩更友好,因为变量的名字可以变得更短,但对象的属性名则不能。