Is this uncharted territory or I am too tired right now for deep googling?
(First published Aug 7 2011)
1 2 3 4 5 6 7 8 9 |
/* string indexing DBJ.ORG 06AUG11 */ dbj = { "string_indexing": "ABC" [0] === "A" }; alert(dbj.string_indexing); /* in V8 (Chrome) yields true */ |
“[]” operator on a string literal. I could not find any mention of it in ECMA spec. I think it is a “good thing”, why not. But is it standard? Or just an “De-facto” standard?
1 2 3 4 |
var s = "ABC"; s[2]="!"; alert(s) // this shows "ABC" on Chrome |
Above does not throw an error in Chrome. Does this mean it is OK to do it, but it has no effect on string. Or does this mean Chrome team has to fix this, and throw an error?
1 2 |
alert("ABC"[2] = "!"); // shows "!" |
Above behaves OK, and shows the result of the assignment. But assignment has not been made. This is because there is nothing to assign to , as string literal my friends is not a variable. But even it we are dealing with variable containing a string, as we see from previous example string variable remains unchanged, after Chrome V8 quietly goes over assignment to the result of using the [ ] operator.
Most likely adding [ ] operator to string literal, provokes V8 javascript interpreter (and most of the others like IE9 ‘Chakra‘) to create a temporary array object, made from slicing the original string ‘host’ into an array?
1 2 3 4 5 6 7 8 |
var s = "ABC"[2] = "!"; // this is what is happening behind a scene temporary = "ABC"; // do whatever You can, to the string literal Array.prototype.call.slice("ABC")[2] = "!"; // the result of string literal change is lost var s = temporary ; // |
If this is true, is this bug or feature, in V8 Chrome and/or elsewhere? It seems it was decided (by ECMA comitee and/or V8 team) as logical, that one can do things to string literals, without exceptions being thrown, but no changes will be made to the string literal (because it is a literal not a variable) .
I am not sure what is logical for You, but this or following not throwing an exception, does not strike me as logical:
1 2 3 |
// Chrome does not throw an exception here alert(String.fromCharCode(undefined)); // |
Am I missing something in here?
Update 2013 Apr
Two years later and we have the same puzzle. In both IE10 and the latest Chrome, we still can use [] aka “indexing operator” to get to characters in strings. But, assignment using the same operator silently fails.
1 2 |
c = "ABC"[1] ; /* leaves c with value 'B' */ s = "ABC"[1] = "+" ; /* SILENT FAILURE: leaves s with value "ABC" */ |
Silent failure is a bad thing. We can not have this left in or promoted to ES6. This issue can be and should be immediately rectified by IE and WebKit team (and yes the Blink team too).
1 2 |
"X" = "Z" ; /* throws: "ReferenceError: Invalid left-hand side in assignment" */ "ABC"[1] = "+" ; /* also must throw: "ReferenceError: Invalid left-hand side in assignment" */ |
NOTE: same issue applies to both string literals and String objects.
7 thoughts on “string [ ] operator behaves badly. JavaScript”
I *think* you’re supposed to use charAt. Pretty lame if this is not in the spec, but the way I’d understand it is that [Number] is only on arrays and strings are not true arrays. Still…
I think some kind of [un]documented ES5 behaviour is going on here. Above works the same in IE9 too.
Also, to me
seems “nicer”, “logical” and “simpler” (all subjective) , than
Whatever is the personal preference, this seems not documented at all?
@kangax : Thanks Juriy. Two observations if I may?
1. Source:
Can you please point us into the direction of the ECMA spec paragraph, which defines/mentions string [ ] operator?
2.Terminology:
If this is named “property access” :
then “3” must be a “property” of a string. Which in current ES5 terminology it is not, I think. Which somehow makes it questionable to call string [ ] operator “property access” ? On an array there is no property called “3” for example. instead there is “[]” operator. I have not seen yet, [ ] operator on ES5 arrays called “property access”?
btw: this table is v. useful … good work as ever.
This kind of property access on strings is spec’d in ES5. De-facto before that. See one of the last rows in http://kangax.github.com/es5-compat-table/
—
kangax
Using the [] operator on a string is very convenient but it’s also a bit misleading. For instance a string is not passed by reference so you can’t mutate it like arrays: [].sort.call(“cab”) throws an “illegal access” exception in Chrome (there is however the ++ operator which does mutation for numbers). But, interestingly, the assigment fails quietly, which is certainly not consistent with [].sort.
As for what’s going on internally: if I remember correctly whenever you’d like to use a primitive type as an object then the interpreter makes it an object so you can call methods on it or use the bracket operator.
@Balazs @kangax
I would not agree to call array [ ] operator (aka indexing operator) anything else but that. Perhaps because I know how it is implemented.
It seems my C++ demon got hold of me again :) Perhaps I should not have skipped Haskell.
String is not passed by reference and is thus immutable, indeed. which is in this story irrelevant since assignment through [ ] operator does not work on strings anyway.
Thanks All …
ECMA spec paragraph is here — http://es5.github.com/#x15.5.5.2
As you can see String instances have their “own”, special [[GetOwnProperty]] (called from [[GetProperty]], called from [[Get]], called from GetValue, called from reference evaluation).
And a reference with baseValue of string “foo” and property name of “0” is exactly what’s returned from something like
"foo"[0]
.I don’t know what this is named. I call it property access. In ECMA spec you’ll find them referring to it the same way, as Property Accessors — http://es5.github.com/#x11.2.1
As far as “string [] operator”, as you call it… There’s just one
[]
operator which can have any MemberExpression on its left side. And it’s still a property access no matter what object the property is being accessed off.