5

I am porting over some Java code to JavaScript. I have a lot of member elements that are a char. Is it more efficient to make those a number or a string (where the string will always be a single character)?

Update: The way it's presently used in Java is I have:

/** 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;

And then I have lots of places where I either assign one of the TAB_STOP_* values to tabStop or test the value of tabStop against specific values. There is no need for it to be l/c/r, I just used those to make it easier to read in the debugger (this code is from Java 1.3, long before enums).

The cleanest way to do this is to make them enums and then tabStop is a number. However, if it's faster & uses less memory if each is a 1 character string, I can do that instead.

Update 2: A big thank you to Juan Mendes & JLRishe - really good detail. What I've decided to do is go with enums (ie numbers) because the difference between the two is minimal and enums make everything a lot clearer.

David Thielen
  • 28,723
  • 34
  • 119
  • 193
  • 1
    @pc-shooter That's absolutely false. – Jordan Running May 22 '14 at 16:15
  • I think it depends on exactly *how* the chars are used. If you're doing binary manipulation with them, use a number. If they are used for the Unicode characters they represent (which seems more likely), use a string. – p.s.w.g May 22 '14 at 16:18
  • 1
    It's hard to answer this without some more specifics about your application. Typically, character data should be stored as string values, but if you are using `char`s so much that treating them all as strings could have a negative impact on memory usage/performance, or if your code does a lot of processing based on their numeric values, then there may be a valid reason to store them as numbers. – JLRishe May 22 '14 at 16:20
  • @JLRishe - I just provided more info. Thanks. – David Thielen May 22 '14 at 16:24
  • @DavidThielen [Please see my answer](http://stackoverflow.com/a/23812923/227299), I think the answer depends on how you're going to use it. I posted an example that seems to indicate you should use a string instead of integers. – Ruan Mendes May 22 '14 at 17:05

4 Answers4

3

There are no chars in JavaScript. You can use 1 character strings. If you wanted to use integers to save memory, you'd have to call String.fromCharCode whenever you needed and that would be creating a String anyway so I find it hard to believe that you would get any benefits from storing integers.

Here's a test where I use an int to stand for 'a' and create a longer string using both strings and the int http://jsperf.com/using-ints-as-chars

Setup code

var x = "a";
var y = 97;

Creating from ints (383 ops/sec)

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

Creating from 1 char strings (592 ops/sec) FASTER

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

If you're trying to emulate enums in JS, here's one simple way, using strings so it's easier to debug. The strings are compared using pointers so there's no performance penalty

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"
}

Ideally, you can't test the enum against a string, if you have a string and you want to compare with an enum, you'd need to convert it into an enum first (to make sure it's one of the valid strings). The following is a safer enum.

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'));

Update

As noted by https://stackoverflow.com/users/1945651/jlrishe, string comparison is not doing using address comparison, like regular Objects, so equality test is going to scan the string. Therefore, the micro optimization would be to use a number instead of a string if you're going to test equality on the string a lot. Note that if you use the "almost-type-safe" enums I showed above, then the equality check would only be for a pointer. See http://jsperf.com/string-comparison-versus-number-comparison and Is JavaScript string comparison just as fast as number comparison?

Community
  • 1
  • 1
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • Based on this line in the question: "There is no need for it to be l/c/r, I just used those to make it easier to read in the debugger" it seems like these are largely just arbitrary placeholders and their actual values are not actually used anywhere in the program except for debugging. +1 for an illuminating analysis though. – JLRishe May 22 '14 at 17:37
  • @JLRishe When using them as enums, make it a string so it's clear to who's reading the code. Added some code about enums, not worrying about performance, but usability. http://jsfiddle.net/2VMBx/2/ – Ruan Mendes May 22 '14 at 22:15
3

Well, the first thing to bear in mind here is that "premature optimization is the root of all evil", and unless you have a legitimate concern that this will impact the efficiency of your application, then this is unlikely to be a bottleneck in its performance.

If your objective is to provide enumeration-like functionality, then I think this is even more reason that number vs. string is unlikely to be an issue, but to further assuage those concerns, if you are referencing them consistently throughout your application, then you can always switch back and forth at any future time to test which is more efficient and then use the one that's best.

As in Java, strings and other objects in JavaScript are stored by reference, so in your situation the memory footprint of a one-character string is largely irrelevant and the question becomes how much a reference to that string costs. I'm unable to find any conclusive information on this, but I would strongly suspect that this is at most 64-bits, as that is the largest address size on typical modern machines.

So the issues on the table are:

  1. Memory usage
  2. Efficiency of comparing two values
  3. Ease of debugging

Strings, numbers, and objects should all be equal on #1 because a number takes up 64-bits, and a reference takes up 64 bits.

On #2 again, they're pretty evenly matched, because we can pretty safely assume that the JavaScript engine designer will do a reference equality check on two strings before comparing their values, so if two variables reference the same instance of a string, this should be comparable to comparing two numbers.

On #3, string has a slight advantage over numbers because you can just print them, whereas you need a conversion step to convert a number to an intelligible value.

So I recommend sticking with strings in the short term and then re-evaluating if you encounter some performance concerns. If you write the code well, it should be easy to switch off. And as mentioned above, if you're using references to a small set of strings, then the actual size of those strings is largely insignificant. So feel free to use "left", "center", "right" as the actual values if that makes your debugging more intelligible.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • I don't agree in point 1: if a number uses 64 bits, a String uses 64 (the pointer) plus at least one bit for each character (it depends on the encoding), plus the size of the fields (length, for example) which are included in the object – Pablo Lozano May 22 '14 at 17:03
  • @PabloLozano I think the more important question is not how much memory it takes. If you plan to use the char code instead of a string, you'll have to end up converting your integers into strings which will make it run slower in most cases. See my answer – Ruan Mendes May 22 '14 at 17:08
  • @JuanMendes I agree if the scenario is that, but I cannot see in the question that a conversion to String is needed in any moment. OP said that Java code was old and enum was not a choice in that moment, so I'm thinking in an equivalent to enum. – Pablo Lozano May 22 '14 at 17:13
  • 1
    @PabloLozano If he is using a set of three predefined values throughout his application, as stated in the question, then the size of those actual values quickly becomes mathematically insignificant if they are reference types. If you have the code `var a = "hello!"; var b = a; var c = a;` then the cost of creating `b` and `c` is the cost of a pointer each, not a pointer plus the size of the string `"hello"`. – JLRishe May 22 '14 at 17:13
  • @JLRishe Yes, in this specific scenario, but in my understanding the answer has a "general scope". Said that, I think this answer is better than mine and the OP chose it because this one wasn't still present ;) – Pablo Lozano May 22 '14 at 17:17
  • @JLRishe I think you got it. If you have to convert them back to strings, then just make them strings. However, if you use them as read only, it won't matter, because you're just testing if two values point to the same string – Ruan Mendes May 22 '14 at 17:19
  • @PabloLozano The OP accepted your answer because you provided a link that seemed to satisfy his desire for high performance. However, the OP didn't ask a very good question, since performance is always dependent on how you use your code. The OP just wanted a rule they can use anytime, without thinking about it, which is never a good thing... Especially when it comes to premature optimization. Don't make your code less readable for optimizations that probably won't matter 99% of the time. – Ruan Mendes May 22 '14 at 17:22
  • @JuanMendes I see. I guess I assumed that using `===` with two string variables could have a performance hit, but I imagine that any decent JavaScript engine is going to do a reference equals check before comparing their values. – JLRishe May 22 '14 at 17:24
  • @JLRishe Strings are immutable, so they are always compared by memory address. However, if you're using the `String` wrapper object, two different wrappers will always point to different objects, not to the string directly, therefore will not compare as equals. `new String('a') === new String('a') // False` – Ruan Mendes May 22 '14 at 22:23
  • @JuanMendes Are you saying that two equivalent strings will always have the same address in JavaScript, or simply that JavaScript will first compare them by address for optimal performance? If I wrote `var a = "h" + "i"; var b = String.fromCharCode(104) + "isle".substring(0, 1);`, are you saying that `a` and `b` would point to the same address? – JLRishe May 23 '14 at 05:09
  • @JLRishe That's a great (humbling) question. I didn't think of that. I know that literal strings in the code go into a string pool and do not change. However, for dynamically created strings, I don't think there's a guarantee there would not be duplicates in memory. I thought it did because I assumed that `===` always checked addresses, as it does for objects, but http://ecma-international.org/ecma-262/5.1/#sec-11.9.6 , 5th bullet, explains that for String, it should compare the characters, not that it points to the same address. – Ruan Mendes May 23 '14 at 17:11
  • @JLRishe When comparing string literals, it's an address test only, when comparing dynamic strings, it scans the string. I'm assuming this from the following performance results http://jsperf.com/string-comparison-versus-object-comparison – Ruan Mendes May 23 '14 at 18:37
0

Unlike java, javascript doesn't have strong typing, so it doesn't matter what type you will use.

please read this: 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

If you need to know the memory usage of each type, you can read all the section 8 of this document 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, 264−253+3) values, representing the double-precision 64-bit
Baptiste Pernet
  • 3,318
  • 22
  • 47
  • 2
    I was with you up until the w3schools bit. How about [this page](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Values,_variables,_and_literals) from the MDN? – Tom Fenech May 22 '14 at 16:18
  • This doesn't really answer the question. OP is asking about the efficiency of using strings vs. numbers to represent character values, and the fact that JavaScript variables are dynamically typed has nothing to do with that. – JLRishe May 22 '14 at 16:21
  • This doesn't prove anything. – Ruan Mendes May 22 '14 at 16:50
  • I don't understand what it means actually, I was just pointing to some resource in case it could help... – Baptiste Pernet May 22 '14 at 16:52
0

As asm.js efficiency boost is based in using numbers all the time, avoiding any other type, I'd say yes, using int is more efficient. Comparing is easier (faster) and it needs less memory.

UPDATE: Here you have a link with performance tips for V8 (Chrome JS engine).

Pablo Lozano
  • 10,122
  • 2
  • 38
  • 59