什么是替代char的最佳选择?

5
我正在将一些Java代码移植到JavaScript。我有许多成员元素是char。将它们变成数字或字符串更有效率(其中字符串始终只包含一个字符)? 更新: 在Java中,它目前的用法是:我有:
/** alignment is left. */
public static final char TAB_STOP_LEFT = 'l';

/** alignment is center. */
public static final char TAB_STOP_CENTER = 'c';

/** alignment is right. */
public static final char TAB_STOP_RIGHT = 'r';

private char tabStop;

然后我有很多地方要么给tabStop分配一个TAB_STOP_*值,要么测试tabStop的值是否等于特定的值。它不需要是l/c/r,我只是为了在调试器中更容易阅读而使用了这些值(此代码来自Java 1.3,早在枚举类型出现之前)。

最干净的方法是将它们定义为枚举类型,然后tabStop就成为一个数字。然而,如果每个字符都是一个长度为1的字符串,速度更快、占用内存更少,那我可以选择使用字符串。

更新2: 非常感谢Juan Mendes和JLRishe——非常详细。我决定采用枚举类型(即数字),因为两者之间的差异很小,而枚举类型可以使一切更清晰。


1
@pc-shooter 这完全是错误的。 - Jordan Running
我认为这取决于字符的使用方式。如果您正在进行二进制操作,请使用数字。如果它们用于表示Unicode字符(这似乎更有可能),请使用字符串。 - p.s.w.g
1
如果没有关于您的应用程序的更多具体信息,很难回答这个问题。通常,字符数据应存储为字符串值,但是如果您使用char太多,以至于将它们全部视为字符串可能会对内存使用/性能产生负面影响,或者如果您的代码基于它们的数字值进行大量处理,则可能有正当理由将它们存储为数字。 - JLRishe
@JLRishe - 我刚刚提供了更多信息。谢谢。 - David Thielen
@DavidThielen 请看我的回答,我认为答案取决于你将如何使用它。我发布了一个示例,似乎表明你应该使用字符串而不是整数。 - Ruan Mendes
4个回答

3
JavaScript中没有char。您可以使用1个字符的字符串。如果您想要使用整数来节省内存,每当需要时都必须调用String.fromCharCode,这将创建一个String,因此我很难相信您会从存储整数中获得任何好处。
下面是一个测试,我使用一个int代表'a',并使用字符串和int创建了一个更长的字符串http://jsperf.com/using-ints-as-chars 设置代码
var x = "a";
var y = 97;

从整数创建(383次/秒)

var str = '';
for (var i = 0; i < 100000; i++) {
   str += String.fromCharCode(y);
}

创建 1 个字符字符串(592 次/秒)更快

var str = '';
for (var i = 0; i < 100000; i++) {
   str += x;
}

如果您正在尝试在JS中模拟枚举,则可以使用字符串的一种简单方式,这样更容易调试。使用指针比较字符串,因此没有性能惩罚。
function Enum(key1, key2, ...) {
    for (var i = 0; i < arguments.length;; i++) {
        this[arguments[i]] = arguments[i];
    }
}

var Planets = new Enum('Earth', 'Mars', 'Venus');
//
if (someValue == Planets.Earth) {
    // console.log(Planets.Earth) -> outputs "Earth"
}

理想情况下,您无法将枚举与字符串进行测试。如果您有一个字符串并且想要与枚举进行比较,则需要先将其转换为枚举(以确保它是有效的字符串之一)。以下是更安全的枚举。
function EnumInstance(value) {  
    this.getValue = function() { // debug only
        return value;
    }
}

function Enum(enumValues) {
    for (var i = 0; i < arguments.length; i++) {
        this[arguments[i]] = new EnumInstance(arguments[i]);
    }
}

Enum.prototype.fromString = function(enumValue) {
    if ( !this[enumValue] ) {
        throw new Error('Invalid enum value: ' + enumValue);
    }
    return this[enumValue];
};


var Planets = new Enum('Earth', 'Venus', 'Mars');
// This outputs false, you can't compare the strings directly    
console.log("Are Planets.Earth and 'Earth' equal?", Planets.Earth == 'Earth');
// This outputs true, first convert into an enum
console.log("Are Planets.Earth and Planets.fromString('Earth') equal?",
    Planets.Earth == Planets.fromString('Earth'));
// If you try Planets.fromString('Pluto'), an exception will be thrown
try {
    var enumValue = Planets.fromString('Pluto')
} catch(e) {
    console.log(e);
}



console.log("Are Planets.Earth and 'Earth' equal?", Planets.Earth == 'Earth');
// This outputs true, first convert into an enum
console.log("Are Planets.Earth an 'Earth' equal?", 
    Planets.Earth == Planets.fromString('Earth'));

更新

https://stackoverflow.com/users/1945651/jlrishe所述,字符串比较不使用地址比较,像常规的Object一样,因此等式测试将扫描字符串。因此,微小的优化是如果您要经常测试字符串的相等性,则使用数字而不是字符串。请注意,如果您使用我上面展示的“几乎类型安全”的枚举,则相等性检查仅针对指针。请参见http://jsperf.com/string-comparison-versus-number-comparisonIs JavaScript string comparison just as fast as number comparison?


