每日知识积累 Day 10

156 阅读7分钟

每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。

1. 一个类型体操

类型体操题目集合 Diff

获取两个接口类型中的差值属性。

例如:

type Foo = {
  a: string;
  b: number;
};
type Bar = {
  a: string;
  c: boolean;
};

type Result1 = Diff<Foo, Bar>; // { b: number, c: boolean }
type Result2 = Diff<Bar, Foo>; // { b: number, c: boolean }

分析

最终的结果由三个部分组成:T 中有但是 U 中没有;U 中有但是 T 中没有;或者它们都有但是值不相同的。然后使用 & 链接起来即可。

尝试写出

type Diff<U extends {}, T extends {}> = {
  [P in keyof T as P extends keyof U ? never : P]: T[P];
} & {
  [P2 in keyof U as P2 extends keyof T ? never : P2]: U[P2];
} & {
  [P3 in keyof U as P3 extends keyof T
    ? U[P3] extends T[P3]
      ? never
      : P3
    : never]: U[P3];
};

简写方式

可以包一层,把这个写的更漂亮一些。

type DiffX<U, Y> = {
  [P in keyof Diff<U, Y>]: Diff<U, Y>[P];
};

测试用例

type Result1 = {
  [P in keyof Diff<Foo, Bar>]: Diff<Foo, Bar>[P];
}; // { b: number, c: boolean }
type Result2 = Diff<Bar, Foo>; // { b: number, c: boolean }

参考答案

type MyExclude<T, K> = T extends K ? never : T;
type Diff<O extends {}, O1 extends {}> = {
  [P in MyExclude<keyof O, keyof O1> | MyExclude<keyof O1, keyof O>]: (O &
    O1)[P];
};

经验总结

  1. 第一个经验:[P in MyExclude<keyof O, keyof O1> | MyExclude<keyof O1, keyof O>]
  2. 第二个经验:(O & O1)[P]

2. 两个 Leetcode 题目

刷题的顺序参考这篇文章 LeeCode 刷题顺序

2.1 [54] 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。


示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:


输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100

尝试实现:

/**
 * @param {number[][]} matrix
 * @return {number[]}
 */
var spiralOrder = function (matrix) {
  let dir = 1; // 1 2 3 4
  const m = matrix.length;
  const n = matrix[0].length;
  const mn = m * n;
  const rst = [];

  let i = 0;
  let j = -1;

  for (let k = 0; k < mn; k++) {
    // 尝试向右
    if (dir === 1) {
      if (matrix[i] && typeof matrix[i][j + 1] !== "undefined") {
        j++;
        rst.push(matrix[i][j]);
        matrix[i][j] = undefined;
        continue;
      } else {
        dir = 2;
      }
    }

    // 尝试向下
    if (dir === 2) {
      if (matrix[i + 1] && typeof matrix[i + 1][j] !== "undefined") {
        i++;
        rst.push(matrix[i][j]);
        matrix[i][j] = undefined;
        continue;
      } else {
        dir = 3;
      }
    }

    if (dir === 3) {
      if (matrix[i] && typeof matrix[i][j - 1] !== "undefined") {
        j--;
        rst.push(matrix[i][j]);
        matrix[i][j] = undefined;
        continue;
      } else {
        dir = 4;
      }
    }

    if (dir === 4) {
      if (matrix[i - 1] && typeof matrix[i - 1][j] !== "undefined") {
        i--;
        rst.push(matrix[i][j]);
        matrix[i][j] = undefined;
        continue;
      } else {
        dir = 1;
      }
    }

    if (dir === 1) {
      if (matrix[i] && typeof matrix[i][j + 1] !== "undefined") {
        j++;
        rst.push(matrix[i][j]);
        matrix[i][j] = undefined;
        continue;
      } else {
        return rst;
      }
    }
  }
  return rst;
};

我的思路:

  1. 首先走过的路会变成墙,意思就是不可行,每一次撞墙就切换方向。
  2. 顺时针螺旋的意思就是方向为:右 下 左 上 右 下 左 上 右 下 左 上 ...
  3. 由于矩阵的元素个数是有限的,所以我们的循环是有出口的

得分结果: 40.29% 17.78

总结提升:

  1. 什么是时候需要返回,以免计算重复,这是很重要的问题。

2.2 [59] 螺旋矩阵 Ⅱ

给你一个正整数 n ,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。


示例 1:


输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:

输入:n = 1
输出:[[1]]


提示:

1 <= n <= 20

