2023/5/30面试小结

一、本应会但并不会的题

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)是指从根节点开始,沿着一条路径尽可能向下访问每个节点,直到无法继续为止,然后返回上一个节点继续访问。具体来说深度优先遍历分为三种:

  1. 前序遍历(Preorder Traversal):根节点->左子树->右子树
  2. 中序遍历(Inorder Traversal):左子树->根节点->右子树
  3. 后序遍历(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]

2023/5/30面试小结_第1张图片

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()
检查数组中每项是否都通过回调函数的测试。

我们可以调用这些静态方法以便更好地操作数组的数据和结构。

7.rem、px、rpx和em

rem是一种相对于根元素(html元素)字体大小的单位。使用rem单位来设置页面元素的尺寸和间距,可以实现响应式布局。例如,如果根元素的字体大小为16像素,则1rem等于16像素,2rem等于32像素,依此类推。

px是一种绝对长度单位,表示像素大小。它的值表示固定的像素大小,不会随屏幕尺寸、分辨率等因素变化以实现精确的页面设计。

rpx是一种相对于屏幕宽度的单位,它在微信小程序中使用。使用rpx单位来设置元素的尺寸和间距能够使页面在不同设备的屏幕上保持一致的显示效果,从而实现响应式布局。1rpx表示屏幕宽度的1/750,因此,在不同宽度的屏幕中,1rpx的实际像素大小会随之改变。

em 是一种相对长度单位,它的值相对于当前元素的字体大小来计算。em 的一般用法是将其应用到父元素的字体大小上,子元素便可以根据父元素的字体大小来计算自身的尺寸。例如,对于一个具有 font-size: 16px; 的父元素,一个 font-size: 1.2em; 的子元素将会以 16px * 1.2 的大小进行渲染。

二、总结

1.408基础不扎实,多回顾,多记忆,多思考

2.编程能力薄弱,多刷算法题

3.前端三小件基础不扎实,多加练习

4.语言组织能力弱,很多知识点不能表述清晰,多尝试口述知识点。

你可能感兴趣的:(面试,面试,javascript,职场和发展,前端,开发语言)