6

I know what the difference is between substr(start[, length]) and substring(start[, end]). This is addressed e.g. in this earlier question: What is the difference between substr and substring?

But why did they add substr later when substring was already present? I am looking for information about the history of this decision.

I have found that substring was there from the beginning and then 'suddenly' in Ecmascript 3rd edition they have two different functions to achieve the exact same thing with different arguments. Why?

Community
  • 1
  • 1
David Mulder
  • 24,033
  • 8
  • 45
  • 104
  • 1
    They aren't the same, see this answer: http://stackoverflow.com/a/3745518/2449905 – Dan Lowe Sep 20 '15 at 13:08
  • 2
    backwards compatibility? – baao Sep 20 '15 at 13:10
  • @DanLowe Did you read the first 7 words of my question? – David Mulder Sep 20 '15 at 13:13
  • @michael Backwards compatibility with what? – David Mulder Sep 20 '15 at 13:13
  • 4
    I don't understand the question, There are lots of functions which have same functionality in almost all programming languages. Should we ask about all of them in SO? By the way, all answers here are opinion based. – masoud Sep 20 '15 at 13:31
  • 5
    This question is under discussion on meta [Are language specs and their developments on topic?](http://meta.stackoverflow.com/q/306471) – DavidPostill Sep 20 '15 at 13:48
  • 2
    If you are accepting they are different (which you have done by posting the relevant link) I fail to see the point of your question in `But why did they add substr later when substring was already present?`. That would make sense if you are saying they are the same - which you are not. I feel like you are trying to get around saying they do similar things and are caught up on that fact. – IfTrue Sep 22 '15 at 12:27
  • @IfTrue I said from the first version that I knew the difference '-_- But why don't we then have 10 different versions of for example `String.replace`, one for regexes, one only for strings, one that is global, one which accepts the replacement string before the text, etc. Very simple reason: Because it's hard to remember, hard to comprehend and hard to write (especially if you call them `replace`, `repl`, `rePlace`, etc., like with `substring` and `substr`). – David Mulder Sep 22 '15 at 16:00
  • @DavidMulder I propse you edit the question to make clear mention that your question is more about the similarity in the names themselves. Your question as it stands makes no mentions that you are curious because of the similar names and then is even more misleading when it states `they have two different functions to achieve the exact same thing with different arguments. ` even though you are repeating you know their differences. The key here is THEY CAN produce the same thing given DIFFERENT ARGUMENTS but so can function(){ return 1 + 1;} and function(){ return 4 - 2;}. – IfTrue Sep 22 '15 at 16:07

5 Answers5

7

Use cases for the 2 functions are different.

String.prototype.substr() lets you take a substring from the end of the string using negative start value which makes it suitable for some more use cases without generating overly verbose, unreadable code.

// alert('1234567890'.substring('1234567890'.length - 4)) <-- UGLY
alert('1234567890'.substr(-4))

By that alone it makes the assumption in the question that the functions achieve the exact same thing simply wrong, and that makes it no different then asking why any other function exist. The only people who can answer such a question are the ones who made that decision.

Phantômaxx
  • 36,442
  • 21
  • 78
  • 108
Amit
  • 41,690
  • 8
  • 66
  • 101
  • 1
    @epascarello - of course I did, perhaps I didn't understand though, care to elaborate? – Amit Sep 20 '15 at 13:12
  • 8
    OP knows they are different, wants to know why there is two methods that do basically the same thing. OP is not asking how they are different. – epascarello Sep 20 '15 at 13:13
  • 1
    Exactly that, if you would have read the first 7 words of the question you would have know that this has nothing to do with answering the question... – David Mulder Sep 20 '15 at 13:14
  • 8
    @DavidMulder - so just to clarify, is your question equal to "Why is there Math.sin & Math.cos"? since you can get the same result with both, just using different arguments? – Amit Sep 20 '15 at 13:15
  • 2
    @Amit That indeed is quite an interesting question as well. It can be somewhat mathematically justified as representing different geometrical concepts, but yeah, I would have to delve into the history of mathematics to find out the exact reason. Right now I care however about this duplication and not that other fields have their own problems... – David Mulder Sep 20 '15 at 13:23
  • 1
    @DavidMulder look at the edited snippet. I think that shines light to why it makes perfect sense to have that flexibility. – Amit Sep 20 '15 at 13:25
  • @Amit The answer doesn't state anything about historical reasoning... if it would be just to add that behaviour then a more directed function would have made more sense: after all, having two functions with virtually the same name and different arguments is terrible language design. – David Mulder Sep 20 '15 at 13:26
  • 1
    @DavidMulder - We're going to have to agree to disagree regarding the usage or design of the language. I do however agree that my answer has no historical information in it, but if your interested is purely in history, you've come to the wrong place - My answer shows why your assumption that the *functions achieve the exact same thing* is wrong. – Amit Sep 20 '15 at 13:31
  • +1. It's probably worth noting that Perl's `substr` already had this support for negative indices; JavaScript probably took it from there (or from another language with the same thing). – ruakh Sep 22 '15 at 20:03
5

I don't think we can definitively answer this without someone involved with ECMA ten years ago weighing in, however we can infer some things.

substring asks for the index (from 0) of the start, and optionally an index (again from 0) of the end.

substr asks for the index (from 0) of the start, and a length. That makes it simpler to use for some cases, e.g. if you want to fetch 2 characters from the first "s" you can just specify 2 as the length instead of having to first calculate the index of the first "s" and then adding 2 to that, and then passing both positions to substring.

As was pointed out in a comment, substr allows a negative starting position.

Arguably, substr is a better implementation. And so that is quite possibly why it was added.

As to the question of why are both there, that is probably for backward compatibility. Because Javascript runs in a wide variety of browsers (and these days elsewhere), in runtimes maintained by multiple organizations, there is no easy way to deprecate or eliminate anything. It's easier to just leave it there and add more to the language instead. Old code still works, and new code has better options available to use.

Dan Lowe
  • 37,115
  • 15
  • 104
  • 98
  • 1
    5 years ago I got irritated by the reaction to this question, so I didn't react to any of the questions later and just now got a badge for it so looked at it again and I just wanted to acknowledge that you got closest to actually replying to my question and I wanted to thank you for at least trying :) . – David Mulder Dec 07 '20 at 14:51
  • Years later, I still Identify with OP's sense that his question was not fully addressed. No-one picks up on his dissatisfaction with the addition of a method whose name fails to distinguish its behavior from the one it supersedes. It also frustrates me that the method names don't help me remember which is which. That said, I'd guess a couple of reasons: (1) Consistency with other languages: substring() comes from java; substr() from C++, and (2) I'd be willing to bet the ECMA committee got a chuckle out of the fact that "substr" is a substring of "substring". :) – Jeremy Bull Feb 26 '21 at 20:41
