1、Algorithm 一道算法题
这道题可以使用动态规划的思想。即想要使用最小的步数到达目的地,那么每一步都需要跳到能够得到的最远距离。
通过遍历数组,找到每个元素能跳跃的最远距离范围内,能跳到最远的那个元素作为当前元素的下一跳的元素。然后统计元素的个数。
遍历的过程中,记录当前元素的跳跃最远距离索引 end,当前元素能跳跃的最远范围内能跳到最远的距离的那个元素的索引 maxPosition,已经跳跃的元素个数 step。最后 step 就是所求的最小的到达目的地的步数。
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
const len = nums.length;
let step = 0,end = 0,maxPosition = 0;
// 遍历数组,找到每个元素能跳跃的最远距离的范围内能跳到最远的下一个距离的那个元素并把 step +1
for(let i=0;i<nums.length-1;i++){
maxPosition = Math.max(maxPosition,i+nums[i]);
if(i === end){
end = maxPosition;
step++;
}
}
return step;
};
2、Review 读一篇英文文章
Debounce – How to Delay a Function in JavaScript (JS ES6 Example)
In JavaScript, a debounce function makes sure that your code is only triggered once per user input. Search box suggestions, text-field auto-saves, and eliminating double-button clicks are all use cases for debounce.
In this tutorial, we'll learn how to create a debounce function in JavaScript.
在 JavaScript 中,防抖函数(debounce function)确保你的代码在每次用户输入时只触发一次。搜索框建议、文本字段自动保存和消除重复按钮点击都是防抖的使用场景。
在本教程中,我们将学习如何在 JavaScript 中创建一个防抖函数。
The term debounce comes from electronics. When you’re pressing a button, let’s say on your TV remote, the signal travels to the microchip of the remote so quickly that before you manage to release the button, it bounces, and the microchip registers your “click” multiple times.
防抖(debounce)一词源自电子学。当你按下按钮,比如说电视遥控器上的按钮时,信号迅速传输到遥控器的微芯片,以至于在你释放按钮之前,它会反弹,导致微芯片多次注册你的“点击”。
To mitigate this, once a signal from the button is received, the microchip stops processing signals from the button for a few microseconds while it’s physically impossible for you to press it again.
为了解决这个问题,一旦接收到按钮的信号,微芯片会在你无法再次按下按钮的几微秒内停止处理来自按钮的信号。
In JavaScript, the use case is similar. We want to trigger a function, but only once per use case.
Let's say that we want to show suggestions for a search query, but only after a visitor has finished typing it.
Or we want to save changes on a form, but only when the user is not actively working on those changes, as every "save" costs us a database trip.
And my favorite—some people got really used to Windows 95 and now double click everything 😁.
This is a simple implementation of the debounce function (CodePen here):
在 JavaScript 中,使用情况类似。我们希望触发一个函数,但每个使用情况只触发一次。
假设我们想要为搜索查询显示建议,但只有在访问者完成输入后才显示。
或者我们想要在表单上保存更改,但只有在用户不活跃地进行这些更改时才保存,因为每次“保存”都会导致数据库访问。
还有一个我最喜欢的例子——有些人非常习惯于 Windows 95,现在什么都双击 😁。
这是一个简单的防抖函数实现(CodePen 在这里):
function debounce(func, timeout = 300){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}
function saveInput(){
console.log('Saving data');
}
const processChange = debounce(() => saveInput());
It can be used on an input or a button or a window event:
它可以用在一个输入框上或按钮上,或窗口事件中。
And on other elements like a simple JS function.
So what’s happening here? The
debounceis a special function that handles two tasks:
- Allocating a scope for the timer variable
- Scheduling your function to be triggered at a specific time
Let’s explain how this works in the first use case with text input.
When a visitor writes the first letter and releases the key, the
debouncefirst resets the timer withclearTimeout(timer). At this point, the step is not necessary as there is nothing scheduled yet. Then it schedules the provided function—saveInput()—to be invoked in 300 ms.But let's say that the visitor keeps writing, so each key release triggers the
debounceagain. Every invocation needs to reset the timer, or, in other words, cancel the previous plans withsaveInput(), and reschedule it for a new time—300 ms in the future. This goes on as long as the visitor keeps hitting the keys under 300 ms.The last schedule won’t get cleared, so the
saveInput()will finally be called.
还可以在其他元素上使用,比如一个简单的 JavaScript 函数。
那么这里发生了什么呢?防抖是一个特殊的函数,它处理两个任务:
- 为计时器变量分配一个作用域。
- 在特定时间调度你的函数触发。
让我们解释一下在文本输入的第一个使用情况中它是如何工作的。
当访问者输入第一个字母并释放按键时,防抖首先通过 clearTimeout(timer) 重置计时器。此时这一步骤并不是必要的,因为还没有安排任何计划。然后它安排提供的函数 saveInput() 在 300 毫秒后被调用。
但是假设访问者继续输入,所以每次释放按键都会再次触发防抖。每次调用都需要重置计时器,换句话说,取消之前使用 saveInput() 安排的计划,并重新安排一个新的时间——未来的 300 毫秒。只要访问者在 300 毫秒内持续按键,这个过程就会一直进行下去。
最后一个计划不会被清除,所以 saveInput() 最终会被调用。
That’s good for triggering auto-save or displaying suggestions. But what about the use case with multiple clicks of a single button? We don’t want to wait for the last click, but rather register the first one and ignore the rest (CodePen here).
这对于触发自动保存或显示建议非常有用。但是对于多次点击同一个按钮的情况怎么办呢?我们不想等待最后一次点击,而是要注册第一次点击并忽略后续的点击(CodePen 在这里)。
function debounce_leading(func, timeout = 300){
let timer;
return (...args) => {
if (!timer) {
func.apply(this, args);
}
clearTimeout(timer);
timer = setTimeout(() => {
timer = undefined;
}, timeout);
};
}
Here we trigger the
saveInput()function on the firstdebounce_leadingcall caused by the first button click. We schedule the timer destruction for 300 ms. Every subsequent button click within that timeframe will already have the timer defined and will only push the destruction 300 ms to the future.
在这里,我们通过第一次按钮点击触发 debounce_leading 调用来触发 saveInput() 函数。我们为 300 毫秒安排了计时器的销毁。在这个时间范围内的每次后续按钮点击都已经定义了计时器,并且只会将销毁时间推迟 300 毫秒。
In this article, I showed you how to implement a debounce function in JavaScript and use it to, well, debounce events triggered by website elements.
However, you don’t need to use your own implementation of debounce in your projects if you don’t want to. Widely used JS libraries already contain its implementation. Here are a few examples:
在本文中,我向您展示了如何在 JavaScript 中实现一个防抖函数,并将其用于防抖网站元素触发的事件。
然而,如果您不想使用自己实现的防抖函数,您并不需要在项目中使用它。广泛使用的 JavaScript 库已经包含了它的实现。以下是一些例子:
| Library | Example |
|---|---|
| jQuery (via library) | $.debounce(300, saveInput); |
| Lodash | _.debounce(saveInput, 300); |
| Underscore | _.debounce(saveInput, 300); |
3、Technique/Tips 分享一个小技术
前端代码重构与设计模式
3.1 如果业务有一个需求点,需要对不同的目标(dst),在不同类型(type)下得到不同的长度(length)。此时应该如何编写前端逻辑呢?
先写一个不优雅的例子:
Bad Case:
const TYPE_MAP = {
TYPE_ONE:"TYPE_ONE",
TYPE_TWO:"TYPE_TWO",
TYPE_THREE:"TYPE_THREE"
}
const DST_MAP = {
DST_ONE:"DST_ONE",
DST_TWO:"DST_TOW",
DST_THREE:"DST_THREE"
}
let length = 0;
if(type === TYPE_MAP.TYPE_ONE ){
switch(dst){
case DST_MAP.DST_ONE:
length = 1;
break;
case DST_MAP.DST_TWO:
length = 2;
break;
case DST_MAP.DST_THREE:
length = 3;
break;
default:
length = 0;
}
}
else if(type === TYPE_MAP.TYPE_TWO){
switch(dst){
case DST_MAP.DST_ONE:
length = 1;
break;
case DST_MAP.DST_TWO:
length = 2;
break;
case DST_MAP.DST_THREE:
length = 3;
break;
default:
length = 0;
}
}
else if(type === TYPE_MAP.TYPE_THREE){
switch(dst){
case DST_MAP.DST_ONE:
length = 1;
break;
case DST_MAP.DST_TWO:
length = 2;
break;
case DST_MAP.DST_THREE:
length = 3;
break;
default:
length = 0;
}
}
上述代码可以正确实现逻辑,但是太过臃肿,而且如果要新增一种类型或者新增一种目标时,就需要改动逻辑代码,新增 else if 语句以及 case 语句。 其实上述逻辑用 1 行代码就可以为 length 正确赋值,重构后的代码如下:
const maxLength = MAX_LENGTH_FOR_DST_TYPE_MAP[`${type}.${dst}`] || 0;
当然了,除了上述逻辑代码,我们还需要定义一个策略常量 MAX_LENGTH_FOR_DST_TYPE_MAP,它的结构如下:
const MAX_LENGTH_FOR_DST_TYPE_MAP ={
[TYPE_MAP.TYPE_ONE]:{
[DST_MAP.DST_ONE]: 1,
[DST_MAP.DST_TWO]:2,
[DST_MAP.DST_THREE]:3
},
[TYPE_MAP.TYPE_TWO]:{
[DST_MAP.DST_ONE]: 1,
[DST_MAP.DST_TWO]:2,
[DST_MAP.DST_THREE]:3
},
[TYPE_MAP.TYPE_THREE]:{
[DST_MAP.DST_ONE]: 1,
[DST_MAP.DST_TWO]:2,
[DST_MAP.DST_THREE]:3
},
}
虽然我们新增类型 type 或者新增目标 dst 时,仍然需要改动代码,但是此时我们就不需要动逻辑代码了,只需要往常量 MAX_LENGTH_FOR_DST_TYPE_MAP 中新增一个类型和目标的映射即可。
上述的重构方式属于设计模式中的策略模式。
策略模式的思想就是定义一系列可以相互替换的算法策略类,让环境主体接受不同参数的请求,并把请求委托给某一个具体的策略。
对应到我们这里的例子,MAX_LENGTH_FOR_DST_TYPE_MAP 就是一个简单的算法策略类,它封装了 type、dst和 length 三者之间的映射关系,当不同的请求参数传到业务环境主体时,我们使用这一行不变的逻辑代码就可以把请求委托到具体的策略。
const maxLength = MAX_LENGTH_FOR_DST_TYPE_MAP[type][age] || 0;
策略模式是前端常用的设计模式,它提供了对开放-封闭原则的完美支持,并且有效避免了多重条件分支语句,使得代码更易理解、更易维护。
4、Share 分享一个观点
曾国藩说读书,讲两条,一是每日坚持,坚持读一页一行也行,只要每日都读,精进就快,这叫不疾而速。最怕鼓起劲来就大干一场会战,过了劲又撂下不管,那就很难完成。日拱一卒,是完成任何工作、实现任何目标理想的关键。成功都靠拱卒、不靠出车,因为没有那么多车可以出。曾国藩说读书的第二条,是一本未读完,不动下一本。这样你就不会留下一大堆半途而废没读的书。