Why is String.prototype.substr() deprecated?
Asked Answered
R

4

159

It is mentioned on the ECMAScript standard here that :

... These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.

There is also a red warning on MDN : String.prototype.substr() MDN doc

Does anyone know why (ECMAScript standard say that) programmers should not use or assume the existence of String.prototype.substr ?

Retrieve answered 4/10, 2018 at 6:21 Comment(8)
I suspect substring is intended to replace it - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… - substring takes a start and end position which is more conventional than start + number of characters.Flabbergast
Don't know, but one reason why I'd avoid it is because the .substring method sounds very similar, but uses end index as a second parameter rather than length. Easy to mix up, probably, better to just use one method, if possibleUveitis
@Flabbergast - "Replace" is an odd term here, as substring has been in the standardized language since there was one, but substr never has. :-)Miscible
So, why not add substr to the standards, since many prefer using substr over substring? (arguing that using a length can be easier than using an index)Coronograph
Replace substr by slice. Works identical with strings.Pfeifer
@Flabbergast That's not my experience at all. Usually I know in advance the length the substring that I want. For example, if I have a date like '03-Mar-2022', I know that the month is always a fixed 3 characters long, so from a code readability and maintenance perspective, specifying "3" for the substring length is way more sensible than having to specify an ending index.Mussel
slice != substr....> "12345678".substr( -2 , 2) '78' vs "12345678".slice( -2 , 2) '' Iy
In addition to what @Iy said, it appears to me that the second parameter of slice is not a length, but an index. That being the case, slice is identical to substring (and not substr) for strings.Evertor
M
127

Because it's never been part of the standardized language. It wasn't in the ECMAScript 1 or 2 specs at all, and only appears in ECMAScript 3 in Section B.2 ("Additional Properties") (and subsequent editions in similar annexes through to today [ES2022 draft as of this writing]), which said:¹

Some implementations of ECMAScript have included additional properties for some of the standard native objects. This non-normative annex suggests uniform semantics for such properties without making the properties or their semantics part of this standard.

Moreover, substr is largely redundant with substring and slice, but the second argument has a different meaning.

In pragmatic terms, I'd be surprised if you found a full mainstream JavaScript engine that didn't provide it; but I wouldn't be surprised if JavaScript engines targeted at embedded/constrained environments use didn't provide it.


¹ That wording has changed more recently to:

The ECMAScript language syntax and semantics defined in this annex are required when the ECMAScript host is a web browser. The content of this annex is normative but optional if the ECMAScript host is not a web browser.


NOTE This annex describes various legacy features and other characteristics of web browser ECMAScript hosts. All of the language features and behaviours specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification. However, the usage of these features by large numbers of existing web pages means that web browsers must continue to support them. The specifications in this annex define the requirements for interoperable implementations of these legacy features.

These features are not considered part of the core ECMAScript language. Programmers should not use or assume the existence of these features and behaviours when writing new ECMAScript code. ECMAScript implementations are discouraged from implementing these features unless the implementation is part of a web browser or is required to run the same legacy ECMAScript code that web browsers encounter.


