Why is this line valid in javascript ?
var a = 0[0];
After that, a is undefined.
When you do 0[0], the JS interpreter will turn the first 0 into a Number object and then try to access the [0] property of that object which is undefined.
There is no syntax error because the property access syntax 0[0] is allowed by the language grammar in this context. This structure (using terms in the Javascript grammar) is NumericLiteral[NumericLiteral].
The relevant part of the language grammar from section A.3 of the ES5 ECMAScript spec is this:
Literal ::
NullLiteral
BooleanLiteral
NumericLiteral
StringLiteral
RegularExpressionLiteral
PrimaryExpression :
this
Identifier
Literal
ArrayLiteral
ObjectLiteral
( Expression )
MemberExpression :
PrimaryExpression
FunctionExpression
MemberExpression [ Expression ]
MemberExpression . IdentifierName
new MemberExpression Arguments
So, one can follow the grammer through this progression:
MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]
And, similarly Expression can also eventually be NumericLiteral so after following the grammar, we see that this is allowed:
NumericLiteral [ NumericLiteral ]
Which means that 0[0] is an allowed part of the grammar and thus no SyntaxError.
Then, at run-time you are allowed to read a property that does not exist (it will just be read as undefined) as long as the source you are reading from either is an object or has an implicit conversion to an object. And, a numeric literal does indeed have an implicit conversion to an object (a Number object).
This is one of those often unknown features of Javascript. The types Number, Boolean and String in Javascript are usually stored internally as primitives (not full-blown objects). These are a compact, immutable storage representation (probably done this way for implementation efficiency). But, Javascript wants you to be able to treat these primitives like objects with properties and methods. So, if you try to access a property or method that is not directly supported on the primitive, then Javascript will temporarily coerce the primitive into an appropriate type of object with the value set to the value of the primitive.
When you use an object-like syntax on a primitive such as 0[0], the interpreter recognizes this as a property access on a primitive. Its response to this is to take the first 0 numeric primitive and coerce it into a full-blown Number object which it can then access the [0] property on. In this specific case, the [0] property on a Number object is undefined which is why that's the value you get from 0[0].
Here is an article on the auto-conversion of a primitive to an object for purposes of dealing with properties:
The Secret Life of Javascript Primitives
Here are the relevant portions of the ECMAScript 5.1 specification:
9.10 CheckObjectCoercible
Throws TypeError if value is undefined or null, otherwise returns true.
11.2.1 Property Accessors
Let baseReference be the result of evaluating MemberExpression.
Let baseValue be GetValue(baseReference).
Let propertyNameReference be the result of evaluating Expression.
Let propertyNameValue be GetValue(propertyNameReference).
Call CheckObjectCoercible(baseValue).
Let propertyNameString be ToString(propertyNameValue).
If the syntactic production that is being evaluated is contained in strict
mode code, let strict be true, else let strict be false.
Return a value of type Reference whose base value is baseValue and whose
referenced name is propertyNameString, and whose strict mode flag is
strict.
An operative part for this question is step #5 above.
8.7.1 GetValue (V)
This describes how when the value being accessed is a property reference, it calls ToObject(base) to get the object version of any primitive.
9.9 ToObject
This describes how Boolean, Number and String primitives are converted to an object form with the [[PrimitiveValue]] internal property set accordingly.
As an interesting test, if the code was like this:
var x = null;
var a = x[0];
It would still not throw a SyntaxError at parse time as this is technically legal syntax, but it would throw a TypeError at runtime when you run the code because when the above Property Accessors logic is applied to the value of x, it will call CheckObjectCoercible(x) or call ToObject(x) which will both throw a TypeError if x is null or undefined.
Like most programming languages, JS uses a grammar to parse your code and convert it to an executable form. If there's no rule in the grammar that can be applied to a particular chunk of code, it throws a SyntaxError. Otherwise, the code is considered valid, no matter if it makes sense or not.
The relevant parts of the JS grammar are
Literal ::
NumericLiteral
...
PrimaryExpression :
Literal
...
MemberExpression :
PrimaryExpression
MemberExpression [ Expression ]
...
Since 0[0] conforms to these rules, it's considered a valid expression. Whether it's correct (e.g. doesn't throw an error at run time) is another story, but yes it is. This is how JS evaluates expressions like someLiteral[someExpression]:
evaluate someExpression (which can be arbitrary complex)
convert the literal to a corresponding object type (numeric literals => Number, strings => String etc)
call the get property operation on result(2) with the property name result(1)
discard result(2)
So 0[0] is interpreted as
index = 0
temp = Number(0)
result = getproperty(temp, index) // it's undefined, but JS doesn't care
delete temp
return result
Here's a example of a valid, but incorrect expression:
null[0]
It's parsed fine, but at run time, the interpreter fails on step 2 (because null can't be converted to an object) and throws a run time error.
There are situations where you could validly subscript a number in Javascript:
-> 0['toString']
function toString() { [native code] }
While not immediately apparent why you would want to do this, subscripting in Javascript is equivalent to using dotted notation (albeit the dot notation limits you to using identifiers as keys).
I'd just like to note that this being valid syntax is not in any way unique to Javascript. Most languages will have a runtime error or a type error, but that's not the same thing as a syntax error. Javascript chooses to return undefined in many situations where another language might raise an exception, including when subscripting an object that does not have a property of the given name.
The syntax doesn't know the type of an expression (even a simple expression like a numeric literal), and will allow you to apply any operator to any expression. For example, attempting to subscript undefined or null causes a TypeError in Javascript. It's not a syntax error - if this is never executed (being on the wrong side of an if-statement), it won't cause any problems, whereas a syntax error is by definition always caught at compile time (eval, Function, etc, all count as compiling).
Because it is valid syntax, and even valid code to be interpreted. You can try to access any property of any object(and in this case 0 will be cast to a Number-object), and it will give you the value if it exists, otherwise undefined. Trying to access a property of undefined does not work however, so 0[0][0] would result in a runtime error. This would still be classified as valid syntax though. There's a difference of what is valid syntax and what won't cause runtime/compiletime errors.
Not only is the syntax valid the result does not have to be undefined though in most, if not all sane cases it will. JS is one of the most pure object oriented languages. Most so called OO languages are class oriented, in the sense that you can't change the form (it's tied to the class) of the object once created, only the state of the object. In JS you can change the state as well as the form of the object and this you do more often than you think. This ability makes for some rather obscure code, if you misuse it. Numerals are immutable, so you can't change the object itself, not it's state nor it's form so you could do
0[0] = 1;
which is an valid assignment expression that returns 1 but doesn't actually assign anything, The numeral 0 is immutable. Which in itself is somewhat odd. You can have a valid and correct (executable) assingment expression, that doesn't assign anything(*). However the type of the numeral is a mutable object so you can mutate the type, and the changes will cascade down the prototype chain.
Number[0] = 1;
//print 1 to the console
console.log(0[0]);
//will also print 1 to the console because all integers have the same type
console.log(1[0]);
of course it's a far cry from the sane use category but the language is specified to allow for this because in other scenarios, extending the objects capabilities actually makes a lot of sense. It's how jQuery plugins hook into the jQuery object to give an example.
(*) It does actually assign the value 1 to the property of an object, however there's no way you can reference that (transcient) object and it will thus be collected at the nexx GC pass
In JavaScript, everything is object, so when interpreter parse it, it treats 0 as a object and tries to return 0 as a property. The same thing happens when you try to access 0th element of true or ""(empty string).
Even if you set 0[0]=1, it will set the property and its value in memory, but while you access 0 it treats as a number (Don't get confuse between treating as Object and number here.)
Related
During a debugging session, after changing the return of a method from 0 to null, I started seeing an exception that wasn't occurring before.
Digging deeper, I realized that if a variable is holding a Number, you can call a property on it like if it was any other object; the same thing, however, doesn't happen if you try to call a property on the number directly.
For instance:
const number = 0;
console.log(number.foo) // undefined
0.foo // throws SyntaxError: Invalid or unexpected token
What causes this distinction, and where can I read more about it?
UPDATE
I just realized that (0).foo returns undefined as well, so there's at least some consistency there, but I didn't know Numbers could have properties in javascript.
Actually you can only try to read, but never assign to these properties:
const number = 3;
number.someProperty = true;
number.someProperty // returns `undefined`
. is taken as the optional decimal point directly after a number. Adding a space, using brackets, or adding another . to be the decimal point (so that the second . will be interpreted as a property accessor) will resolve the error.
You can access properties on primitive numbers because attempting to invoke methods or lookup properties on primitives will cause them to be wrapped in the corresponding object to perform the operation.
console.log(0 .foo);
console.log(0..foo);
console.log((0).foo);
0.foo throws SyntaxError: Invalid or unexpected token and the code is not execute because it is not a valid program.
The lexical parser finds 0 and knows that a number starts there. After 0 there is a . that tells it that the number is a real number, not an integer number. If after . there are more digits then they are the decimal part of the number.
In this case there aren't more digits after . and the number is 2.. The next characters after . (foo) are not part of the number but they are an identifier.
All in all, the input "program" 0.foo is parsed into two tokens: 2., which is a number followed by foo, which is an identifier.
This combination (number followed immediately by identifier) is not a valid program fragment. In a correct program, a number can be followed by a space, an operator, a semicolon, a closed parenthesis and probably several other lexical tokens but not by an identifier.
The code fragment is not a valid JavaScript program; it cannot be compiled and therefore it is not executed.
There are several ways to make it work. For example (0).foo.
If you try to run 0.foo in the developers console in Firefox it throws an error that summarizes what I explained above:
Uncaught SyntaxError: identifier starts immediately after numeric literal
This is because of JS OOP nature and its prototyping model. Everything, even primitive values, has a prototype, which is usually an Object. So any primitive, String, Boolean, BigInt, etc. has properties, with exception of null and undefined.
(1).toString()
(1n).toString()
(true).toString()
You can access values' prototypes like this:
Number.prototype
Boolean.prototype
String.prototype
Thus you can add properties to primitives (but you should not do this due to compatibility issues and error-prone nature of such modifications). But here is an example of this:
Number.prototype.negative = function () {
return Number(this * -1)
};
(1).negative(); // -1
In the case of numbers you got a syntax error because of parsing rules of JS, not because of its OOP model. JS interprets 0. as a number with mantissa, thus it expects a number after the dot.
Consider the following class declaration:
class A {}
A.prototype.test1 = function test1() { console.log("test1") }
Evaluating the stringified version of test1 produces valid JS, as we can see here:
const a = new A
eval(`(${a.test1})`)() // outputs "test1"
However, if we build our class in a different but fundamentally equivalent way:
class B {
test2() { console.log("test2") }
}
Evaluating the stringified version of test2 fails:
const b = new B
eval(`(${b.test2})`)() // SyntaxError: Unexpected token {
(it was stringified to test2() { console.log("test2") } which is not valid JS except when embedded in a class declaration)
I can understand stringified native functions being not evaluable, for example "".indexOf.toString() will return a string containing [native code] and I accept that.
But isn't there a way to guarantee that Function.prototype.toString() called on user-defined functions (that is, function from which the source code is available) produces valid, evaluable JS?
But isn't there a way to guarantee that Function.prototype.toString() called on user-defined functions (that is, function from which the source code is available) produces valid, evaluable JS?
No. It depends on the type of the function. The specification requires the following:
toString Representation Requirements:
The string representation must have the syntax of a FunctionDeclaration, FunctionExpression, GeneratorDeclaration, GeneratorExpression, AsyncFunctionDeclaration, AsyncFunctionExpression, ClassDeclaration, ClassExpression, ArrowFunction, AsyncArrowFunction, or MethodDefinition depending upon the actual characteristics of the object.
The use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.
If the object was defined using ECMAScript code and the returned string representation is not in the form of a MethodDefinition or GeneratorMethod then the representation must be such that if the string is evaluated, using eval in a lexical context that is equivalent to the lexical context used to create the original object, it will result in a new functionally equivalent object. In that case the returned source code must not mention freely any variables that were not mentioned freely by the original function's source code, even if these “extra” names were originally in scope.
If the implementation cannot produce a source code string that meets these criteria then it must return a string for which eval will throw a SyntaxError exception.
Since you have a MethodDefinition you get the representation of a method. If you have a function that is neither a MethodDefinition nor a GeneratorMethod, then you can likely get a representation that can be evaluated (third point), but even then, the spec says that implementation should return a representation that throws a syntax error, so ¯\_(ツ)_/¯.
A.S.: The question is about type of error, not about the phenomenon
"use strict" throws TypeError if system variables like NaN and undefined got changed.
But why is it a TypeError? Why not SyntaxError?
Edit: Really, it is not the SyntaxError here since there are no errors in syntax of the snippet.
But the root of the error lies in the fact, that some protected object cannot be changed manually; so, it is lkely the AccessError (there are no such, I know).
Then, why access-errors appear like type-ones?
In ES5, there are 6 different kinds of native errors:
EvalError
This exception is not currently used within this specification. This
object remains for compatibility with previous editions of this
specification.
RangeError
Indicates a numeric value has exceeded the allowable range.
ReferenceError
Indicate that an invalid reference value has been detected.
SyntaxError
Indicates that a parsing error has occurred.
TypeError
Indicates the actual type of an operand is different than the expected
type.
URIError
Indicates that one of the global URI handling functions was used in a
way that is incompatible with its definition.
In your case, the error is thrown because you attempt to assign a value to window.NaN or window.undefined, which are non-writable properties.
Before assigning the new value, the internal [[Put]] method checks [[CanPut]], which will return false because the property is non-enumerable. And therefore [[Put]] will throw.
So the problem is that the writability of the assigned reference (left operand in the assignment expression) is not the expected one. Then, among the 6 error kinds above, the most appropriate seems TypeError, even if writability is not exactly a type.
Because NaN and undefined are just read only properties of window. They are not operators (like > or +), or keywords (like var or class).
Trying to assign something to NaN gives the following error in Chrome:
TypeError: Cannot assign to read only property 'NaN' of object '#<Window>'
Because it's a valid argument that does not make sense. But it does not make sense in the SEMANTIC level, not the syntactic.
Here's an example:
1+1=42
This is wrong, but it's wrong in a different way than, say:
42 1 1 + =
You can read the first one as an arithmetic equation (one plus one equals forty-two). The second one, you can't even read.
The same goes here. the statement NaN=300; can be read in Javascript as "have NaN point to the value 300`. But when the parser submit this request to the engine, the engine goes "Nope, can't do this".
syntax error
In most programming & scripting languages this generally means that you've used an operator in the wrong place.
For more info on "syntax errors", have a look at this article: https://en.wikipedia.org/wiki/Syntax_error
type error
These occur when a data-type miss-matches some requirement.
For more information on "data-types", have a look at this article: https://en.wikipedia.org/wiki/Data_type
So, the type error is thrown because NaN and undefined are "data-types", which are global constants with a specific (designated) value, and "strict mode" prevents issues by not allowing to change these.
I'm working through Learning Javascript Properly and am reading Chapter 4 of Professional Javascript for Web Developers. On p. 86 they say
Primitive values can’t have properties added to them even though
attempting to do so won’t cause an error. Here’s an example:
var name = “Nicholas”;
name.age = 27;
alert(name.age); //undefined
They also say that JavaScript does not treat strings as objects, like other languages do. I wanted to see if I could get it to print out name.age.
I tried
var name = "Nicholas";
name.age = 27, alert(name.age);
And got undefined.
Trying
var name = "Nicholas";
name.age = 27 & alert(name.age);
Also gave undefined.
But,
var name = "Nicholas";
alert(name.age = 27);
gives 27!
With regards to the text's original example, the author says
"Here a property called age is defined on the string name and assigned
a value of 27. On the very next line, however, the property is gone.
Only reference values can have properties defined dynamically for
later use."
What is going on with the comma separated assignment and function call - I knew you could use commas to separate variable assignments, but you can also do function calls? What are the limits to the comma in javascript?
How does the & operator work for chaining together code snippets? What is that operator and what should it be used for?
Why did my last example work when the other ones wouldn't? Does this have to do with scope?
Edit: Thanks JLRishe and SirReal. I didn't really understand JLRishe's answer until reading SirReal's, but I recommend reading both!
Really, the answer to all three of your questions has to do with how the ,, & and = operators are defined, and has nothing to do with properties on primitive values.
What is going on with the comma separated assignment and function call
Your second example is an example of the comma operator, and this is different from the comma used in variable declarations. It evaluates each of its operands one at a time from left to right, and the ultimate value of the expression is the value of the last operand.
How does the & operator work for chaining together code snippets?
The & operator evaluates its two operands from left to right and produces the result of applying a bitwise AND to them. Your alert() is executing as a side effect of this. The expression 27 & alert(name.age) evaluates to 0, and this is what is assigned to name.age (but this doesn't really do anything). You can see similar behavior with:
name.age = 27 * alert(name.age);
or
name.age = 27 - alert(name.age);
or several other operators.
Why did my last example work when the other ones wouldn't?
What you observed in your last example is the well-defined behavior of the = operator. It performs an assignment and produces the value of the right-hand side. The assignment doesn't actually happen in this case, for the reasons you quoted, but it evaluates to the value of the right-hand nonetheless, and that is what is passed to the alert() function.
& is bitwise AND, a comparison operator. It's used to compare values at the bit level. You should probably not be using this for chaining code together.
, (comma) is the comma operator.
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
There don't appear to be any limits to that, but there are not a lot of good reasons to abuse this. MDN suggests that this is mostly for working with multiple values in for loops.
=, the simple assignment operator allows chaining. That means x = y = 2 is valid and will result in x == 2 and y == 2. I haven't found explicit documentation to support this, but I think it's safe to infer that the result of the assignment is returned when using =, which is the strange behavoir you're seeing. While you can't actually set a property on a string, the value you try to set is returned in case you wanted to chain it or otherwise use it.
You can't think like this in javascript. Variable assignment will work in this context if the variable can be assigned but has no bearing on the value passed to the function, that is, in this case 27, not name.age.
var name = "Nicholas";
alert(name.age = 27);
This will always alert an integer of 27. This is because you are not just assigning the name.age property a value, you are also passing the value to the alert function regardless of the property you attempted to assign the value to.
Comma operators string together operands and evaluate them in the order that you define them. As they are evaluated separately name.age has no value when it is evaluated as the second operand in your example.
The & operator is a bitwise operator that converts both sides to integers and evaluates both sides as their 32 bit binary representations. As other comments and answers have pointed out if either the left or right side is NaN it is treated as 0.
Irrelevant detail added in original answer below
&& evaluates left to right, similar to comma operators, however if the left side evaluates to false, the right side is never evaluated. This allows you to chain a check and an evaluation together without errors. This is called short circuiting.
callback instanceof Function && callback();
The above example checks if callback is a function, only if it is TRUE will the right hand side be evaluated.
The reason alert(name.age = 27) works is because of how primitive wrapper types operate. Primitive values like booleans, numbers, and strings in javascript are treated as objects in certain special cases, permitting the use of methods (like substring()).
Normally, a primitive value would not be able to call a method since it is considered a primitive, not an object (reference type). However, there are primitive wrapper types that enable this to work behind the scenes.
The three primitive wrapper types are Number, Boolean, and String and correspond to their respective primitive values. When you execute a method on a primitive value what is actually happening is the following:
The primitive value is cast as an instance of the primitive wrapper type
The method is executed on the primitive wrapper instance (an object)
The primitive wrapper instance is destroyed (your value goes back to being a regular primitive value).
The variable name in the code example is a string primitive value. When the property .age is added to it, it must first be cast as an instance of the primitive wrapper type String. However, that instance is immediately destroyed after that call executes, which is why the only call that does not return undefined is the one that alerts the property at the same time it is assigned.
The major difference between reference types and primitive wrapper types is the lifetime of the object. When you instantiate a reference
type using the new operator, it stays in memory until it goes out of
scope, whereas automatically created primitive wrapper objects exist
for only one line of code before they are destroyed. This means that
properties and methods cannot be added at runtime. Take this for
example:
var s1 = “some text”;
s1.color = “red”;
alert(s1.color);//undefined
Here, the second line attempts to add a color property to
the string s1. However, when s1 is accessed on the third line, the
color property is gone. This happens because the String object that
was created in the second line is destroyed by the time the third line
is executed. The third line creates its own String object, which
doesn’t have the color property. (Zakas, 147)
This is also why the following code would have worked:
var name = new String("Nicholas");
name.age = 27;
alert(name.age); //27
Here we have an explicit construction of an instance of the String primitive wrapper type, so it survives as an actual object.
Source: Zakas, Professional Javascript for Web Developers, p. 147 3rd ed.
In the following example:
var tester = Object.create(null);
tester.forename = "Bob";
tester[function () { return "surname"; }] = "Jones";
Is javascript simply converting the function definition to a string to use as the property key?
Is javascript simply converting the function definition to a string to use as the property key?
Yes. Note that the string will not be "surname". In fact, to date, it's not specified what the string will be, and it varies from engine to engine. On some engines, it will be roughly what you have in the source. But it would be perfectly valid (e.g., within the spec) for it to be "dunno, some function". The next spec, ES6, is likely to say that, for Function#toString:
An implementation-dependent String source code representation of the this object is returned. This
representation has the syntax of a FunctionDeclaration FunctionExpression, GeneratorDeclaration,
GeneratorExpession, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod
depending upon the actual characteristics of the object. In particular that the use and placement of white space, line
terminators, and semicolons within the representation String is implementation-dependent.
(This is the January 2014 wording.)
But that's the next spec, which isn't final yet, and will probably change a bit as the spec approaches completion (particularly with regard to native functions). As of the current spec, Function#toString can return anything it likes, and it need not vary from function to function.
Yes. The property names of objects are always strings.
If you attempt to use a different type, it will be coerced into string.
If you want them to be other types, you can use ES6 Map.