1. 说一下 == 和 === 的区别
== 运算符执行类型转换后的相等比较,即: == 运算符在比较之前会尝试将两个操作数转换为相同的类型,然后在进行比较。
1 == '1' // true
=== 运算符 执行严格的相等比较,不会进行类型转换。只有在类型相等并且值相等的情况下才会返回true。
1 === 1 // true
1 === '1' // false
2. [] == [] 输出的值; [] === [] 输出的值;为什么?
[] == [] // false
[] === [] // false
// javaScript 中数组是引用数据类型,使用的是引用比较,意味着只有当数组引用同一个内存地址的时候,才会被认为是相等的
3. script标签的加载方式
script标签的加载方式有多种:
- 内联方式 :将JavaScript代码直接嵌入到HTML文件中,使用
<script>
标签包裹代码,并将其放置在<head>
或<body>
标签内。 - 外部文件方式:将JavaScript代码存储在外部的.js文件中,然后使用
src
属性将外部文件链接到HTML文件中。 - 延迟加载方式:通过在
<script>
标签中添加defer
属性,将JavaScript代码推迟到文档解析完毕后再执行。 - 异步加载方式:通过在<script>标签中添加async属性,使得JavaScript代码在加载时不会阻塞页面的解析和渲染过程。
4. defer 和 async 加载的区别
defer是延迟加载方式,延时加载的脚本会在文档解析完毕后按照顺序执行,执行时会保持对页面的渲染控制权。也就是说,脚本执行不会阻塞页面的渲染,可以在脚本中操作DOM元素。延时加载的脚本会在文档解析期间下载,但在文档解析完成后才会执行。
async是异步加载方式,异步加载的脚本在下载完成后立即执行,执行时不会阻塞页面的渲染。异步加载的脚本不会保持对页面的渲染控制权,在异步加载的脚本中操作DOM元素时需要注意,可能需要等待页面的完全加载或使用其他方式确保DOM元素可用。
5. 闭包的定义、闭包的使用场景、结合项目经验说一下
闭包是指在函数内部创建的函数以及该函数所能访问的外部变量的组合。
使用场景:
- 创建私有变量
- 延长变量的生命周期
相关链接:面试官:说说你对闭包的理解?闭包使用场景 | web前端面试 - 面试官系列 (vue3js.cn)
6. 了解事件循环吗,谈谈你对他的理解
事件循环的核心思想是基于事件触发和回调函数的机制。在事件循环中,存在一个事件队列(Event Queue),用于存储待处理的事件和回调函数。事件循环会不断地从事件队列中取出事件,并执行相应的回调函数。
事件循环的执行过程可以简单概括为以下几个步骤:
- 执行同步代码:首先,事件循环会执行当前线程中的同步代码,直到遇到异步操作。
- 处理异步操作:当遇到异步操作时,它会被注册并放入相应的事件队列中,然后继续执行下面的代码。
- 等待执行时机:当同步代码执行完毕后,事件循环会检查是否有待处理的异步操作。如果有,它会从事件队列中取出一个事件,并执行相应的回调函数。
- 执行回调函数:事件循环会执行被取出的事件对应的回调函数,这个过程是同步的。如果在执行回调函数的过程中产生了新的异步操作,那么这些操作会被注册并放入事件队列中,等待下一次事件循环的执行。
- 重复上述步骤:事件循环会不断地重复上述步骤,直到事件队列中没有待处理的事件。
事件循环的机制保证了异步操作的执行顺序和正确性。它使得 JavaScript 能够处理大量的异步任务,如定时器、网络请求、事件处理等,而不会阻塞主线程的执行。这对于构建高效、响应迅速的应用程序至关重要。
相关链接:面试官:说说你对事件循环的理解 | web前端面试 - 面试官系列 (vue3js.cn)
7. 讲一下下面这段代码的打印顺序,并解释一下
async function async1(){
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2(){
console.log("async2");
}
console.log("script start");
setTimeout(function(){
console.log("settimeout");
},0)
async1();
new Promise(function(resolve){
console.log("promise1");
resolve();
console.log("promise3");
}).then(function(){
console.log("promise2");
})
console.log("script end");
这里考察的是同步异步的问题,上述代码详解见:面试官:说说你对事件循环的理解 | web前端面试 - 面试官系列 (vue3js.cn)
8. 不使用组件库,实现一个自定义的button按钮,可以传递danger属性控制组将样式,并且可以控制button按钮的大小
// 基于一个vue2的项目中封装这个自定义button
<template>
<button :class="[size, { danger: isDanger }]">
<slot></slot>
</button>
</template>
<script>
export default {
name: "buttonMy",
props: {
size: {
default: "middle",
type: String,
},
isDanger: {
default: false,
type: Boolean,
},
},
};
</script>
<style scoped lang="less">
.middle {
width: 100px;
}
.big {
width: 200px;
}
.small {
width: 50px;
}
.danger {
color: red;
}
</style>
// 使用的时候 先引入
import Mybutton from '@/components/Mybutton.vue'
// 在注册
components: {
buttonMy,
},
// 在模板中引入
<buttonMy size="big" :isDanger="true">提交</buttonMy>
9. 手写一个深拷贝 (这里考察的是递归的方法,但是这里给做一个延伸)
方法一: 使用JSON方法
function deepClone (obj) {
return JSON.parse(JSON.stringify(obj))
}
方法二: 递归的方法
function deepClone1 (obj) {
if (typeof obj !== 'object' || obj == null) {
return obj
}
let deepObj = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
deepObj[key] = deepClone1(obj[key])
}
}
return deepObj
}
方法三: 使用第三方库
比如,lodash的 _.cloneDeep() 方法
let _ = require('lodash');
let obj = {a:1, b: {c: 2}};
let deepCopy = _.cloneDeep(obj);