每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。
1. 一个类型体操
类型体操题目集合 Tuple To Object
传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来。
例如:
const tuple = ["tesla", "model 3", "model X", "model Y"] as const;
type result = TupleToObject<typeof tuple>; // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
分析
元组类型的不好遍历,我们将其先转成联合类型然后使用 in 操作符进行遍历。
尝试写出
type TupleToObject<T extends any[]> = {
[P in T[number]]: P;
};
测试用例
type C = TupleToObject<["name"]>; // {name: 'name'}
参考答案
type TupleToObject<T extends (string | number)[]> = { [P in T[number]]: P };
经验总结
注意这里不能写成 <T extends unknown[]> 只能写成 <T extends any[]> 否则 T[number] 会报错。由于 object 的键必须是 number string symbol 所以写的好一点的话:
type TupleToObject<T extends PropertyKey[]> = {
[P in T[number]]: P;
};
2. 两个 Leetcode 题目
刷题的顺序参考这篇文章 LeeCode 刷题顺序
2.1 [419] 棋盘上的战舰
给你一个大小为 m x n 的矩阵 board 表示棋盘,其中,每个单元格可以是一艘战舰 'X' 或者是一个空位 '.' ,返回在棋盘 board 上放置的 舰队 的数量。
舰队 只能水平或者垂直放置在 board 上。换句话说,舰队只能按 1 x k(1 行,k 列)或 k x 1(k 行,1 列)的形状放置,其中 k 可以是任意大小。两个舰队之间至少有一个水平或垂直的空格分隔 (即没有相邻的舰队)。
输入:board = [["X",".",".","X"],[".",".",".","X"],[".",".",".","X"]]
输出:2
示例 2:
输入:board = [["."]]
输出:0
提示:
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] 是 '.' 或 'X'
进阶:你可以实现一次扫描算法,并只使用 O(1) 额外空间,并且不修改 board 的值来解决这个问题吗?
尝试实现:
/**
* @param {character[][]} board
* @return {number}
*/
var countBattleships = function (board) {
const row = board.length;
const col = board[0].length;
let count = 0;
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
if (
board[i][j - 1] !== "X" &&
(!board[i - 1] || (board[i - 1] && board[i - 1][j] !== "X")) &&
board[i][j] === "X"
) {
count++;
}
}
}
return count;
};
我的思路: 思路是看解析来的,遍历每个元素只要上方或者左边元素存在并且不等于 “X” 然后这个元素本身就是 “X” 则说明这个元素可以作为战舰的 “头” 有多少战舰头就有多少艘战舰。
得分结果: 5.36% 24.20%
总结提升:
- 首先,题目错误太多,导致根本看不懂;看不懂的时候看英文原题。
- 其次,将问题转化成更加简单的等价信息非常重要,其实这个题并不难。
2.2 [661] 图片平滑器
图像平滑器 是大小为 3 x 3 的过滤器,用于对图像的每个单元格平滑处理,平滑处理后单元格的值为该单元格的平均灰度。
每个单元格的 平均灰度 定义为:该单元格自身及其周围的 8 个单元格的平均值,结果需向下取整。(即,需要计算蓝色平滑器中 9 个单元格的平均值)。
如果一个单元格周围存在单元格缺失的情况,则计算平均灰度时不考虑缺失的单元格(即,需要计算红色平滑器中 4 个单元格的平均值)。
给你一个表示图像灰度的 m x n 整数矩阵 img ,返回对图像的每个单元格平滑处理后的图像 。
示例 1
输入:img = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[0, 0, 0],[0, 0, 0], [0, 0, 0]]
解释:
对于点 (0,0), (0,2), (2,0), (2,2): 平均(3/4) = 平均(0.75) = 0
对于点 (0,1), (1,0), (1,2), (2,1): 平均(5/6) = 平均(0.83333333) = 0
对于点 (1,1): 平均(8/9) = 平均(0.88888889) = 0
示例 2
输入: img = [[100,200,100],[200,50,200],[100,200,100]]
输出: [[137,141,137],[141,138,141],[137,141,137]]
解释:
对于点 (0,0), (0,2), (2,0), (2,2): floor((100+200+200+50)/4) = floor(137.5) = 137
对于点 (0,1), (1,0), (1,2), (2,1): floor((200+200+50+200+100+100)/6) = floor(141.666667) = 141
对于点 (1,1): floor((50+200+200+200+200+100+100+100+100)/9) = floor(138.888889) = 138
提示:
m == img.length
n == img[i].length
1 <= m, n <= 200
0 <= img[i][j] <= 255
尝试完成:
/**
* @param {number[][]} img
* @return {number[][]}
*/
var imageSmoother = function (img) {
const row = img.length;
const img2 = [];
for (let i = 0; i < row; i++) {
const col = img[i].length;
for (let j = 0; j < col; j++) {
let count = 1;
let sum = img[i][j];
// 右
if (img[i] && typeof img[i][j + 1] !== "undefined") {
sum += img[i][j + 1];
count++;
}
// 左
if (img[i] && typeof img[i][j - 1] !== "undefined") {
sum += img[i][j - 1];
count++;
}
// 下
if (img[i + 1] && typeof img[i + 1][j] !== "undefined") {
sum += img[i + 1][j];
count++;
}
// 上
if (img[i - 1] && typeof img[i - 1][j] !== "undefined") {
sum += img[i - 1][j];
count++;
}
// 上左
if (img[i - 1] && typeof img[i - 1][j - 1] !== "undefined") {
sum += img[i - 1][j - 1];
count++;
}
// 上右
if (img[i - 1] && typeof img[i - 1][j + 1] !== "undefined") {
sum += img[i - 1][j + 1];
count++;
}
// 下左
if (img[i + 1] && typeof img[i + 1][j - 1] !== "undefined") {
sum += img[i + 1][j - 1];
count++;
}
// 下右
if (img[i + 1] && typeof img[i + 1][j + 1] !== "undefined") {
sum += img[i + 1][j + 1];
count++;
}
if (img2[i]) {
img2[i][j] = Math.floor(sum / count);
} else {
img2[i] = [];
img2[i][j] = Math.floor(sum / count);
}
}
}
return img2;
};
**我的思路:**这不需要什么高深的思路,但是下笔的时候需要考虑很多语法层面的东西,多维数组的可访问性很容易出错。
得分结果: 19.70% 13.63%
3. 三个前端题目
- 手写一个函数,实现 Function.prototype.call 功能。
- 手写 call apply bind 的核心在于 js 中的一个机制:
- 如果
typeof a.b === "function";并且 a.b 是一个普通函数,则在执行a.b()的时候,函数体中的 this 会指向 a; - 不论是手写这三个中的哪一个,依据的核心都是上面的这个机制,只是使用的方式略微有些不同罢了!
此外,如果使用低版本的 js 写这三个函数,可能会碰到两个难题:
- 如何获取剩余参数
- 如何产生一个独一无二的属性名
- 本文使用 ES6 语法书写了这三个方法,同时将上面两个难点的解决方法放在了最后。
function myCall(context, ...args: any) {
// 检验
if (typeof this !== "function")
throw new Error("myCall must be called by a function");
// 使用a.b机制改变this
const _context = context || window;
const _prop = Symbol("b");
_context[_prop] = this;
// 执行
const result = _context[_prop](...args);
// 删除
delete _context[_prop];
// 返回
return result;
}
// Function.prototype.myCall = myCall
- 手写一个函数,实现 Function.prototype.apply 功能。 和 myCall 唯一区别是参数的类型是不是以数组的形式传递进来的.
function myApply(context, args: any[]) {
// 检验
if (typeof this !== "function")
throw new Error("myApply must be called by a function");
// 使用a.b机制改变this
const _context = context || window;
const _prop = Symbol("b");
_context[_prop] = this;
// 执行
const result = args ? _context[_prop](...args) : _context[_prop]();
// 删除
delete _context[_prop];
// 返回
return result;
}
// Function.prototype.myApply = myApply
**等价操作:**下面两个操作是等价的
ES6:
myFunction(...myArray);ES5:myFunction.apply(null, myArray);
- 手写一个函数,实现 Function.prototype.bind 功能。
- 区别在于:会形成一个闭包,返回的是一个函数
- 执行和删除的时机也不同
- 带一点柯里化的味道
function myBind(context, ...args: any) {
// 检验
if (typeof this !== "function")
throw new Error("myBind must be called by a function");
// 使用a.b机制改变this
const _context = context || window;
const _prop = Symbol("b");
_context[_prop] = this;
return function (...rest: any) {
// 合并参数
const parm = [...args, ...rest];
// 执行
const result = _context[_prop](...parm);
// 删除
delete _context[_prop];
// 返回
return result;
};
}
// Function.prototype.myBind = myBind
4.四句英语积累
- run out of something -- nothing left
- The printer [has run out of ink]. Can you change the cartridge please?
- We only have a couple of weeks to finish the project - we['re running out of time]!
- figure something out -- to finally understand or find the solution after a lot of thought
- It took me a few days to [figure out how to use] the new software.
- [He's checked the machine 3 times. but he still can't figure out why it's not working].