2

As told, they are not the same, they just can do some operations in common. In fact, their internal steps to complete the work are also different from each other. Here are some quotes from Ecma International's site:

B.2.3 String.prototype.substr (start, length)

The substr method takes two arguments, start and length, and returns a substring of the result of converting the this object to a String, starting from character position start and running for length characters (or through the end of the String if length is undefined). If start is negative, it is treated as (sourceLength+start) where sourceLength is the length of the String. The result is a String value, not a String object. The following steps are taken:

  1. Call ToString, giving it the this value as its argument.
  2. Call ToInteger(start).
  3. If length is undefined, use +∞; otherwise call ToInteger(length).
  4. Compute the number of characters in Result(1).
  5. If Result(2) is positive or zero, use Result(2); else use max(Result(4)+Result(2),0).
  6. Compute min(max(Result(3),0), Result(4)–Result(5)).
  7. If Result(6) ≤ 0, return the empty String “”.
  8. Return a String containing Result(6) consecutive characters from Result(1) beginning with the character at position Result(5).

The length property of the substr method is 2.

NOTE: The substr function is intentionally generic; it does not require that its this value be a String object. Therefore it can be transferred to other kinds of objects for use as a method.

and

15.5.4.15 String.prototype.substring (start, end)

The substring method takes two arguments, start and end, and returns a substring of the result of converting this object to a String, starting from character position start and running to, but not including, character position end of the String (or through the end of the String is end is undefined). The result is a String value, not a String object.

If either argument is NaN or negative, it is replaced with zero; if either argument is larger than the length of the String, it is replaced with the length of the String.

If start is larger than end, they are swapped.

