6

I am doing some exercises in my object-oriented javascript book, I notice that this:

var a = "hello";
a.charAt('e'); // 'h'
a.charAt('adfadf'); //'h'

Why is the string in the argument seemingly evaluated to the integer 0 for the charAt() method for strings?

Edit: I was aware that the charAt()'s usage usually takes an integer, and the exercise feeds charAt() with a string, and I also was aware that the string is likely then to be coerced into an integer first, which I did verify to be NaN. Thanks Kendall, for suggesting putting this missing bit of information in the question proper

Thanks!

Nik So
  • 16,683
  • 21
  • 74
  • 108
  • https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/charAt – j08691 Apr 02 '12 at 17:06
  • Kendall, I was expecting error. just as j08691 suggests, I did go through some documentation for the CharAt() and I wanted to make sure it takes an integer and only an integer. So I thought feeding non-integer will throw an error.. Or at least some sort of parseInt() will first kick into coerce the string, which I knew to be NaN, but I didn't expect NaN to mean 0 – Nik So Apr 02 '12 at 17:10
  • 2
    It would have helped to say that in the first place. – Kendall Frey Apr 02 '12 at 17:11
  • be aware this quirk is very likely browser specific and code should not rely on this behavior... – jbabey Apr 02 '12 at 17:18
  • @jbabey what "quirk?" The behavior the OP describes? No, that is not "very likely browser specific" _at all_ unless the browser does not correctly implement the ECMA-262 spec. – Matt Ball Apr 02 '12 at 17:22
  • @MДΓΓБДLL you are right, this has no ties to the w3 DOM spec and should not be browser dependent. – jbabey Apr 02 '12 at 17:32

8 Answers8

15

Because Number('e') is NaN, and <any nonempty string>.charAt(NaN) just returns the first character. This behavior is exactly what is laid out in the spec:

15.5.4.4 String.prototype.charAt (pos)

When the charAt method is called with one argument pos, 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 position be ToInteger(pos).
  4. Let size be the number of characters in S.
  5. If position < 0 or position ≥ size, return the empty String.
  6. Return a String of length 1, containing one character from S, namely the character at position position, where the first (leftmost) character in S is considered to be at position 0, the next one at position 1, and so on.

Step 3 is the crux of the matter. ToInteger of both 'e' and 'adfadf' is 0. Why? Again, time to hit the spec:

9.4 ToInteger

The abstract operation ToInteger converts its argument to an integral numeric value. This abstract operation functions as follows:

  1. Let number be the result of calling ToNumber on the input argument.
  2. If number is NaN, return +0.
  3. If number is +0, -0, +∞, or −∞, return number.
  4. Return the result of computing sign(number) × floor(abs(number)).

We need to go deeper! What is ToNumber('e'), and what is ToNumber('adfadf')? If you're surprised that I'm once again about to quote the spec, I'm doing something wrong:

9.3.1 ToNumber Applied to the String Type

ToNumber applied to Strings applies the following grammar to the input String. If the grammar cannot interpret the String as an expansion of StringNumericLiteral, then the result of ToNumber is NaN.

...I'm not going to quote the entire grammar for StringNumericLiteral. Because 'e' and 'adfadf' are neither StrDecimalLiteral s nor HexIntegerLiteral s, ToNumber of both of those values is NaN. Finally we have the conversion: from string to NaN to 0, which brings us back up the chain to charAt: position is 0, so charAt('e') and charAt('adfadf') both return the leftmost character in S.

Now, if those strings were instead valid StrNumericLiteral s, such as '0xe' and '0xadfadf':

> 'hello'.charAt('0xe')
  ""
> 'hello'.charAt('0xadfadf')
  ""

