Prototypal inheritance
With the exception of null and undefined, each primitive data type has a
prototype, a corresponding object wrapper that provides methods for working
with values. When a method or property lookup is invoked on a primitive,
JavaScript wraps the primitive behind the scenes and calls the method or
performs the property lookup on the wrapper object instead.
For example, a string literal has no methods of its own, but you can call the
.toUpperCase() method on it thanks to the corresponding String object
wrapper:
"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL
This is called prototypal inheritance—inheriting properties and methods from a value's corresponding constructor.
Number.prototype
> Number { 0 }
> constructor: function Number()
> toExponential: function toExponential()
> toFixed: function toFixed()
> toLocaleString: function toLocaleString()
> toPrecision: function toPrecision()
> toString: function toString()
> valueOf: function valueOf()
> <prototype>: Object { … }
You can create primitives using these constructors, instead of just defining
them by their value. For example, using the String constructor creates a
string object, not a string literal: an object that not only contains our string
value, but all the inherited properties and methods of the constructor.
const myString = new String( "I'm a string." );
myString;
> String { "I'm a string." }
typeof myString;
> "object"
myString.valueOf();
> "I'm a string."
For the most part, the resulting objects behave as the values we've used to
define them. For example, even though defining a number value using the
new Number constructor results in an object containing all the methods and
properties of the Number prototype, you can use mathematical operators on
those objects just as you would on number literals:
const numberOne = new Number(1);
const numberTwo = new Number(2);
numberOne;
> Number { 1 }
typeof numberOne;
> "object"
numberTwo;
> Number { 2 }
typeof numberTwo;
> "object"
numberOne + numberTwo;
> 3
You'll very rarely need to use these constructors, because JavaScript's built-in prototypal inheritance means they provide no practical benefit. Creating primitives using constructors can also lead to unexpected results, because the result is an object, not a simple literal:
let stringLiteral = "String literal."
typeof stringLiteral;
> "string"
let stringObject = new String( "String object." );
stringObject
> "object"
This can complicate the use of strict comparison operators:
const myStringLiteral = "My string";
const myStringObject = new String( "My string" );
myStringLiteral === "My string";
> true
myStringObject === "My string";
> false
Automatic semicolon insertion (ASI)
While parsing a script, JavaScript interpreters can use a feature called automatic semicolon insertion (ASI) to try to correct instances of omitted semicolons. If the JavaScript parser encounters a token that isn't allowed, it tries to add a semicolon before that token to fix the potential syntax error, as long as one or more of the following conditions is true:
- That token is separated from the previous token by a line break.
- That token is
}. - The previous token is