每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。
1. 一个类型体操
Pick
题目要求
实现 TS 内置的 Pick<T, K>,但不可以使用它。
从类型 T 中选择出属性 K,构造成一个新的类型。
例如:
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = MyPick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
分析
- 首先必须搞清楚如何遍历 Ts 中的 object 类型,只有这样才能在遍历的时候做手脚。
type LookObj<T extends {}> = {
[K in keyof T]: T[K];
};
type Obj = {
name: string;
age: number;
};
type C = LookObj<Obj>;
- 然后就是知道如何遍历联合类型。
// 由于 T extends {} 所以 keyof T 是联合类型,所以遍历联合类型使用 in 关键字
type LookObj<T extends {}> = {
[K in keyof T]: T[K];
};
- 最后是知道如何求两个联合类型的交集。
type Inter<T, P> = T extends P ? T : never;
type C = Inter<"a" | "b" | "c", "d" | "a">; // "a"
有了上面的基础信息,那么我们写 MyPick 的时候,就是先求出两个泛型的交集,然后遍历交集构造新的 object 类型即可。
尝试写出
type MyPick<T extends {}, K> = {
[P in K extends keyof T ? K : never]: T[P];
};
测试用例
type TodoPreview = MyPick<Todo, "title" | "completed">;
/**
* type TodoPreview = {
* title: string;
* completed: boolean;
* }
*/
参考答案
type MyPick<T extends {}, K extends keyof T> = { [P in K]: T[P] };
经验总结
虽然没有我考虑的完全,但是这里的 K extends keyof T 还是值得一学的。
2. 两个 Leetcode 题目
刷题的顺序参考这篇文章 LeeCode 刷题顺序
2.1 [283] 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
进阶:你能尽量减少完成的操作次数吗?
尝试实现:
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var moveZeroes = function (nums) {
let count = 0;
const n = nums.length;
if (n < 2) return nums;
for (let i = 0; i < n; i++) {
const num = nums[i];
nums[i - count] = num;
if (num === 0) {
count++;
}
}
for (let i = n - count; i < n; i++) {
nums[i] = 0;
}
return nums;
};
我的思路:
- 分为两步,第一步是遍历数组,对于每一个元素,假如前面有 count 个 0 则往前移动 count 位;同时,如果自己也是 0 则 count + 1
- 第二步,得到第一次遍历之后的数据之后,最后的 count 个元素都应该置为 0
得分结果: 19.75% 18.31%
2.2 [118] 杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:
输入: numRows = 1
输出: [[1]]
提示:
1 <= numRows <= 30
尝试完成:
/**
* @param {number} numRows
* @return {number[][]}
*/
var generate = function (numRows) {
const rst = [];
for (let i = 0; i < numRows; i++) {
if (i === 0) rst.push([1]);
if (i === 1) rst.push([1, 1]);
if (i === 2) rst.push([1, 2, 1]);
if (i > 2) {
const current = [];
for (let j = 0; j < i + 1; j++) {
if (j === 0 || j === i) {
current[j] = 1;
} else {
current[j] = rst[i - 1][j - 1] + rst[i - 1][j];
}
}
rst.push(current);
}
}
return rst;
};
我的思路:
- 先写边界条件下的返回值,边界条件一般也是初始条件。
- 观察到 numRows 为行数,并且每一行的元素个数等于行序号 + 1
得分结果: 9.14% 15.33%
总结提升:
- 序列号是从 0 开始的,但是自然数是从 1 开始的;这很难办,很容易出错,需要想一个好用的解决方案。
3. 三个前端题目
- 手写一个函数,实现简单浅拷贝。
这个题要想做的好,首先必须画好边界:
- 只拷贝对象类型,其他基本类型直接照原样输出;
- 简单的浅拷贝只需要区分数组和一般值对象,不要去管其他类型的对象;
- 只对目标对象上的 ownProperty 进行拷贝,而不要管原型链上的属性;
据此,实现的程序如下:
function shallowCopy(source) {
if (Object(source) !== source) return source;
const _tmp = Array.isArray(source) ? [] : {};
for (let key in source) {
if (source.hasOwnProperty(key)) _tmp[key] = source[key];
}
return _tmp;
}
补充一个知识点,如果 a 是"object"类型的,如何判断 a 的 b 属性的值是否是真的 undefined?
() => a.b === undefined && a.hasOwnProperty(b);
- 叙述 js 中其他类型的值到字符串类型的转换规则
将 x 转成 string 类型,可分为隐式转换和强制转换,所谓隐式转换就是指
""+x的结果,所谓强制转换指的就是String(x)的结果:
- undefined: "undefined"
- null: "null"
- string: 返回原值,注意 String("abc")不是包装类型,注意与 new String("abc")区别开
- number: "Infinity", "-Infinity", "NaN", 变成相同的字面量,但是数字太大会变成字符串类型的科学计数法的模样
- boolean: "true", "false"
- symbol: 隐式转换报错;强制转换如:
String(Symbol(9)) // "Symbol(9)" - bigint: 会变成将 n 去掉的相同的字面量,不会出现因为数字大变成字面量
- object: 内部机制
所谓内部机制,指的就是:
1). 判断 x 是否有 valueOf 方法,如果有的话根据其返回值做出不同的反映:
- 返回值为 symbol 类型,报错
- 返回值为引用类型, 跳到 2
- 返回值 y 为其它非引用类型,最终结果为:
String(y)2). 如果没有 valueOf 方法,或 valueOf 返回引用类型,则接着判断是否有 toString 方法,如果有的话根据其返回值做出不同的反映: - 返回值为 symbol 类型,报错
- 返回值为引用类型,
报错 - 返回值 y 为其它非引用类型的 y,最终结果为:
String(y)3). 如果没有 toString 方法,则返回Object.prototype.toString,call(x)
- 叙述 js 中其他类型的值到布尔值的转换规则
js 中其他类型的值向布尔类型转换就比较简单了;事实上,js 中常见的一共只有8种假值,也就是不能通过
if()判断的值:
-
- ""
-
- false
-
- +0
-
- -0
-
- NaN
-
- null
-
- undefined
-
- Boolean(false)
其余的值都能够通过if()的判断,也就是所谓的真值。下面是一些容易搞混的假值:
-
- ![]
-
- !({})
-
- !123n
-
- !Symbol(0)
-
- !Object(false)
4.四句英语积累
- deal with something
- I had to deal with a lot of [customer complaints] last week.
- Weve had some technical problems with the new websites, but [we're dealing with them].
- set up -- to organise or plan something such as a meeting or event
- Can you s[et up a meeting for Thursday afternoon] please?
- We've [set up a series of workshops] to train staff on how to use the new software.