每日的知识积累,包括 1 个 Ts 类型体操,两个 Leetcode 算法题,三个前端八股文题,四个英语表达积累。
1. 一个类型体操
Length of String
计算字符串的长度,类似于 String#length。
分析
这个仍然是递归,通过这道题目我想出来了将 String 转成 Tuple 的方法。
尝试写出
type LengthOfString<T extends string, R extends any[] = []> = {
0: R["length"];
1: T extends `${infer F}${infer L}`
? LengthOfString<L, [unknown, ...R]>
: LengthOfString<"", [unknown, ...R]>;
}[T extends "" ? 0 : 1];
测试用例
type C = LengthOfString<"1abc12312312">; // type C = 10
参考答案
type LengthOfString<
S extends string,
U extends any[] = []
> = S extends `${infer L}${infer R}`
? LengthOfString<R, [L, ...U]>
: U["length"];
经验总结
- 比起三元表达式
{}[]这种形式更加容易理解。 - 将 String 转成 Tuple 的方法:
type String2Tuple<T extends string, R extends string[] = []> = {
"0": [...R];
"1": T extends `${infer F}${infer L}` ? String2Tuple<L, [...R, F]> : never;
}[T extends `${infer F}${infer L}` ? "1" : "0"];
type C = String2Tuple<"1231231">; // type C = ["1", "2", "3", "1", "2", "3", "1"]
2. 两个 Leetcode 题目
刷题的顺序参考这篇文章 LeeCode 刷题顺序
2.1 [14] 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
尝试实现:
/**
* @param {string[]} strs
* @return {string}
*/
var longestCommonPrefix = function (strs) {
const n = strs.length;
let rst = "";
let idx = 0;
while (true) {
let _a = strs[0][idx];
for (let i = 0; i < n; i++) {
if (strs[i][idx]) {
if (strs[i][idx] !== _a) {
return rst;
}
} else {
return rst;
}
}
rst += _a;
idx++;
}
};
我的思路:
- 初始值为 "" 直接返回
- 使用 while 循环而不是 for 循环
得分结果: 35.40% 64.19%
总结提升:
- 时时刻刻记住防错和出口。
2.2 [434] 字符串中的单词数
统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例:
输入: "Hello, my name is John"
输出: 5
解释: 这里的单词是指连续的不是空格的字符,所以 "Hello," 算作 1 个单词。
尝试完成:
/**
* @param {string} s
* @return {number}
*/
var countSegments = function (s) {
let rst = 0;
s.split(" ").forEach((v) => {
if (v !== "") rst++;
});
return rst;
};
我的思路:
- 感觉没啥好说的,注意 js 语言本身的限制。
得分结果: 39.83% 36.44%
总结提升:
- 收集一些之前没有注意到的语法限制:
"".split(" "); // [''] length = 1
" ".split(" "); // ['', '', '', ''] length = 3
2.3 [58] 最后一个单词的长度
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = "Hello World"
输出:5
解释:最后一个单词是“World”,长度为 5。
示例 2:
输入:s = " fly me to the moon "
输出:4
解释:最后一个单词是“moon”,长度为 4。
示例 3:
输入:s = "luffy is still joyboy"
输出:6
解释:最后一个单词是长度为 6 的“joyboy”。
提示:
1 <= s.length <= 104
s 仅有英文字母和空格 ' ' 组成
s 中至少存在一个单词
尝试完成:
/**
* @param {string} s
* @return {number}
*/
var lengthOfLastWord = function (s) {
const _s = s.trim();
const _a = _s.split(" ");
const n = _a.length;
if (!n) return 0;
return _a[n - 1].length;
};
我的思路:
- 感觉没啥好说的,注意 js 语言本身的限制。
得分结果: 99.70% 80.90%
2.4 [344] 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
提示:
1 <= s.length <= 105
s[i] 都是 ASCII 码表中的可打印字符
尝试完成:
/**
* @param {character[]} s
* @return {void} Do not return anything, modify s in-place instead.
*/
var reverseString = function (s) {
let i = 0;
let j = s.length - 1;
while (j > i) {
[s[i], s[j]] = [s[j], s[i]];
j--;
i++;
}
return s;
};
我的思路:
[a,b]=[b,a]这种交换对于数组中的元素也是可以的!
得分结果: 13.33% 41.92%
3. 三个前端题目
- 延时加载 js 脚本的几种方法
- defer 属性:在 script 标签上写入 defer 标志,此脚本虽然会在文档解析的同时进行加载,但会等待文档解析完成之后才会执行。多个带 defer 标签的 script 标签都会在文档加载完毕之后按照在 dom 文档中的顺序从上到下依次执行,是有序的;
- async 属性:在 script 标签上写入 async 标志,此脚本会在文档解析的同时进行加载,并且一旦加载完成就立即开始执行,同时阻塞文档的解析。多个带有 async 标志的 script 标签,因为网络原因加载时间是不能保证的,所以它们之间的执行顺序是不能够得到保证的!
- 动态标签:在某个时机(通常是某个事件发生之后),动态的创建 script 标签并设置其 scr 的值,设置之后浏览器会自动发送 get 请求获取对应的资源;加载完成并执行完毕之后会触发 script 标签对象的 onload 事件;加载失败执行的是 onerror 事件;
- 异步执行:将一些脚本代码放到 setTimeout 的定时器中执行;
- 调整位置:调整 script 标签在 dom 中的位置也可以改变 script 的执行时间。
- 类数组以及其转成数组的方法
- 类数组的本质是一个 js 中的对象;此对象有length 属性和一些索引属性,所谓的索引属性指的就是 number 类型的 propertykey。但是如果使用
Array.isArray来检验会得到 false 结果; - 常见的类数组包括普通函数的
arguments对象,以及使用 querySelector 获取的NodeList对象; - 将类数组 x 变成真实数组的方法有:
- Array.from(x)
[...x]- 此外,可以借助 Array.prototype 上的各类方法;这种方式的原理就是:由于 x 不是数组,但是可以假装 x 是数组,这样 x 就可以使用数组上的特定方法,这些特定的方法执行结果是数组;那么如何假装 x 是真的数组呢?使用
Array.prototype.fnc就可以了,而数组构造函数原型对象上能够返回数组的方法有:- slice
- call
- concat
- forEach
于是:
Array.prototype.slice.call(x)Array.prototype.call.call(x,0)Array.prototype.concat.call(x,[])Array.prototype.forEach.call(x,v=>v)- ...
- arguments 的理解以及其遍历的方式
- 首先,arguments 是类数组,原因是它具有 length 属性(表示实参/实际收到的的数目)和索引属性(表示实参序列)。但是无法通过 Array.isArray 的检测,并且不具有大部分数组方法。
- 然后,arguments 对象只存在于普通对象中,而在箭头函数中不存在;
- 此外,arguments 对象上的 callee 属性表示正在运行的函数,可以用做匿名函数的递归;
- 最后,遍历类数组的方法大致可以分成两种,一个是转换为真正的数组,然后遍历,另外一个是嫁接数组上的方法进行遍历;
- 具体来说:
Array.prototype.forEach.call(likeArray, cb)。
4.四句英语积累
- be done -- I guess we [are done].
- lemme -- [Lemme] go and pray fast.
- dare -- You don't want or you don't [dare]?
- updated -- to keep myself [updated].