尝试完成:

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function (n) {
  const matrix = Array(n)
    .fill(0)
    .map((v) => Array(n).fill(0));

  let i = 0;
  let j = -1;
  let dir = 1; // 1 2 3 4 右 下 左 上

  for (let k = 0; k < n * n; k++) {
    const num = k + 1;
    if (dir === 1) {
      if (
        matrix[i] &&
        typeof matrix[i][j + 1] !== "undefined" &&
        matrix[i][j + 1] === 0
      ) {
        j++;
        matrix[i][j] = num;

        continue;
      } else {
        dir = 2;
      }
    }
    if (dir === 2) {
      if (
        matrix[i + 1] &&
        typeof matrix[i + 1][j] !== "undefined" &&
        matrix[i + 1][j] === 0
      ) {
        i++;
        matrix[i][j] = num;

        continue;
      } else {
        dir = 3;
      }
    }
    if (dir === 3) {
      if (
        matrix[i] &&
        typeof matrix[i][j - 1] !== "undefined" &&
        matrix[i][j - 1] === 0
      ) {
        j--;
        matrix[i][j] = num;

        continue;
      } else {
        dir = 4;
      }
    }
    if (dir === 4) {
      if (
        matrix[i - 1] &&
        typeof matrix[i - 1][j] !== "undefined" &&
        matrix[i - 1][j] === 0
      ) {
        i--;
        matrix[i][j] = num;

        continue;
      } else {
        dir = 1;
      }
    }
    if (dir === 1) {
      if (
        matrix[i] &&
        typeof matrix[i][j + 1] !== "undefined" &&
        matrix[i][j + 1] === 0
      ) {
        j++;
        matrix[i][j] = num;

        continue;
      } else {
        dir = 2;
      }
    }
  }
  return matrix;
};

我的思路:

  1. 首先初始化矩阵所有元素值都等于 0,与矩阵外的索引 undefined 拉开差距
  2. 然后循环 n^2 次,每次确定正确的位置,填上序列号 + 1
  3. 方向仍然为 右 下 左 上的循环顺序

得分结果: 99.36% 29.77%

总结提升:

  1. 在 js 中初始化一个全 0 矩阵的方式为:const matrix = Array(n).fill(0).map(v => Array(n).fill(0)) 其它方法很容易出错。
  2. 老生常谈的问题了 if(0)

3. 三个前端题目

  1. 叙述 + 操作符的计算规则
  • bigint 不能出现在一般的+运算中!
  • 所有值作为+的操作数的时候都会先转成 number 或者 string 类型之后再使用转换值计算
  • symbol 不能向 number 或者 string 隐式转换,所以 symbol 也不能参与+运算
  • 如果操作数之一为 string 类型的,或者转化类型之后为 string 的,则应该按照【与字符串做加法隐式转换规则】计算
  • 如果操作数之一为 number 类型的,并且另一个操作数不为 string 或者转化类型之后的 string,则应该按照【与数字做加法隐式转换规则】计算
  • 如果两个操作数均不为 number 或者 string,或者转换之后的类型军部为 number 或者 string,则只有可能是 null undefined boolean,它们都是先转成 number 类型的再计算
  • 引用类型转成 number 或者 string 的方式为,依次检查: valueOf -> toString -> Object.prototype.toString.call(x)的返回值。
  1. bigint 类型的作用 由于 js 遵循的是 IEEE 双精度浮点数标准,也就是使用 64bit 的二进制数去表示一个浮点数,其中 1bit 为符号位,11bit 为指数位,剩下的 52 位为尾数。

这就造成了 52bit 最大只能表示绝对值为2^53 - 1的数字,如果将符号位算进来,就是-(2^53 - 1) -> +(2^53 - 1),也就是-2^53+1 -> 2^53-1

这恰好对应了Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER的值。

也就是说超过这个范围,js 就只能用浮点数代替了,就会产生舍入误差了。所以ECMAScript 2020才引入bigint这个新的数据类型来表示这个范围之外的整数。

bigint 的原理就是使用非固定长度的二进制数表示整数,因为其长度是不固定的,所以 bigint 的数据不能参与到和 number 类型的数据的运算过程中去。 3. 对比扩展运算符和 Object.assign

  • 相同点:
    • 两者都是浅拷贝
    • 在实现浅拷贝功能的时候,两者都是后面的值覆盖前面的值
    • 都只会复制 ownProperty,包括 ownPropertyName 和 ownPropertySymbol,不涉及原型链上的属性
  • 不同点:
    • 浅拷贝只是扩展运算符的众多用途的一种
    • 扩展运算符产生一个新的对象,而 Object.assign 是往目标对象上合并属性(不会产生新的对象)
  • 此外:{...obj1, ...obj2}相当于 Object.assign({}, obj1, obj2)

4. 6 个常见邮件错误

  1. I would like to remember everyone that registration for the conference ends on Thursday this week.
  • remember -> remind (remind means to let someone else to remember)
  • Could you please remind Mr Singh about the product catalogues?
  1. We can assure you that you will receive the goods util 2 pm on Firday.
  • util -> by (util tells how long the situation continues, but by means the time something happens before that)
  • We need to have the report finished by 12 pm on Monday.
  1. I'm afraid but Mr Johnson will not be able to attend the meeting tomorrow.
  • but -> X (I'm afraid + subject/that)
  • I'm afraid we won't have time to discuss that topic tomorrow.
  1. In out next meeting, we will discuss about the new marketing strategy.
  • about -> X (not talk about, never use about after discuss) (have a discussion on/about something)
  • I think we should discuss these problems in our weekly meeting.
  1. Would you mind to send me the results of the survey again? I think I deleted the original email by mistake.
  • send to -> sending (mind doing something)
  • Would you mind confirming the details once again?
  1. I would like to inform that we have accepted your proposal, and would you like to arrange another meeting to discuss the next steps.
  • inform -> inform you (inform sb sth)
  • We would like to inform you that this year's conference will take place in Chennai.