.

Tags:

threemusketeers
Extracting a portion of a string is a fairly well understood practice. With JavaScript, there are three different built-in functions which can perform that operation. Because of this, often it is very confusing for beginners as to which function should be used. Even worse, sometimes it is easy to fall into the trap and choose the wrong function.

String’s substring (ECMAScript 5.1 Specification Section 15.5.4.15) is the first logical choice to retrieve a part of the string. This substring function can accept two numbers, the start and the end (exclusive) character position, respectively. In case the end value is smaller than the start, substring is smart enough to swap the values before doing the string extraction. An example of substring is illustrated in this code snippet:

var a = 'The Three Musketeers';
a.substring(4, 9);     'Three'
a.substring(9, 4);     'Three'

Many JavaScript environments (including most modern web browsers) also implement a variant of substring called substr (Section B.2.3). However, the parameters for substr are the start character position and the numbers of characters to be extracted, respectively. This is shown in the following fragment:

var b = 'The Three Musketeers';
b.substr(4, 9);     'Three Mus'
b.substr(9, 4);     ' Mus'

This pair of functions, when they are both available, can be really confusing. It is so easy to mistake one for another and thereby leading to an unexpected outcome. It also does not help that the names, substring and substr, are too similar. Without looking at the documentation or the specification, there is a chance of picking a wrong one.

To add more confusion to this mixture, a String object also supports slice (Section 15.5.4.13), just like in Array’s slice. For all intents and purposes, slice has a behavior very close to substring (accepting start and end position). However, there is a minor difference. If the end value is smaller than the start, slice will not internally swap the values. In other words, it follows what is expected for Array’s slice in the same situation and thus it returns an empty string instead.

var c = 'The Three Musketeers';
c.slice(4, 9);       'Three'
c.slice(9, 4);       ''

Each of these three function can accept two parameters and perform the string extraction based on those parameter values. The result however can be different. Again, it is just like in the confusing case of Array methods (see my previous blog post on JavaScript Array: slice vs splice)

When we write our own JavaScript library, how can we minimize such a confusion? The solution is of course to avoid an API which leads to this situation at the first place. Whenever a new public function needs to be introduced, search for existing ones to ensure that there will not be a similar confusion. Of course, it is even better if such a step is enlisted in the API review checklist.

Prevention is the best cure. Be advised of your function name!

  • Jack Moore

    My opinion is that substring’s swapping of parameters violates the principle of least surprise.

    Since it’s such a common use-case, I think it’s worth pointing out that substr and slice can use negative indices to extract from the end of the string, while substring cannot.


    var a = 'The Three Musketeers';
    a.slice(-10);
    // "Musketeers" in all browsers
    a.substr(-10);
    // "Musketeers" in Modern Browsers
    // "The Three Musketeers" in IE8 and lower.

    • Agree on the automatic swapping. JavaScript substring seems to be modelled after Java’s substring. However, with Java, an exception is thrown for a crazy case like end < start.

      • I know I’m being nitpicky here, but you probably mean end = 0, right? … because `someString.slice(2, -2)` doesn’t seem so crazy to me. 🙂

        • It’s about Java’s substring. Whether it’s x.substring(2, 0) or y.substring(2,-2), Java will throw an exception. Very Java-ish, I know 🙂

  • steve j

    “and the amount of characters to be extracted”
    should be

    “and the number of characters to be extracted”

    Normally, I let the grammar/usage errors pass, but the italics pushed me over the edge this time.

  • Greyson Richey

    You made JavaScript Weekly! Unfortunately, their subheading for your article was a little misleading… “Which function should you use when?” That being said, perhaps your article opener was a little misleading as well.

    I’d love to read a thorough explanation and argument on which to use in what common situations.

  • You forgot to point out that substr() is deprecated and so should no longer be used as there is no guarantee that it will continue to be supported in the future.

  • gotofritz

    Another important thing you didn’t point out, and the reason I normally use slice, is that it accepts a negative end arg, which allows one to start counting from the end. Neither substr nor substring allow that.

    "The Three Musketeers".slice(10, -2) //"Musketee"
    "The Three Musketeers".substr(10, -2) //""
    "The Three Musketeers".substring(10, -2) //"The Three "

    • gotofritz

      Oh, I see someone had already pointed that out in the comments

  • medikoo

    I use `slice` only, which as @gotofritz:disqus pointed has other interesting uses, additionally it matches Array’s `slice` behavior. Less is better

  • @ariya:disqus good post. I made a fiddle for this article, maybe someone need. here is the link: http://jsfiddle.net/canvast/pQ6PZ/

    • Boom Lee

      Thanks, very nice~ !

  • Tiago Celestino

    It’s crazy for me mind. lol Nice article.

  • Nice!