基于问题中的这一句话:“它不需要是 l/c/r,我只是为了在调试器中更容易阅读而使用了这些”,这似乎只是任意占位符,它们的实际值实际上在程序中并没有被使用,除了调试之外。但是对于这个启示性的分析给予赞扬。 - JLRishe
使用它们作为枚举时,将其设置为字符串,以便于阅读代码的人清楚明白。添加了一些关于枚举的代码,不必担心性能,而是要注重可用性。http://jsfiddle.net/2VMBx/2/ @JLRishe - Ruan Mendes

3
首先要记住的是“过早优化是万恶之源”,除非您担心这会影响应用程序的效率,否则这不太可能成为其性能瓶颈。
如果您的目标是提供类似枚举的功能,那么我认为数字与字符串之间的差异更不可能成为问题,但为了进一步缓解这些担忧,如果在整个应用程序中始终引用它们,则可以随时切换并测试哪个更有效,然后使用最好的一个。
在JavaScript中,与Java一样,字符串和其他对象都是按引用存储的,因此在您的情况下,单个字符的字符串的内存占用量基本上是不相关的,问题变成了该字符串的引用成本。我找不到任何确凿的信息,但我强烈怀疑这最多是64位,因为这是典型现代计算机上最大的地址大小。
因此,需要考虑以下问题:
1.内存使用情况 2.比较两个值的效率 3.调试的便利性
字符串、数字和对象在第一点上都应该相等,因为数字占用64位,引用占用64位。
在第二点上,它们非常匹配,因为我们可以相当安全地假设JavaScript引擎设计者会在比较两个字符串之前进行引用相等性检查,因此如果两个变量引用同一字符串实例,则应该与比较两个数字相当。
在第三点上,字符串比数字略有优势,因为您可以直接将其打印出来,而将数字转换为可理解的值需要一个转换步骤。
因此,我建议短期内坚持使用字符串,然后在遇到一些性能问题时重新评估。如果编写的代码良好,切换应该很容易。如上所述,如果您正在使用对一小组字符串的引用,则这些字符串的实际大小在很大程度上是不重要的。所以请随意使用“left”,“center”,“right”作为实际值,如果这使您的调试更加明智。

我不同意第一点:如果一个数字使用64位,那么一个字符串就会使用64位(指针)加上每个字符至少一个比特(这取决于编码),再加上包含在对象中的字段的大小(例如长度)。 - Pablo Lozano
@PabloLozano 我认为更重要的问题不是它需要多少内存。如果你计划使用 char 代码而不是字符串,那么你最终将不得不将整数转换为字符串,这会使它在大多数情况下运行更慢。请参阅我的答案。 - Ruan Mendes
1
如果他在整个应用程序中使用了一组三个预定义值,正如问题所述,那么如果它们是引用类型,则这些实际值的大小很快变得在数学上不重要。如果您有代码 var a =“hello!”; var b = a; var c = a;,则创建 bc 的成本是每个指针的成本,而不是指针加上字符串 "hello" 的大小。 - JLRishe
@JLRishe 我认为你已经明白了。如果你必须将它们转换回字符串,那么就把它们变成字符串。然而,如果你将它们用作只读,那么这并不重要,因为你只是测试两个值是否指向同一个字符串。 - Ruan Mendes
@PabloLozano,OP接受了你的答案,因为你提供了一个链接,似乎满足了他对高性能的渴望。然而,OP没有问一个很好的问题,因为性能总是取决于你如何使用你的代码。OP只是想要一个规则,他们可以随时使用,而不需要思考,这从来不是一件好事...特别是当涉及到过早优化时。不要为了可能99%的时间都不重要的优化而使你的代码变得不可读。 - Ruan Mendes
显示剩余7条评论

0

和Java不同,JavaScript没有强类型,所以使用什么类型并不重要。

请阅读此链接:http://www.w3schools.com/js/js_datatypes.asp

var x;               // Now x is undefined
var x = 5;           // Now x is a Number
var x = "John";      // Now x is a String

如果您需要知道每种类型的内存使用情况,可以阅读本文档的第8节http://people.mozilla.org/~jorendorff/es5.html#sec-8

The String type is the set of all finite ordered sequences of zero or more 16-bit unsigned integer values (“elements”)
The Number type has exactly 18437736874454810627 (that is, 264253+3) values, representing the double-precision 64-bit

2
我一直跟着你,直到w3schools那部分。那么,来看看MDN上的这个页面怎么样? - Tom Fenech
这并没有真正回答问题。OP正在询问使用字符串与数字表示字符值的效率,而JavaScript变量是动态类型与此无关。 - JLRishe
这并不能证明任何事情。 - Ruan Mendes
我其实不太明白它的意思,只是想指出一些资源以防有所帮助... - Baptiste Pernet

0

由于asm.js的效率提升基于始终使用数字,避免使用任何其他类型,因此我认为使用int更有效率。比较更容易(更快),并且需要更少的内存。

更新:这里有一个链接,提供V8(Chrome JS引擎)的性能提示。


JavaScript运行时是否足够智能,如果一个变量只被赋予整数值,它会将数字视为整数?如果是这样的话,那就太棒了。 - David Thielen
你发布的链接甚至没有提到 asm.js 或 OP 所问的特定情况,请更明确地引用。 - Ruan Mendes
那里有2个链接,第一个是 asm.js 字母。 - Pablo Lozano

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接