Miscible answered 4/10, 2018 at 6:27 Comment(5)
Is there any reason why deprecating it is better than adding it to the standardized language? If one knows the desired length of the new string, foo.substr(startPos, newLen); seems cleaner than foo.substring(startPos, startPos+newLen);; that would be especially true of startPos were a more complicated expression.Haymaker
@Haymaker - It isn't deprecated, it's never been part of the standard library in the first place. As for whether it would be better to just put it in the standard library, well, that's a judgement call for TC39. Since TC39 has no policy on the standard library (e.g., should it be large or small)... (I asked once. The answer is: They can't agree on a direction, so it's all case-by-case and [externally] which way the wind is blowing and whether it's a Tuesday or not. :-) )Miscible
Uncertainty of direction is a major problem of design by committee. It's especially bad with C, where it's unclear whether the Standard is trying to define a core language upon which dialects can be built that are maximally suitable for a wide range of individual purposes, or to define a language that includes everything necessary to serve a majority of use cases (IMHO, the authors of the C89 rationale make it very clear they intended the former, but compiler writers treat the C Standard as the latter). IMHO, people writing language standards need to either recognize current practices...Haymaker
...or explicitly state that responsibility for doing so lies with implementations, or else perhaps recognize what current practices are while and indicate that quality implementations should uphold practices when doing so would be useful and practical, but that doing so is not required for conformance if, in an implementer's judgment, the cost of upholding a particular practice would exceed the benefits thereof.Haymaker
I much prefer substr() over substring() for most of my work - it would be nice if this was just codified into the language. It would also be nice if MDN indicated that it was technically non-standard vs. deprecated. Deprecated fully suggests that it was valid at some point. Grep indicates a mere 9,036 references to .substr( in my code... yeah this needs codification of the Cow Path.Offwhite
B
24

The main advantage of substr is that you can specify a negative start position! To do the same with substring is awful.

Barty answered 23/3, 2022 at 9:15 Comment(3)
.slice supports negative start (and end) positions.Frontwards
@NicoleAshley and now we have 2 functions doing almost the same thing (slice and substring), but not exactly. Yeeyyy! I will continue with substr if no one cares. Also, this "answer" should be a comment because it does not answer the question ^^'Dicephalous
Can I go arrrrrrgh!!!!... on this?Candancecandela
C
9

I've added this declaration as an ambient declaration in a top-level ambient.d.ts file:

interface String {
  /**
   * Gets a substring beginning at the specified location and having the specified length.
   * (deprecation removed)
   * @param from The starting position of the desired substring. The index of the first character in the string is zero.
   * @param length The number of characters to include in the returned substring.
   */
  substr(from: number, length?: number): string;
}

I find substr very useful. It's often a lot more concise to specify how long you want a string to be rather than the end index of the string. If substr really ever is removed from browser or Node.js JavaScript support, I suspect many of us will simply monkey-patch substr back into existence anyway.

Chancellery answered 13/2, 2022 at 19:46 Comment(1)
While I support your notion for monkey-patching it back in I'd not recommend your approach without a polyfill to jump into place in case .substr is suddenly not supported anymore. Simply "hiding" the deprecation notice is just reckless.Tho
H
0

If substr will be dropped in the future, then you can use this replacement, instead of rewriting all your JavaScript codes. It is written according to ECMA specification: https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-string.prototype.substr

B.2.2.1 String.prototype.substr ( start, length ) This method returns a substring of the result of converting the this value to a String,
starting from index start and running for length code units (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. It performs the following steps when called:

  1. Let O be ? RequireObjectCoercible(this value).
  2. Let S be ? ToString(O).
  3. Let size be the length of S.
  4. Let intStart be ? ToIntegerOrInfinity(start).
  5. If intStart = -∞, set intStart to 0.
  6. Else if intStart < 0, set intStart to max(size + intStart, 0).
  7. Else, set intStart to min(intStart, size).
  8. If length is undefined, let intLength be size; otherwise let intLength be ? ToIntegerOrInfinity(length).
  9. Set intLength to the result of clamping intLength between 0 and size.
  10. Let intEnd be min(intStart + intLength, size).
  11. Return the substring of S from intStart to intEnd.
String.prototype.substr = function (start, length)
{
    const S = this.toString(); // step 1 and 2
    const size = S.length; // step 3
    let intStart = Number.isNaN(Number(start)) ? 0 : Number.parseInt(start); // step 4
    if (intStart === -Infinity) intStart = 0; // step 5
    else if (intStart < 0) intStart = Math.max(size + intStart, 0); // step 6
    else intStart = Math.min(intStart, size); // step 7
    let intLength = length === undefined ? size : (Number.isNaN(Number(length)) ? 0 : Number.parseInt(length)); // step 8
    intLength = Math.max(Math.min(intLength, size), 0); // step 9
    let intEnd = Math.min(intStart + intLength, size); // step 10
    return S.substring(intStart, intEnd); // step 11
};

.. and tested using this script (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr):

const aString = "Mozilla";

console.log(aString.substr(0, 1)); // 'M'
console.log(aString.substr(1, 0)); // ''
console.log(aString.substr(-1, 1)); // 'a'
console.log(aString.substr(1, -1)); // ''
console.log(aString.substr(-3)); // 'lla'
console.log(aString.substr(1)); // 'ozilla'
console.log(aString.substr(-20, 2)); // 'Mo'
console.log(aString.substr(20, 2)); // ''
Hobble answered 25/1, 2024 at 20:26 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.