well, that's a different story for a different answer.

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • There's something here that confuses me; you are saying that if either argument in `x.substring` is NaN or negative, it will be replaced with zero. So when you call `'some string'.charAt(NaN)` it calls `'some string'.substring(NaN, NaN + 1)` which should be the same as `'some string'.substring(0, 0)` according to the specification. But that evaluates to the empty string `''`. How come it returns the first character? – hattenn Jul 02 '13 at 21:30
  • @hattenn I think the `NaN` to `0` conversion happens at the `charAt()` call, not at the `substring` call, so we get `charAt(NaN)` → `charAt(0)` → `substring(0, 1)` (and **not** `charAt(NaN)` → `substring(NaN, NaN)` → `substring(0, 0)`. I should rephrase this answer to be clearer, since it definitely suggests that the latter is the case, when clearly it is not. – Matt Ball Jul 02 '13 at 21:39
  • That's what I thought too @Matt, thanks for the quick update. – hattenn Jul 02 '13 at 21:40
  • @hattenn pretty much rewrote the answer! Please let me know if that makes more sense to you. – Matt Ball Jul 03 '13 at 03:16
4

The JavaScript core string.charAt(idx) method takes an integer argument as the index for which character to return.

'abc'.charAt(0); // => 'a'

If you give it a non-integer argument then it will presumably attempt to convert the argument to a number using either the Number(arg) converter or possibly parseInt(arg, 10). Both of these functions return NaN if given a string that doesn't parse as an integer, so the charAt() function must convert NaN to zero automatically:

Number('e'); // => NaN
parseInt('e', 10); // => NaN
'abc'.charAt(NaN); // => 'a'

Perhaps directly related, calling charAt() with no argument returns the first character:

'abc'.charAt(); // => 'a'
maerics
  • 151,642
  • 46
  • 269
  • 291
  • Thanks Maerics, it's about the same as MДΓΓ БДLL's answer, so I just went with time-order. And I didn't downvote, don't know who.. I upvoted actually – Nik So Apr 02 '12 at 17:12
  • 1
    @Nik: no worries. Keep in mind that you can up- (or down-) vote as many answers as you like (Matt's refers to the core specification so it's just as valid/helpful as mine despite amounting to the same information). Downvoting is best done sparingly, e.g. if the answer is really unhelpful, offtopic, etc. – maerics Apr 02 '12 at 17:16
3

When you call String.charAt(pos), it first evaluates the toInteger value of pos, and since you gave it 'e', that evaluates to 0, resulting in your answer. For more information, see below:


According to http://ecma262-5.com/ELS5_HTML.htm#Section_15.5.4.4

String.prototype.charAt (pos)

Returns a String containing the character at position pos in the String resulting from converting this object to a String.

If pos is a value of Number type that is an integer, then the result of x.charAt(pos) is equal to the result of x.substring(pos, pos+1).

When the charAt method is called with one argument pos, 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 position be ToInteger(pos).

  3. Let size be the number of characters in S.
  4. If position < 0 or position ≥ size, return the empty String.

According to http://ecma262-5.com/ELS5_HTML.htm#Section_9.4

9.4 ToInteger

The abstract operation ToInteger converts its argument to an integral numeric value. This abstract operation functions as follows:

  1. Let number be the result of calling ToNumber on the input argument.

    2. If number is NaN, return +0.

  2. If number is +0, −0, +∞ or –∞, return number.
  3. Return the result of computing sign(number) * floor(abs(number)).
K-man
  • 353
  • 1
  • 13
2

The charAt() method takes an integer as its parameter. Passing a string will evaluate to NaN, false, 0. Therefore, the first character in the string will always be returned.

Joe
  • 6,401
  • 3
  • 28
  • 32
1

Because charAt tries to convert what you give it to a number. Converting "adfadf" to a number gives you NaN, or Not A Number. The defined behavior for charAt with NaN is to return the first character.

Alex Turpin
  • 46,743
  • 23
  • 113
  • 145
1

Both 'e' and 'adfadf' are strings. Strings when are inputed as integer values are always returning 0 as a value.

Since "hello" is an array of chars itself, it will return you the first index which is 'h' (starting from 0).

Panagiotis
  • 1,539
  • 1
  • 14
  • 28
0

The charAt function takes an integer, not a string, as an argument:

"hello".charAt(0) // 'h'
"hello".charAt(1) // 'e'
"hello".charAt(2) // 'l'

And when you give it a string, it basically ends up interpreting it as a 0 and returns the first character.

nicolaskruchten
  • 26,384
  • 8
  • 83
  • 101
-1

Friend charAt method is like

string.charAt(index)

i.e. you need to pass a index to method

if you want to do same thing than use code like

string.charAt(string.indexOf('a'));

hope this will help you.

Yogesh Prajapati
  • 4,770
  • 2
  • 36
  • 77
  • 2
    This is rather useless, don't you think? You already know what char is at `string.indexOf('a')`...it'd be `'a'`, by definition. – cHao Apr 02 '12 at 17:18