0%

题目

你能在你最喜欢的那天吃到你最喜欢的糖果吗?

题解

官方题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var canEat = function (candiesCount, queries) {
const n = candiesCount.length;

// 前缀和
const sum = new Array(n).fill(0);
sum[0] = candiesCount[0];
for (let i = 1; i < n; ++i) {
sum[i] = sum[i - 1] + candiesCount[i];
}

const q = queries.length;
const ans = new Array(q).fill(0);
for (let i = 0; i < q; ++i) {
const query = queries[i];
const favoriteType = query[0],
favoriteDay = query[1],
dailyCap = query[2];

const x1 = favoriteDay + 1;
const y1 = (favoriteDay + 1) * dailyCap;
const x2 = favoriteType == 0 ? 1 : sum[favoriteType - 1] + 1;
const y2 = sum[favoriteType];

ans[i] = !(x1 > y2 || y1 < x2);
}
return ans;
};

let candiesCount = [7, 4, 5, 3, 8],
queries = [
[0, 2, 2],
[4, 2, 4],
[2, 13, 1000000000],
];

console.log('canEat :>> ', canEat(candiesCount, queries));

收获

JS 的技巧

1
const ans = new Array(q).fill(0);

输出长度为 q 的数组,用 0 填充

了解了前缀和这个东西

[7, 4, 5, 3, 8]

用前缀和得到

[7,7+4 7+4+5,7+4+5+3,7+4+5+3+8]

然后拿到区间进行判断。

题目

连续的子数组和

题解

官方题解

如果事先计算出数组 nums 的前缀和数组,则对于任意一个子数组,都可以在 O(1) 的时间内得到其元素和。用 prefixSums[i] 表示数组 nums 从下标 00 到下标 i 的前缀和,则 nums 从下标 p+1 到下标 q(其中 p<q)的子数组的长度为 q−p,该子数组的元素和为 prefixSums[q]−prefixSums[p]

如果 prefixSums[q]−prefixSums[p] 为 k 的倍数,且 q−p≥2,则上述子数组即满足大小至少为 2 且元素和为 k 的倍数。

prefixSums[q]−prefixSums[p] 为 k 的倍数时,prefixSums[p]prefixSums[q] 除以 k 的余数相同。因此只需要计算每个下标对应的前缀和除以 k 的余数即可,使用哈希表存储每个余数第一次出现的下标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var checkSubarraySum = function (nums, k) {
const m = nums.length;
if (m < 2) {
return false;
}
const map = new Map();
map.set(0, -1);
let remainder = 0;
for (let i = 0; i < m; i++) {
remainder = (remainder + nums[i]) % k;
if (map.has(remainder)) {
const prevIndex = map.get(remainder);
// 2 - 0 >= 2
if (i - prevIndex >= 2) {
return true;
}
} else {
map.set(remainder, i);
// 5 0
// 1 2
}
}
return false;
};

let nums = [23, 2, 4, 6, 7],
k = 6;

console.log(checkSubarraySum(nums, k));

// 0 23%6
// 5
// 1 7%6
// 1
// 2 5%6
// 5
// 11%6
// 5
// 12%6
// 0

收获

我看到解题方法(上面的加粗划重点)我已经麻了,这是数学规律吗?

prefixSums[q]−prefixSums[p] 为 k 的倍数时,prefixSums[p]prefixSums[q] 除以 k 的余数相同。因此只需要计算每个下标对应的前缀和除以 k 的余数即可,使用哈希表存储每个余数第一次出现的下标。

数组为 [23, 2, 4, 6, 7] k6

prefixSums[q]−prefixSums[p] 为 k 的倍数

1
((23 + 2 + 4) - 23) % 6 = 0

只要下面这两个余数相同就可以了

1
2
23 % 6 = 5
(23 + 2 + 4) % 6 = 5

记一下获取图片宽高的方法

1
2
3
4
5
6
7
8
9
function getMeta(url) {
var img = new Image();
img.onload = function () {
let { width, height } = img;
console.log('{width, height} :>> ', { width, height });
};
img.src = url;
}
getMeta("https://www.xxxx.com/images.png")

大名鼎鼎的 request 库不维护了,虽然还能使用,但是作者不建议使用,翻了一下打算用 got 作为替代。

具体见

安装 got 依赖

1
yarn add got

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
import { promisify } from 'util';
import stream from 'stream';
import fs from 'fs-extra';
import got from 'got';

const pipeline = promisify(stream.pipeline);

pipeline(
got.stream('http://xxx.com/xxx.zip'),
fs.createWriteStream('xxx.zip')
).then(() => {
console.log('文件下载成功');
});

后记

发现 got 官方 demo 里的有些方法没用过,后面要抽时间了解一下。

  • util.promisify(original) 将回调转换为promise返回,看上去不错啊
  • stream.pipeline 一种在流和生成器之间传递管道的模块方法,用于转发错误并正确清理并在管道完成时提供回调。

问题

优化下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function filterCategories(list = [], name) {
return list.filter((data) => {
let { extensions } = data;
if (extensions == undefined) {
return false;
}
let { appName } = extensions;
if (appName == undefined) {
return false;
}
return appName == name;
});
}

