String vs string:代码性能的隐形杀手
\n\n您是否曾疑惑,为什么在有些地方TypeScript会报错String,而在另一些地方却报错string?或者,您是否见过经验丰富的开发者看到new String("hello")时皱起眉头?这个问题在Stack Overflow上可是个热门话题,获得了数千次浏览。这绝非偶然。String和string之间的区别不仅仅是语言的一个特性,而是一个根本性的差异,它会影响您代码的性能、安全性和可读性。
在本文中,我们不会仅仅给出干巴巴的定义。我们将深入剖析每种类型背后的原理,它们在运行时的行为方式,以及为什么正确的选择可以将您的应用程序速度提升数十个百分点。准备好,精彩内容即将开始!
\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
当您编写let name: string = "Alice"时,您创建了一个原始类型。当您编写let name: String = new String("Alice")时,您创建了一个包装器对象。有趣的事情就从这里开始。
string类型(小写)
\n这是一个原始类型。它在99.9%的情况下被使用。它轻量、快速且可预测。两个原始类型的比较是按值进行的:
\n\nlet a: string = "hello";\nlet b: string = "hello";\nconsole.log(a === b); // true\n\nTypeScript推荐使用它。这是声明字符串变量、函数参数和返回值的实际标准。
\n\nString类型(大写)
\n这是一个对象类型,是一个包装器构造函数。它从JavaScript时代起就存在,用于处理字符串方法。然而,直接使用它通常被认为是一种反模式,除非您有非常特殊的任务。两个String对象的比较是按引用进行的:
let a: String = new String("hello");\nlet b: String = new String("hello");\nconsole.log(a === b); // false! 这是内存中的不同对象\n\n这可能导致难以发现的bug。想象一下,您正在检查字符串值是否等于"hello",却因为有人使用了new String()而得到false。
性能:为什么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——只是内存中的字符序列。对其的操作在引擎级别进行了优化。