一、本应会但并不会的题
1.用js写一个栈
class Stack {
constructor() {
this.items = [];
}
// 添加元素到栈顶
push(element) {
this.items.push(element);
}
// 移除栈顶元素并返回
pop() {
return this.items.pop();
}
// 返回栈顶元素
peak() {
return this.items[this.items.length - 1];
}
// 判断栈是否为空
isEmpty() {
return this.items.length === 0;
}
// 返回栈中元素个数
size() {
return this.items.length;
}
}
const stack = new Stack();
stack.push(1);
stack.push(2);
console.log(stack); // Stack { items: [1, 2]}
console.log(stack.peak()); // 2
console.log(stack.pop()); // 2
console.log(stack.isEmpty()); // false
console.log(stack.size()); // 1
2.二叉树的深度优先遍历的三种实现
二叉树的深度优先遍历(Depth-First Search,DFS)是指从根节点开始,沿着一条路径尽可能向下访问每个节点,直到无法继续为止,然后返回上一个节点继续访问。具体来说深度优先遍历分为三种:
- 前序遍历(Preorder Traversal):根节点->左子树->右子树
- 中序遍历(Inorder Traversal):左子树->根节点->右子树
- 后序遍历(Postorder Traversal):左子树->右子树->根节点
下面是一个使用递归实现二叉树前序、中序和后序遍历的示例代码:
// 树节点
class TreeNode {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
// 二叉树
class BinaryTree {
constructor() {
this.root = null;
}
// 向树中插入新节点
insert(value) {
let newNode = new TreeNode(value);
// 如果根节点不存在则将新节点设置为根节点,否则递归插入节点
if (!this.root) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
// 递归地插入节点 左中右
insertNode(node, newNode) {
if (newNode.value < node.value) {
// 新节点在左侧的情况
if (!node.left) {
// 左侧没节点直接插入
node.left = newNode;
} else {
// 左侧有节点递归插入
this.insertNode(node.left, newNode);
}
} else {
// 新节点在右侧的情况
if (!node.right) {
// 右侧没节点直接插入
node.right = newNode;
} else {
// 右侧有节点递归插入
this.insertNode(node.right, newNode);
}
}
}
// 先序遍历 根->左->右 要传入一个节点参数
preOrderTraversal(root, result = []) {
if (root) {
// 访问根节点
result.push(root.value);
// 再访问左子树
this.preOrderTraversal(root.left, result);
// 再访问右子树
this.preOrderTraversal(root.right, result);
}
return result;
}
// 中序遍历 左->根->右
inOrderTraversal(root, result = []) {
if (root) {
// 访问左子树
this.inOrderTraversal(root.left, result);
// 访问根节点
result.push(root.value);
// 访问右子树
this.inOrderTraversal(root.right, result);
}
return result;
}
// 后序遍历 左->右->根
postOrderTraversal(root, result = []) {
if (root) {
this.postOrderTraversal(root.left, result);
this.postOrderTraversal(root.right, result);
result.push(root.value);
}
return result;
}
}
// 测试
const binaryTree = new BinaryTree();
binaryTree.insert(4);
binaryTree.insert(2);
binaryTree.insert(7);
binaryTree.insert(1);
binaryTree.insert(3);
binaryTree.insert(10);
binaryTree.insert(8);
binaryTree.insert(5);
console.log(binaryTree.preOrderTraversal(binaryTree.root)); // [4, 2, 1, 3, 7, 5, 10, 8]
console.log(binaryTree.inOrderTraversal(binaryTree.root)); // [1, 2, 3, 4, 5, 7, 8, 10]
console.log(binaryTree.postOrderTraversal(binaryTree.root)); // [1, 3, 2, 5, 8, 10, 7, 4]
3.Vue2到Vue3实现响应式为什么要从Object.defineProperty()转为使用Proxy代理
在Vue 2中,对象响应式是通过Object.defineProperty()来实现的。但是这种方式存在一些局限性,比如无法监测到新增属性和删除属性。另外,由于Object.defineProperty()只能对属性进行劫持,所以需要遍历对象的每个属性进行劫持,性能不够优秀。
Vue 3中引入了Proxy代理,它能够监测到更多类型的变化,包括新增属性和删除属性。同时,它可以一次性监听整个对象,而不需要遍历每个属性,因此在性能方面比Object.defineProperty()更加优秀。
除此之外,使用Proxy代理还可以实现更为复杂的监听操作,比如监听数组的变化、深度监听等。因此,在Vue 3中采用Proxy代理来实现响应式更加灵活和高效。
4.实现{name:‘xiaoming’,id:0, age:18}->‘name=xiaoming&id=0&age=18’
const obj = {
name: "xiao5",
id: 0,
age: 18,
};
function objToQueryString(obj) {
return Object.keys(obj)
.map((key) => key + "=" + obj[key])
.join("&");
}
const result = objToQueryString(obj);
console.log(result); // name=xiao5&id=0&age=18
5.如何实现一个抽象类,抽象类和普通类有什么不同
在ES6中可以通过class关键字和constructor方法来实现一个类,但是ES6并没有提供直接创建抽象类的关键字。不过,我们可以通过在类中定义抽象方法来模拟实现一个抽象类。
一个抽象方法就是只有方法签名,而没有具体实现的方法。在ES6中,我们可以通过抛出错误的方式来提示子类必须实现该抽象方法。
以下是实现抽象类的示例代码:
// 定义一个抽象类Animal
class Animal {
constructor(name) {
this.name = name;
}
// 声明一个抽象方法speak,该方法必须由子类继承并且实现
speak() {
throw new Error('This method must be implemented by the subclass.');
}
}
// 继承自Animal的子类
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
// 实现抽象方法speak
speak() {
console.log('Woof!');
}
}
const myDog = new Dog('Buddy', 'Labrador');
myDog.speak(); // 'Woof!'
普通类和抽象类的主要区别在于,抽象类不能被实例化,它只能作为其他类的基类(父类)来使用。抽象类强制要求子类必须实现某些方法或属性,而普通类则没有这种要求,子类可以选择性地覆盖父类的方法和属性。
另外,抽象类中可以定义抽象方法,而普通类中只能定义具体方法。抽象方法没有具体的实现,必须在子类中覆盖实现;而具体方法有实现,在子类中可以继承或者重写。
6.Array的静态方法有哪些
Array中的静态方法是指挂载在Array构造函数上而不是Array.prototype上的方法。常见的Array静态方法有以下几个:
Array.isArray()
判断一个值是否为数组,返回布尔值。
Array.from()
从类似数组或可迭代对象创建一个新的数组实例。可以接受第二个参数来对每个元素执行一个map函数。
Array.of()
创建一个包含任意数量的参数的新数组实例。这个方法用起来更加方便,不需要使用数组字面量或者apply函数了。
Array.prototype.concat()
合并两个或多个数组,返回一个新的数组实例。
Array.prototype.forEarch()
遍历数组的所有项,并向回调函数传递每一项和该项在数组中的索引。
Array.prototype.map()
创建一个新的数组,其中每个元素都是原始数组的处理结果。
Array.prototype.filter()
创建一个新的数组,其中只包含原数组中经过过滤器回调函数处理后返回true值的元素。
Array.prototype.reduce()
将数组中的所有项累加到一个单独的数值中。
Array.prototype.some()
检查数组里是否有至少一个项通过回调函数的测试。
Array.prototype.every()
检查数组中每项是否都通过回调函数的测试。
我们可以调用这些静态方法以便更好地操作数组的数据和结构。
二、总结
1.408基础不扎实,多回顾,多记忆,多思考
2.编程能力薄弱,多刷算法题
3.前端三小件基础不扎实,多加练习
4.语言组织能力弱,很多知识点不能表述清晰,多尝试口述知识点。