为了容错,每次获取对象下面属性都会先判断一下。但是代码看上去不优雅

比如还有这种写法,更难看了

1
2
3
4
5
6
7
8
9
10
11
function filterCategories(list = [], name) {
return list.filter((data) => {
try {
let { extensions } = data;
let { appName } = extensions;
return appName == name;
} catch (error) {
return false;
}
});
}

群里的也有群友说用 lodash 里的_.has _.get方法

解决

MDN - 可选链操作符,优雅的一行解决问题。

1
2
3
function filterCategories(list = [], name) {
return list.filter((data) => data?.extensions?.appName == name);
}

后记

IE:小老弟,你写的什么东西?我看不懂

一声不吭直接 babel.io 转码安排上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function filterCategories(list = [], name) {
return list.filter((data) => {
var _data$extensions;

return (
(data === null || data === void 0
? void 0
: (_data$extensions = data.extensions) === null ||
_data$extensions === void 0
? void 0
: _data$extensions.appName) == name
);
});
}

问题

有一段字符串 let string = 'let a = 123;console.log(a)',需要运行这段字符串。

我第一个想到的就是是 eval

但是 MDN 提到了:永远不要使用 eval!

使用 eval 的糟糕代码:

1
2
3
4
function looseJsonParse(obj) {
return eval('(' + obj + ')');
}
console.log(looseJsonParse('{a:(4-1), b:function(){}, c:new Date()}'));

不用 eval 的更好的代码:

1
2
3
4
function looseJsonParse(obj) {
return Function('"use strict";return (' + obj + ')')();
}
console.log(looseJsonParse('{a:(4-1), b:function(){}, c:new Date()}'));

解决

1
Function(`let string = 'let a = 123;console.log(a)`)();

后记

真香

被问到 BFC 了,讲真,我有时候怀疑自己是不是前端,以前没有注意到这类问题。现在都是组件化开发,平时我自己用到的都是基本的 flex 一把梭,平时页面可能也对细节要求不低,但是讲真,确实没有注意到 BFC,难道是我是写功能太多,切页面太少的缘故?还是说现在浏览器越来越新,我们不需要再考虑这样的问题了?类似于 js 的 varlet

既然碰到了那就学习一下吧。

文档与教程

MDN 的标准文档,读起来可能艰涩一些,可以看一下其他的文章教程后再回过头来看这个文档:

优秀教程文章,可以看了这三篇文章再去看官方文档:

个人理解

先引用一下 MDN 文档,毕竟是标准,当然直接这么看你可能还是有点楞逼,不知道 BFC 是干嘛的?反正我是楞逼的

阅读全文 »

需求

鼠标悬停变小手,mark 一下以后不用查资料了

实现

1
cursor:pointer;
  • cursor 其他取值
  • auto 标准光标
  • default 标准箭头
  • pointer, hand 手形光标
  • wait 等待光标
  • textI 形光标
  • vertical-text 水平I形光标
  • no-drop 不可拖动光标
  • not-allowed 无效光标
  • help 帮助光标
  • all-scroll 三角方向标
  • move 移动标
  • crosshair 十字标
  • e-resize
  • n-resize
  • nw-resize
  • w-resize
  • s-resize
  • se-resize
  • sw-resize

问题

1
['1', '2', '3'].map(parseInt);

求上面的返回值

翻车

控制台一敲结果是 [1, NaN, NaN]

这车翻的太丢人了?Mark 一下

竟然还有人问为啥

熟读文档:

补充一下:

实际上这是对 map 方法回调函数的第二个参数和 parseInt 的第二个参数是进制

1
['1', '2', '3'].map(parseInt);

的执行过程是

1
2
3
parseInt('1', 0); // 1
parseInt('2', 1); // NaN
parseInt('3', 2); // NaN

还问

  • 为什么 parseInt 第二个参数为 0 返回1
  • 解释一下下面的现象
1
2
parseInt('19', 7); // 1
parseInt('91', 7); // NaN

第一个问题有人回答是0进制,我直接???

答案其实文档中有,我已经分别加粗了

对于 parseInt(string, radix)

如果 radix 是 undefined、0或未指定的,JavaScript会假定以下情况

  1. 如果输入的 string以 “0x”或 “0x”(一个0,后面是小写或大写的X)开头,那么radix被假定为16,字符串的其余部分被当做十六进制数去解析。
  2. 如果输入的 string以 “0”(0)开头, radix被假定为8(八进制)或10(十进制)。具体选择哪一个radix取决于实现。ECMAScript 5 澄清了应该使用 10 (十进制),但不是所有的浏览器都支持。因此,在使用 parseInt 时,一定要指定一个 radix。
  3. 如果输入的 string 以任何其他值开头, radix 是 10 (十进制)。

如果第一个字符不能转换为数字,parseInt会返回 NaN。

群友疑惑

parseInt(4,4) 为啥是 NaN
parseInt(10, 9) 为啥是9 10的9进制是9?

阅读全文 »

问题

需要安装 Jenkins 插件的时候发现插件列表为空。

解决

选择插件管理 - 高级 - 升级站点 - 更改 URL

https 替换为 http

点击提交即可