How does changing the prototype of the String.prototype.toString() method affect this code in Javascript?

Asked

Viewed 144 times

6

I couldn’t understand why the method reverse is applied in the string "abcde" (over-written in toString) and not in "12345".

String.prototype.reverse = function() {
    return this.split('').reverse().join('');
}

String.prototype.toString = function() {
    return 'abcde';
}

console.log('12345'.reverse());

  • 5

    Ah, Javascript is full of surprises! But it is for this and others that it is not recommended to change the prototype of a native type in Javascript.

  • I gave +1. I find the doubt valid.

  • I did some tests. Apparently, this problem only occurs with new methods added to String.prototype. I don’t know why, but within the scope of the method created via prototype, is considered the toString modified instead of original value.

  • 3
  • 1

    @Wallacemaxters, I’m on your treadmill. But not to the point of giving up (just taking the down) this overwriting of methods in the chain of prototypes of native objects gives me chills.

  • 1

    I understand that this overwriting is not something that should be done, because it can affect the code in unpredictable ways, as it happened. But I see no problem exploring the language and increasing knowledge about how it operates.

  • If you’re curious, I don’t have a problem. As we receive questions from users with varying level of knowledge it is difficult to know if the user is only playing with the language or if he takes the concept for granted. If that’s all you take my +1.

  • 2
Show 3 more comments

2 answers

9

This happens because, according to the specification, the method String.prototype.split converts the value this to string using the operation ToString. According to step 3 of the algorithm in question:

Let s be ? Tostring(The).

That one ToString (note the capital letter - it is not the same thing as toString) is an abstract operation that ends up, depending on the type of the parameter, calling the implementation toString. In the case of objects, the abstract operation ToString will end up running the implementation toString of this used.

In your case, the this is an object because, when you pass the this within its implementation of String.prototype.reverse, the this (that era a primitive string) is being converted to the object String correspondent.

This conversion made by the algorithm may become "useful" when you pass a non-string like the this of split, but which implements toString. An example:

const result = String.prototype.split.call(
  { // Objeto que implementa `toString` ("argumento" `this` de `split`).
    toString() {
      return '1-2-3';
    }
  },
  '-' // Separador (1º argumento de `split`).
);

console.log(result); //=> ['1', '2', '3']

I don’t know why anyone would do something like that. I think the need for this behavior is more intrinsically linked to the nature of Javascript’s automatic coercion. Finally, the specification itself ensures this genericity for the method split:

The split 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.

In free translation: The function split is intentionally generic; it does not require its value this is a String object. Thus, it can be transferred to other object types for use as a method.

Strict way?

Another variant to make Javascript even more confusing! :')

Note that the conversion to the object in this is only done in the "normal mode" of Javascript execution. If the code is running on strictly speaking, this behavior nay will occur and the split use the primitive value of this (therefore, the over-written implementation of toString will not be taken into account). See your code running in strict mode:

'use strict';

String.prototype.reverse = function() {
  // Como está rodando em modo estrito, este `this`
  // não será convertido em objeto e, portanto,
  // o valor literal será utilizado pelo método `split`.
  return this.split('').reverse().join('');
}

String.prototype.toString = function() {
  return 'abcde';
}

console.log('12345'.reverse()); // Utilizou `12345`. Saída: `54321`.


Javascript is confusing and this situation is just another proof of this.

4

Every object can be converted to a textual representation of it. This is done with the function toString(). All types have this, without exception.

You may be thinking, but a guy String, is already a string, then there is no need to convert. It may be, but the function is present to deliver the textual representation of the object. Even because the way this string is organized internally in the object is not your problem, is object exclusive problem, is implementation detail, only function toString() ensures that it will deliver a textual representation of something, only it allows you to display a text of an object, including on object of type String.

And you can imagine that in many situations this function toString() is implicitly called to get the desired result, for example in console.log() that its function is to print the textual representation of an object. This function does not print implementation details of any object, only the textual representation, even a number, is not printing the number only the text that represents that number. There are other situations that this function is called, and in Javascript this is quite confusing, but it does not come to the case here.

Whenever picking up the value of the object where a text is expected the function will be called. reverse() expects a text, so in practice what’s happening there in the code is roughly:

console.log('Escola Cod3r'.toString().reverse());

The same way I’d have to do this:

console.log(123..toString().reverse());

Note that JS tends to want to turn a lot of the objects into text, this can be confusing.

And it is the result that is obtained. There is nothing extraordinary, this is the right to do for the sake of keeping everything linear, IE, why treat the type String how special? If everything needs to be so, so be it.

We have another fan of no-for-a-period who will suffer from the JS confusions.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.