
笔译
我们的经验表明,非技术开发人员和自学成才的开发人员通常不依赖理论原理,而是依赖启发式方法。
启发式是开发人员从实践中学到的模式和经过验证的规则。 它们可能无法完美地工作或在有限的范围内工作,但它们工作得足够好并且不需要认真思考。 以下是启发式的一些示例:
- “使用
$(document).ready(function(){})用于在 jQuery 站点上初始化代码” - “设计
var self = this需要调用回调函数中的方法” - “箭头函数没有运算符
return»
同时,理论原理可以用来寻找其他问题的解决方案。 它总是正确的,并且通常决定特定元素的操作的结构。 理论原则包括,例如:
请注意,我们只是将启发式的例子放在引号中,以强调启发式与理论框架的严谨性相比的手工性质。 这些启发式示例都不是普遍适用的,但它们在足够多的情况下工作,使用它们的开发人员在不完全理解其工作原理的情况下就可以获得工作代码。
支持理论方法的论据
我们经常发现非技术开发人员不愿意使用理论原理解决问题。 这通常是因为他们在职业生涯早期没有机会学习它们,并且由于启发式方法效果令人满意,因此他们继续使用它们。
然而,尽管表面上很复杂,学习该理论还是非常有用的。 为了什么? 因为该理论将使您对自己的解决方案有效充满信心,并且能够独立得出新问题的答案,而无需寻找其他人的解决方案。 从短期来看,启发式算法可能看起来是一种简单而快速的解决方案,但通常会导致不太理想的解决方案(如果有的话)。
此外,依靠启发式方法,你永远无法学会真正解决问题。 也许很多时候你能够找到可行的解决方案,但迟早你会陷入死胡同,而你将看不到出路。 C&P 程序员在工作中依赖启发式方法。
开发人员技能水平标准
在采访前端开发人员时,我们给他们一个编程挑战,并告诉他们可以自由使用任何来源,无论是 Google 还是 Stack Overflow。 通过这种方式,您可以轻松确定开发人员是启发式追随者还是理论追随者。
第一个毫无例外地从 Stack Overflow 中或多或少合适的示例中复制代码。 只有当代码不能按计划工作时,他们才会开始调整它以适合自己。 他们常常做不到这一点。
后者倾向于在 API 文档中寻找答案。 他们在那里找到有关特定函数需要多少个参数的信息,或者所需 CSS 属性的扩展形式的特定语法。
在面试的前五分钟内,您就可以准确地确定候选人是哪种类型的程序员。
例子
我们以开发者 Bill 为例。 他参加了一些培训课程,解决了一些 JavaScript 问题,并在空闲时间编写了网站,但他并没有“真正”学习 JavaScript。
有一天,比尔遇到了这样一个物体:
const usersById = {
"5": { "id": "5", "name": "Adam", "registered": true },
"27": { "id": "27", "name": "Bobby", "registered": true },
"32": { "id": "32", "name": "Clarence", "registered": false },
"39": { "id": "39", "name": "Danielle", "registered": true },
"42": { "id": "42", "name": "Ekaterina", "registered": false }
}
这样的对象可以显示用户列表以及他们是否已注册特定事件。
假设比尔需要检索注册用户的列表。 换句话说,将它们过滤掉。 他遇到了代码,其中的方法 .filter() 用于过滤列表。 所以他尝试了这样的事情:
const attendees = usersById.filter(user => user.registered);这就是他得到的:
TypeError: usersById.filter is not a function “这简直是无稽之谈,”比尔想,因为他看到了其中的代码 .filter() 起到了过滤器的作用。
问题在于比尔依赖于启发式方法。 他不明白这一点 filter 是在数组上定义的方法,而 usersById - 没有方法的普通对象 filter.
困惑的比尔谷歌“JavaScript 过滤器” 他发现很多关于数组的提及,并意识到他需要转向 usersById 到一个数组。 然后根据要求“javascript 将对象转为数组» 他在 Stack Overflow 上找到了使用的示例 Object.keys()。 之后他尝试:
const attendees = Object.keys(usersById).filter(user => user.registered); 这次没有显示错误,但令比尔惊讶的是,该字段 attendees 仍然是空的。
事实是, Object.keys() 返回对象的键,但不返回其值。 本质上,变量的名称 user 容易误导,因为它不是一个对象 user,以及标识符,即字符串。 由于属性 registered 没有为字符串定义, filter 将每个条目评估为 false,并且数组为空。
Bill 仔细查看了 Stack Overflow 上的答案,并做出了以下更改:
const attendees = Object.keys(usersById).filter(id => usersById[id].registered); 这次的结果更好: ["5", "27", "39"]。 但比尔想要得到访客的物品,而不是他们的身份证。
为了弄清楚如何过滤访客,比尔恼火地搜索了“JavaScript 对象过滤器”,检查 Stack Overflow 上的搜索结果并发现 使用以下代码:
Object.filter = (obj, predicate) =>
Object.keys(obj)
.filter( key => predicate(obj[key]) )
.reduce( (res, key) => (res[key] = obj[key], res), {} );比尔复制这些行并尝试:
const attendees = Object.filter(usersById, user => user.registered); 一切正常——尽管尚不清楚原因。 比尔不明白它的用途 reduce 以及如何使用它。 而且,Bill不明白他只是为全局对象定义的 Object 新的非标准方法。
但比尔不在乎——它有效! 他对后果还不感兴趣。
比尔做错了什么?
Bill尝试了启发式的方法来解决问题,遇到了以下问题:
- 使用
.filter()关于变量,比尔得到TypeError。 他不明白这一点filter在普通物体上检测不到。 - 他申请了
Object.keys()“将对象转为数组”,但这本身并没有带来任何结果。 他需要创建一个对象值数组。 - 即使在获取值并将其用作过滤条件之后,他也只收到了标识符,而不是与这些标识符关联的用户对象。 这是因为过滤后的数组包含 ID,而不是用户对象。
- 随着时间的推移,比尔放弃了这种方法,并在互联网上找到了可行的解决方案。 然而,他仍然不明白它是如何运作的——而且他不会浪费时间去弄清楚,因为他还有其他事情要做。
这是一个人为的例子,但我们已经多次看到开发人员以相同的方式解决问题。 为了有效地解决这些问题,您需要摆脱启发式方法并研究理论。
让我们开始了解基础知识
如果比尔是理论方法的支持者,那么这个过程将如下所示:
- 确定给定的输入并确定所需的输出 - 根据其属性: “我有一个对象,其键是代表 ID 的字符串,其值是代表用户的对象。 我想获取一个数组,其值将是用户对象 - 但仅限于已注册的用户对象”
- 了解如何迭代对象: “我知道我可以通过调用来获取对象中的键数组
Object.keys()。 我想要一个数组,因为数组支持迭代”. - 意识到这个方法有助于获取键,你需要将键转换为值,并记住
map是通过转换另一个数组的值来创建新数组的明显方法:Object.keys(usersById).map(id => usersById[id]) - 看到您现在有一个可过滤的自定义对象数组,其中包含要过滤的实际值:
Object.keys(usersById).map(id => usersById[id]).filter(user => user.registered)
如果比尔走了这条路,他本来可以为我们工作。
为什么人们不更频繁地诉诸理论?
有时他们只是不认识她。 通常,他们太忙而没有时间学习这种解决问题的方法 - 他们只需要让事情顺利进行即可。 他们可能会将这种方法变成一种习惯,从而成为他们技能发展的障碍。
为了避免此类错误,请始终从理论开始。 在该过程的每个阶段,请考虑您正在处理哪些数据。 不要总是依赖熟悉的模式,而是考虑原始数据类型:数组、对象、字符串等。 当您使用函数或方法时,请检查文档以确保您确切地知道它支持哪些数据类型、它采用什么参数以及它的输出是什么。
通过这种方法,您将能够在第一次尝试时找到可行的解决方案。 您可以确定其正确性,因为您根据给定的输入和所需的输出数据专门选择了操作。 深入了解每个操作的基础知识(数据类型和返回值),而不是模糊的业务语言(如“注册用户”)。
资料来源:www.habr.com
