TypeScript中的String与string:如何选择以提升速度?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

String vs string:代码性能的隐形杀手

\n\n

您是否曾疑惑,为什么在有些地方TypeScript会报错String,而在另一些地方却报错string?或者,您是否见过经验丰富的开发者看到new String("hello")时皱起眉头?这个问题在Stack Overflow上可是个热门话题,获得了数千次浏览。这绝非偶然。String和string之间的区别不仅仅是语言的一个特性,而是一个根本性的差异,它会影响您代码的性能、安全性和可读性。

\n\n

在本文中,我们不会仅仅给出干巴巴的定义。我们将深入剖析每种类型背后的原理,它们在运行时的行为方式,以及为什么正确的选择可以将您的应用程序速度提升数十个百分点。准备好,精彩内容即将开始!

\n\n

原始类型 vs 对象:基础中的基础

\n\n

要理解其中的区别,我们需要回顾一下JavaScript(以及TypeScript)是如何处理数据的。该语言有两种类型的值:

\n\n
    \n
  • 原始类型 (primitives): string, number, boolean, null, undefined, symbol, bigint。它们是不可变的,并且按值存储。
  • \n
  • 对象 (objects): Object, Array, Function,以及String(大写开头)、Number、Boolean。它们是可变的,并且按引用存储。
  • \n
\n\n

当您编写let name: string = "Alice"时,您创建了一个原始类型。当您编写let name: String = new String("Alice")时,您创建了一个包装器对象。有趣的事情就从这里开始。

\n\n

string类型(小写)

\n

这是一个原始类型。它在99.9%的情况下被使用。它轻量、快速且可预测。两个原始类型的比较是按值进行的:

\n\n
let a: string = "hello";\nlet b: string = "hello";\nconsole.log(a === b); // true
\n\n

TypeScript推荐使用它。这是声明字符串变量、函数参数和返回值的实际标准。

\n\n

String类型(大写)

\n

这是一个对象类型,是一个包装器构造函数。它从JavaScript时代起就存在,用于处理字符串方法。然而,直接使用它通常被认为是一种反模式,除非您有非常特殊的任务。两个String对象的比较是按引用进行的:

\n\n
let a: String = new String("hello");\nlet b: String = new String("hello");\nconsole.log(a === b); // false! 这是内存中的不同对象
\n\n

这可能导致难以发现的bug。想象一下,您正在检查字符串值是否等于"hello",却因为有人使用了new String()而得到false。

\n\n

性能:为什么string胜出?

\n\n

现在让我们进入最精彩的部分——数据和优化。我们来编写一个简单的性能测试(您可以在您的环境中运行它,例如Node.js或浏览器)。

\n\n
// 性能测试\nconst iterations = 1_000_000;\n\nconsole.time("string (原始类型)");\nlet sum = "";\nfor (let i = 0; i < iterations; i++) {\n    sum += "a";\n}\nconsole.timeEnd("string (原始类型)");\n\nconsole.time("String (对象)");\nlet sumObj = new String("");\nfor (let i = 0; i < iterations; i++) {\n    sumObj += "a"; // 这里会发生自动装箱和拆箱\n}\nconsole.timeEnd("String (对象)");
\n\n

在典型机器上的结果会有显著差异(原始类型快2-5倍)。为什么?因为:

\n
    \n
  • 原始类型string——只是内存中的字符序列。对其的操作在引擎级别进行了优化。

博客

书籍推荐