The following steps are taken:

  1. Call CheckObjectCoercible passing the this value as its argument.
  2. Let S be the result of calling ToString, giving it the this value as its argument.
  3. Let len be the number of characters in S.
  4. Let intStart be ToInteger(start).
  5. If end is undefined, let intEnd be len; else let intEnd be ToInteger(end).
  6. Let finalStart be min(max(intStart, 0), len).
  7. Let finalEnd be min(max(intEnd, 0), len).
  8. Let from be min(finalStart, finalEnd).
  9. Let to be max(finalStart, finalEnd).
  10. Return a String whose length is to - from, containing characters from S, namely the characters with indices from through to −1, in ascending order.

The length property of the substring method is 2.

NOTE: The substring function is intentionally generic; it does not require that its this value be a String object. Therefore, it can be transferred to other kinds of objects for use as a method.

So bottom line is neither they are the same methods, nor their usages are the same.

Gökay Gürcan
  • 1,062
  • 1
  • 10
  • 25
  • So, why was this downvote? Did I say something wrong or what? – Gökay Gürcan Sep 22 '15 at 22:00
  • 1
    You did nothing wrong, the OP just has a distinct misunderstanding as witnessed by everyone who has tried explaining to him the premise of his question is illogical. Look at the edit history - he rolls back the question even after moderators agree he contradicts himself. At this point he seems pretty stuck on it so I personally think the best course of action is to just downvote his question because as it stands it makes no sense. – IfTrue Sep 24 '15 at 12:20
  • Ah, thanks for this brief explanation. I'll check my answer and I'll either update it to match with the question or completely remove if it won't fit here. Thanks again. – Gökay Gürcan Sep 24 '15 at 12:44
  • I think it fits - whether the OP can accept what everyone is telling him - or rephrase his question is another story. – IfTrue Sep 24 '15 at 12:46
  • @IfTrue I don't see any moderators agreeing that the OP contradicted himself (where was the contradiction? Where was the comment by moderators?) and I think he was entirely right to roll back your edit. Your edit implied that the names of the functions was key to the question, when as far as I can see they're irrelevant; all that matters is that there are two functions that can be used to achieve the same thing. – Mark Amery Sep 29 '15 at 12:45
  • @GökayGürcan This answer didn't answer the historical context about which this question was asking. On meta the answers to this question where used as an example why questions like mine are bad, as even when one asks about history you still end up with developers giving technical responses. The first line of this question made it clear that I was aware of the difference in argument format, so copying the spec affirming something nobody asked about is just a very bad and irrelevant answer. – David Mulder Dec 07 '20 at 14:49
-1

substr allows extracting sub-string from the end like: "Good news, everyone!".substr(-4); will return one!. (Not supported by IE8- though). And since JavaScript doesn't natively support Function Overloading. So my theory is that this feature of extracting sub-string from the end was demanded heavily by the community and hence they introduced similar function with another name with a little change. I don't know what was the real reason but according to me the absence of native support for function overloading may caused the birth of this substr function.

Aishwarya Shiva
  • 3,416
  • 10
  • 48
  • 96
-1

Why? Because convenience.

Presumably. There's no way to know for sure without asking the original authors of this decision.

But we can only assume that it's based on reasoning similar to that which led to having e.g. functions Left and Right in Visual Basic, even though identical results can be obtained using the Mid function with a different combination of arguments, albeit less conveniently and with more or longer lines of code. (Or, pick own analogy in own favorite language.)

Fox example, using the length argument of substr instead of the end argument of substring can lead to some mild readability/conciseness gains:

var test = "First Second Last";
//If you want "Second", do either of:
alert(test.substring(test.indexOf("Second"), test.indexOf("Second") + 'Second'.length)) 
alert(test.substr(test.indexOf("Second"), "Second".length)) //<------ WOW!! ---------->

Alternatively, when using substring, you could store test.indexOf("Second") in a temp variable to shorten the substring call:

var n = test.indexOf("Second");
alert(test.substring(n, n + 'Second'.length)) 

but that's one more line of code and one more temp that you don't need when using substr.

Bottom line: It's really no big deal! They added one more function for slightly more convenience.

Jean-François Corbett
  • 34,562
  • 26
  • 126
  • 176
  • Considering `substring` didn't accept negative values within this conjecture it doesn't make sense they didn't then extend the spec with allowing `substring` with negative values. After all, that would have only broken little to no code and prevented function duplicatoin. Additionally `Left` and `Right` have clearly different meanings even if they both return a sub-string, on the other hand in JS we have to functions named 'sub string' but with different arguments. – David Mulder Sep 22 '15 at 11:20
  • I can update my answer to address this. But I think we're really splitting hairs here. – Jean-François Corbett Sep 22 '15 at 12:08