JS 戏精面试题,你都见过吗?
2024-02-19 13:22:02
前言
在 JS 面试中,经常会看到一些简单而又沙雕的题目,这些题目包含一些陷阱,但这些在我们规范的编码下或者业务中基本不会出现。有些面试官就是这样,不专注于制定代码的标准和规范上,却用不规范的代码去检验别人是否细心。这魔幻的世界就是一个攀比的世界,你在意什么,它就爱考什么。不过,只要我们具备扎实的基础知识,这些题目也就迎刃而解了。
戏精题目一:比较两个值是否相等
const a = '1';
const b = 1;
console.log(a == b); // true
console.log(a === b); // false
陷阱在于 ==
和 ===
的区别。==
仅比较值是否相等,而 ===
比较值和类型是否都相等。因此,a == b
为 true
,而 a === b
为 false
。
戏精题目二:数组去重
const arr = [1, 2, 3, 1, 2, 3];
const newArr = [...new Set(arr)];
console.log(newArr); // [1, 2, 3]
陷阱在于 Set
对象的特性。Set
对象会自动去除重复元素,因此 new Set(arr)
会生成一个只包含唯一元素的新数组。而 ...
运算符可以将数组展开为一组参数,因此 [...new Set(arr)]
会生成一个新的数组,其中包含 Set
对象中所有的元素。
戏精题目三:函数柯里化
const add = (a, b) => a + b;
const add10 = add.bind(null, 10);
console.log(add10(20)); // 30
陷阱在于 bind()
方法的用法。bind()
方法可以将函数的第一个参数固定,返回一个新的函数。因此,add10
函数实际上是 add
函数的柯里化版本,第一个参数固定为 10。
戏精题目四:对象深拷贝
const obj = {
name: 'John',
age: 30,
address: {
street: '123 Main Street',
city: 'New York',
state: 'NY'
}
};
const newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// {
// name: 'John',
// age: 30,
// address: {
// street: '123 Main Street',
// city: 'New York',
// state: 'NY'
// }
// }
陷阱在于 JSON.parse()
和 JSON.stringify()
方法的使用。这两个方法可以将对象转换为 JSON 字符串,然后又可以将 JSON 字符串转换为对象。这样就可以实现对象的深拷贝,因为 JSON 字符串中包含了对象的所有属性和值。
戏精题目五:发布-订阅模式
class PubSub {
constructor() {
this.subscribers = {};
}
subscribe(event, callback) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(callback);
}
publish(event, data) {
if (this.subscribers[event]) {
this.subscribers[event].forEach(callback => callback(data));
}
}
}
const pubSub = new PubSub();
pubSub.subscribe('message', (data) => {
console.log(`Received message: ${data}`);
});
pubSub.publish('message', 'Hello world!'); // Received message: Hello world!
陷阱在于发布-订阅模式的实现。发布-订阅模式是一种设计模式,允许对象之间进行通信,而无需它们直接相互引用。在这个例子中,PubSub
类实现了发布-订阅模式,允许对象订阅事件并接收发布到该事件的数据。
戏精题目六:防抖和节流
const debounce = (func, delay) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
func(...args);
}, delay);
};
};
const throttle = (func, delay) => {
let lastTime = 0;
return (...args) => {
const now = Date.now();
if (now - lastTime >= delay) {
func(...args);
lastTime = now;
}
};
};
const handleClick = (e) => {
console.log('Button clicked');
};
const debouncedHandleClick = debounce(handleClick, 500);
const throttledHandleClick = throttle(handleClick, 500);
document.querySelector('button').addEventListener('click', debouncedHandleClick);
document.querySelector('button').addEventListener('click', throttledHandleClick);
陷阱在于防抖和节流的实现。防抖和节流都是函数装饰器,可以控制函数的执行频率。防抖会延迟函数的执行,直到函数停止被调用一段时间后才执行。节流会限制函数在一定时间内只能执行一次。在这个例子中,debounce()
函数实现了防抖,而 throttle()
函数实现了节流。
戏精题目七:实现一个链表
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
}
add(data) {
const node = new Node(data);
if (!this.head) {
this.head = node;
this.tail = node;
} else {
this.tail.next = node;
this.tail = node;
}
}
remove(data) {
if (!this.head) {
return;
}
if (this.head.data === data) {
this.head = this.head.next;
if (!this.head) {
this.tail = null;
}
return;
}
let current = this.head;
let previous = null;
while (current) {
if (current.data === data) {
previous.next = current.next;
if (!current.next) {
this.tail = previous;
}
return;
}
previous = current;
current = current.next;
}
}
print() {
let current = this.head;
while (current) {
console.log(current.data);
current = current.next;
}
}
}
const linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);
linkedList.print(); // 1 2 3 4 5
linkedList.remove(3);
linkedList.print(); // 1 2 4 5
陷阱在于链表的实现。链表是一种数据结构,它由一组节点组成,每个节点都包含一个数据值和一个指向下一个节点的指针。在这个例子中,Node
类表示链表中的节点,而 LinkedList
类表示链表本身。add()
方法用于向链表中添加一个新的节点,remove()
方法用于从链表中删除一个节点,print()
方法用于打印链表中的所有节点。
结语
以上 7 个 JS 面试题只是冰山一角,还有很多类似的题目可能出现在面试中。这些题目看似简单,但往往包含一些陷阱,旨在检验面试者的细心程度和对 JS 语言的掌握程度。因此,在准备 JS 面试时,除了掌握基础知识外,还需要对这些戏精题目有所了解,这样才能在面试中游刃有余。