前端面试提问(3)
1、js两个数相加会不会丢精度?
可能会遇到精度丢失的问题。JavaScript 使用的是 IEEE 754 浮点数标准,即一种二进制表示法,有时不能准确地表示十进制小数。如果你需要进行精确的十进制数值计算,可以使用一些处理精确数值的库,比如 BigInt
、Decimal.js
或者 big.js。
2、整数溢出
在 JavaScript 中,整数溢出是指超出了 JavaScript 数值类型的范围,导致无法准确表示这个整数。如果需要处理大整数,可以考虑使用 BigInt
类型或者使用第三方库来处理。
3、接口调用超时,抛出错误怎么实现?
用Promise.race实现,其 接受一个包含多个 Promise 的可迭代对象(比如数组),并返回一个新的 Promise,该 Promise 将与最先解决(fulfilled)或拒绝(rejected)的 值。
function timeoutPromise(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('接口调用超时'));
}, ms);
});
}
function apiCall() {
return new Promise((resolve, reject) => {
// 模拟异步接口调用
setTimeout(() => {
resolve('接口调用成功');
}, 500);
});
}
// 设置超时时间
const timeoutDuration = 300;
// 使用 Promise.race 同时执行接口调用和超时 Promise
Promise.race([apiCall(), timeoutPromise(timeoutDuration)])
.then((result) => {
// 如果接口调用在超时前完成,result 将是接口调用的结果
console.log(result);
})
.catch((error) => {
// 如果超时,或者接口调用出错,error 将是相应的错误对象
console.error(error.message);
});
4、 发布订阅者模式和观察者模式区别
- 发布-订阅者模式: 使用一个消息代理(消息通道或者事件总线)作为中介,发布者和订阅者之间没有直接联系,而是通过事件中心进行通信。EventBus.
- 观察者模式: 观察者直接订阅被观察者,被观察者维护观察者列表,当状态发生变化时,直接通知观察者。Observer.
4.1 观察者模式
// 被观察者(Subject)
class Subject {
constructor() {
this.observers = []; // 观察者列表
}
// 添加观察者
addObserver(observer) {
this.observers.push(observer);
}
// 移除观察者
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
// 通知观察者状态变化
notifyObservers(message) {
this.observers.forEach(observer => observer.update(message));
}
}
// 观察者(Observer)
class Observer {
constructor(name) {
this.name = name;
}
// 被观察者通知时调用的方法
update(message) {
console.log(`${this.name} 收到通知: ${message}`);
}
}
// 创建被观察者和观察者
const newsSubject = new Subject();
const observer1 = new Observer('Observer 1');
const observer2 = new Observer('Observer 2');
// 观察者订阅被观察者
newsSubject.addObserver(observer1);
newsSubject.addObserver(observer2);
// 被观察者通知观察者
newsSubject.notifyObservers('新闻更新: 今天发生了一些重要事件。');
4.2 Vue.js 中的响应式原理
Object.defineProperty+观察者模式
-
Observer(观察者):
- Vue.js 会遍历对象的每个属性,使用
Object.defineProperty
把属性转换为 getter 和 setter。 - 当数据被访问或者修改时,会触发 getter 和 setter 中相应的逻辑。
- Vue.js 会遍历对象的每个属性,使用
-
Dep(依赖,订阅者):
- 为每个被观察的属性创建一个 Dep 对象,用于管理依赖于该属性的 Watcher 对象。
- Dep 对象有一个数组用于存储 Watcher。
-
Watcher(桥梁):
- Watcher 对象会订阅一个或多个 Dep 对象。
- 当被观察的数据发生变化时,Watcher 对象会收到通知,从而执行相应的回调函数。
function defineReactive(obj, key, val) {
let dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
dep.notify();
}
}
});
}
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
class Watcher {
constructor() {
Dep.target = this;
}
update() {
console.log('数据发生变化,执行更新操作');
}
}
Dep.target = null;
const data = {};
defineReactive(data, 'message', 'Hello, Vue!');
const watcher = new Watcher();
// 模拟触发数据变化
data.message = 'Hello, Vue.js!';
5、迭代器是什么
迭代器(Iterator)是 JavaScript 中的一个接口,它提供了一种顺序访问数据结构(如数组、Map、Set 等)元素的方法。迭代器对象可以通过 next()
方法依次访问数据结构中的每一个元素,直到数据结构的末尾,返回一个包含 value
和 done
属性的对象。
// 创建一个简单的数组
const myArray = ['apple', 'banana', 'cherry'];
// 获取迭代器对象
const iterator = myArray[Symbol.iterator]();
// 使用迭代器对象遍历数组元素
let result = iterator.next();
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
myArray
是一个包含三个元素的数组。- 通过
[Symbol.iterator]()
获取了该数组的迭代器对象iterator
。 - 使用
iterator.next()
方法来依次访问数组中的每个元素,直到done
属性为true
,表示迭代结束。
6、前端可以通过文件切片上传,断点传输
前端可以通过文件切片上传(File Chunking)的方式来实现大文件的上传,这有助于避免上传过程中遇到的一些问题,例如网络中断导致整个文件上传失败的情况。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Chunk Upload</title>
</head>
<body>
<input type="file" id="fileInput" />
<button onclick="uploadFile()">Upload</button>
<script>
function uploadFile() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const chunkSize = 1024 * 1024; // 1MB
const totalChunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
function uploadChunk() {
const start = currentChunk * chunkSize;
const end = Math.min((currentChunk + 1) * chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('fileChunk', chunk);
formData.append('totalChunks', totalChunks);
formData.append('currentChunk', currentChunk);
// 使用 XMLHttpRequest 或 Fetch API 发送 formData 到服务器
// ...
currentChunk++;
if (currentChunk < totalChunks) {
// 继续上传下一个块
uploadChunk();
} else {
// 所有块上传完成,服务器处理合并等操作
console.log('File upload complete!');
}
}
// 开始上传第一个块
uploadChunk();
}
</script>
</body>
</html>
6、vue的生命周期 data和method什么时候更新的,什么时候可以拿到vue实例
vue生命周期
在 created
钩子之后,你可以安全地访问和操作 data
和 methods
,而在 beforeCreate
钩子中,它们还未被初始化。如果需要在初始化阶段执行一些逻辑,可以使用 beforeCreate
钩子。如果需要在 DOM 元素渲染后进行操作,可以使用 mounted
钩子。
7、