本文探讨如何在 JavaScript 代码中运用强类型语言的技巧。这些技巧不仅能减少代码错误,还能缩减代码量。虽然本文以 JavaScript 为例,但这些技巧也适用于大多数弱类型语言。
关键要点:
JavaScript 类型系统
首先快速回顾一下 JavaScript 数据类型系统的工作原理。JavaScript 将其值分为两类:
var a = []; var b = a; a.push('Hello');
当我们更改 a 时,变量 b 也会发生变化,因为它们都引用同一个数组。所有引用类型的工作方式都是如此。JavaScript 不会强制执行任何类型,这意味着任何变量都可以在任何时候保存任何数据类型。本文其余部分将讨论此方法的缺点,以及如何应用强制执行类型的语言中的简单技巧来编写更好的 JavaScript 代码。
引入一致类型规则
一致类型规则在理论上很简单:所有值都应该只有一种类型。强类型语言在编译器级别强制执行此规则,它们不允许您随意混合和匹配类型。弱类型赋予我们很大的自由度。一个常见的例子是将数字连接到字符串中。您不需要执行像在 C 等语言中那样繁琐的类型转换。别担心,我不会告诉您放弃所有便利。一致类型规则只需要您注意变量和函数的行为方式,这样您的代码就会得到改进。
首先,让我们看看该规则如何应用于变量。它非常简单:您的变量应该始终只有一种类型。
var a = []; var b = a; a.push('Hello');
上面的示例显示了问题所在。此规则要求我们假装此示例中的最后一行代码会抛出错误,因为当我们第一次定义变量 text 时,我们赋予它字符串类型的 value,现在我们正在为其赋值一个数字。一致类型规则意味着我们不允许这样更改变量的类型。当您的变量一致时,更容易推断您的代码。它尤其有助于更长的函数,在这些函数中,很容易忽略变量的来源。当在不遵守此规则的代码库中工作时,我意外地导致了许多错误,因为我看到声明了一个变量,然后假设它会保持相同的类型——因为让我们面对现实,这有意义,不是吗?通常没有理由将不同的类型分配给同一个变量。
相同的规则也适用于此。函数的参数也应该保持一致。一个错误的例子:
var text = 'Hello types'; // 错误!不要这样做! text = 1;
这里有什么问题?通常认为,根据类型检查来分支逻辑是不好的做法。对此有一些例外,但通常更好的选择是使用多态性。您应该努力确保函数参数也只有一种类型。如果您忘记考虑不同的类型,它会减少出现问题的可能性,并使代码更简单,因为您不必编写代码来处理所有不同类型的案例。编写 sum 函数的更好方法如下:
function sum(a, b) { if (typeof a === 'string') { a = 1; } return a + b; }
然后,您在调用代码中而不是在函数中处理类型检查。从上面可以看出,该函数现在简单多了。即使我们必须将类型检查移动到其他地方,我们越早在代码中执行它们,效果就越好。我们将在本文后面讨论类型检查和 typeof 的使用,包括如果使用不当,类型检查如何轻松级联。
这与其他两个相关:您的函数应该始终返回相同类型的 value。我们可以在这里举 AngularJS 的一个例子。AngularJS 提供了一个将文本转换为小写的函数,称为 angular.lowercase。还有一个标准函数,String.prototype.toLowerCase。我们可以比较它们的行为来更好地理解规则的这一部分:
function sum(a, b) { return a + b; }
变量 a 将包含您期望的内容:“hello types”。但是,b 将包含什么?它将是一个空字符串吗?函数会抛出异常吗?或者它可能只是 null?在这种情况下,b 的 value 为 null。请注意,立即很难猜测结果是什么——我们一开始就有三种可能的结果。对于 Angular 函数,对于非字符串值,它将始终返回输入。现在,让我们看看内置函数的行为:
var a = []; var b = a; a.push('Hello');
第一次调用的结果相同,但第二次调用会抛出异常。内置函数遵循一致类型规则,并且不允许不正确的参数类型。返回值也始终是一个字符串。所以我们可以说内置函数更好,但您可能想知道究竟是如何做到这一点的?让我们考虑一下此类函数的典型用例。我们在代码中的某个点使用它来将字符串转换为小写。正如 JavaScript 代码中经常发生的那样,我们不能 100% 确定我们的输入是否总是字符串。没关系,因为我们是优秀的程序员,我们假设我们的代码没有任何错误。如果我们使用不遵守这些规则的 AngularJS 函数会发生什么?非字符串值会毫无问题地通过它。它可能会通过几个函数,我们甚至可能会通过 XMLHttpRequest 调用发送它。现在错误的值在我们的服务器中,并最终进入数据库。您可以看到我的意思,对吧?如果我们使用遵守规则的内置函数,我们会在那时立即发现该错误。每当您编写函数时,请确保其返回的类型一致。下面显示了一个不好的例子:
var text = 'Hello types'; // 错误!不要这样做! text = 1;
同样,与变量和参数一样,如果我们有这样的函数,我们就无法对它的行为做出假设。我们将需要使用 if 来检查返回 value 的类型。我们可能会在某个时候忘记它,然后我们手中就会出现另一个错误。我们可以通过多种方式重写它,以下是一种解决此问题的方法:
function sum(a, b) { if (typeof a === 'string') { a = 1; } return a + b; }
这次我们确保所有路径都返回一个字符串。现在更容易推断函数的结果了。
null 和 undefined 是特殊的
到目前为止,我们实际上只讨论了原始类型。当涉及到对象和数组时,您应该遵循相同的规则,但需要注意两个特殊情况。处理引用类型时,有时需要指示没有 value。一个很好的例子是 document.getElementById。如果找不到匹配的元素,它将返回 null。这就是为什么我们将 null 视为与任何对象或数组共享类型的原因,但仅限于那些对象或数组。您应该避免从可能返回 Number 等原始值的函数中返回 null。undefined 也可以被认为是引用的“无值”。出于大多数目的,它可以被视为等于 null,但由于其在其他面向对象语言中的语义,null 更为可取。
使用数组时,您还应该考虑空数组通常比 null 更好。尽管数组是引用类型,您可以使用 null 与它们一起使用,但通常返回空数组更有意义。让我们来看下面的例子:
var a = []; var b = a; a.push('Hello');
这可能是数组最常见的用法之一。您从函数中获取一个数组,然后对其进行迭代以执行其他操作。如果 getListOfItems 在没有项目时返回 null,上面的代码会发生什么?它会抛出错误,因为 null 没有长度(或任何其他属性)。当您考虑像这样使用数组,甚至是 list.forEach 或 list.map 时,您可以看到当没有值时返回空数组通常是一个好主意。
类型检查和类型转换
让我们更详细地了解类型检查和类型转换。您应该何时进行类型检查?您应该何时进行类型转换?
类型转换的第一个目标应该是确保您的值的类型正确。数值应该是数字而不是字符串,依此类推。第二个目标应该是您只需要转换一次 value。进行类型转换的最佳位置是在源处。例如,如果您从服务器获取数据,则应该在处理接收到的数据的函数中进行任何必要的类型转换。从 DOM 解析数据是一个非常常见的错误开始出现的地方。假设您有一个包含数字的文本框,并且您想读取它。或者,它可能只是某个 HTML 元素中的属性,它甚至不必是用户输入。
var text = 'Hello types'; // 错误!不要这样做! text = 1;
由于您可以从 DOM 获取的值通常是字符串,因此在读取它们时进行类型转换非常重要。在某种程度上,您可以将其视为模块的“边缘”。数据通过读取它的函数进入您的 JavaScript 模块,因此它必须将数据转换为正确的格式。通过在模块边缘进行类型转换,我们确保内部不必处理它。这在很大程度上减少了隐式类型强制转换导致错误的可能性。它还允许我们编写更少的代码,因为我们不允许错误的值从边缘进入模块。
function sum(a, b) { if (typeof a === 'string') { a = 1; } return a + b; }
您应该只将 typeof 用于验证,而不是根据类型分支逻辑。对此有一些例外,但这是一个很好的经验法则。让我们来看两个例子:
function sum(a, b) { return a + b; }
这是一个使用 typeof 进行验证的示例。我们确保传递给函数的参数是正确的类型。但是,下面的示例显示了根据类型分支逻辑的含义。
var a = []; var b = a; a.push('Hello');
不要这样做。虽然有时可能需要这样做,但这通常是设计不良的标志。如果您发现自己经常执行这种逻辑,那么您可能应该在代码的早期将 value 转换为正确的类型。如果您最终在代码中使用了大量 typeof,这可能表示您可能需要转换要比较的值。类型检查通常会扩散,这通常是关于类型的设计不良的标志。如前所述,您应该尝试在模块边缘进行类型转换,因为它允许您避免 typeof 级联。如果您尽早进行转换,则之后调用的任何函数都不必进行类型检查或类型转换。这也适用于对象:如果您发现自己使用 instanceof 进行大量检查或检查对象上是否存在属性,则表示您可能应该以不同的方式构造数据。与 typeof 相同的规则也适用于 instanceof:您应该尝试避免它,因为它可能是设计不良的标志。但是,有一种情况是不可避免的:
var text = 'Hello types'; // 错误!不要这样做! text = 1;
如果您的代码需要对异常类型进行特定处理,instanceof 通常是一个不错的选择,因为 JavaScript catch 不允许像其他一些语言那样按类型区分。在大多数其他情况下,您应该尝试避免 instanceof。
结论
正如我们所发现的,JavaScript 的弱类型为我们带来了极大的自由,但我们也必须谨慎行事。否则,我们将最终陷入一个类型混乱的局面,没有任何意义。通过确保我们的代码遵循一致类型规则,我们可以避免很多麻烦。当我们知道类型时,更容易推断我们的代码。我们不必在代码中构建许多类型检查来防止错误。如果您没有使用强类型语言,这似乎很困难,但在您需要调试或维护代码时,它会得到很大的回报。有关该主题的更多信息,我建议您查看 TypeScript。它是一种类似于 JavaScript 的语言,但它为该语言添加了更强的类型语义。它还有一个编译器,当您尝试执行一些愚蠢的操作(例如混合和匹配类型)时,它会吐出错误。
关于 JavaScript 中强类型语言的常见问题解答 (FAQ)
强类型语言是指变量绑定到特定数据类型的语言,任何与该类型不一致的操作都会导致错误。示例包括 Java 和 C 。另一方面,像 JavaScript 这样的弱类型语言允许变量保存任何类型的数据,并且在必要时会自动进行类型转换。如果处理不当,这种灵活性可能会导致意想不到的结果。
JavaScript 本身是一种弱类型语言,但您可以使用 TypeScript(JavaScript 的静态类型超集)来强制执行强类型。TypeScript 为 JavaScript 添加了静态类型,允许在编译时进行类型检查。这有助于在开发过程的早期发现错误。“严格模式”是 JavaScript 中的另一个方法,它通过为不安全的动作抛出错误来使语言的行为更像强类型语言。
强类型语言提供了一些好处。它们可以帮助在编译时而不是运行时捕获错误,这可以节省大量调试时间。它们还使代码更具自文档性,因为变量的数据类型清楚地表明了它们的使用方式。此外,它们可以使代码更可预测且更容易推断,因为它们可以防止意外的类型转换。
虽然 JavaScript 默认情况下不是强类型语言,但您可以使用 TypeScript 或 Flow 等工具来强制执行强类型。这些工具为 JavaScript 添加了静态类型,允许在编译时进行类型检查。这有助于在开发过程的早期发现错误。但是,需要注意的是,这些工具不会改变 JavaScript 的底层性质;它们只是在它之上提供了一层类型安全性。
TypeScript 是由 Microsoft 开发的 JavaScript 静态类型超集。它为 JavaScript 添加了静态类型,允许在编译时进行类型检查。这有助于在开发过程的早期发现错误。TypeScript 代码被转换为 JavaScript,这意味着它可以在任何 JavaScript 运行的地方运行。它与 JavaScript 完全兼容,可以使用 JavaScript 的所有功能。
虽然强类型语言提供了许多好处,但它们也有一些缺点。它们可能更冗长,需要更多代码才能完成与弱类型语言相同的任务。它们还需要一个编译步骤,这可能会减慢开发过程。此外,它们可能不太灵活,并且对于某些任务(例如处理动态数据)更难使用。
“严格模式”是 JavaScript 中的一项功能,它使语言的行为更像强类型语言。它会为不安全的动作抛出错误,例如为只读属性赋值或在声明之前使用变量。这有助于在开发过程的早期发现错误。要启用“严格模式”,只需在 JavaScript 文件或函数的顶部添加“use strict;”一行即可。
类型强制是 JavaScript 中的一项功能,其中语言在必要时会自动将一种数据类型转换为另一种数据类型。例如,如果您尝试添加数字和字符串,JavaScript 将在执行加法之前将数字转换为字符串。虽然这很方便,但如果处理不当,它也可能导致意想不到的结果。
避免 JavaScript 中类型强制的一种方法是使用“严格模式”,它会为不安全的动作抛出错误。另一种方法是使用“===”运算符而不是“==”运算符进行比较,因为前者不执行类型强制。此外,您可以使用 TypeScript 或 Flow 等工具为 JavaScript 添加静态类型,这有助于在编译时捕获与类型相关的错误。
强类型语言在 JavaScript 中的使用可能会在未来增加,因为它们提供了许多好处,例如尽早捕获错误并使代码更可预测。TypeScript 和 Flow 等工具越来越流行,并且正在开发新的工具和功能以使 JavaScript 更具类型安全性。但是,JavaScript 的灵活性和动态性将使其继续成为许多开发人员的首选。
以上是JS中强烈打字的语言借用技术的详细内容。更多信息请关注PHP中文网其他相关文章!