A Simplified JavaScript Grammar
The many small rules in the JavaScript programming language is its grammar. These rules specify the accepted way to write JavaScript code. Unless your code follows the grammar of JavaScript, the computer won’t understand (or worse, might mis-interpret) what you’re telling it to do.
Individually, each rule in itself is not enough to produce complete working code. Instead, the rules are like building blocks, offering various ways to assemble or construct your code.
A language grammar is a set of small rules that can be combined to produce the syntax of your code.
Using This Grammar
The formal grammar for JavaScript is actually quite large and complex. In fact, the grammars for most programming languages are so complex that they require another “language” to describe the grammar. An early and fairly standard language or means to express grammars is the Bakus-Naur Form, or BNF.
For our purposes, we will present a much more simplified grammar. The following sections express the bulk of the JavaScript grammar used in this introductory JavaScript course.
Most of the grammar rules in JavaScript are quite short, defining the order of keywords, identifiers and symbols/operators. In the following grammars, highlighted portions indicate a part of the grammar where you would need to substitute some appropriate code. For example, the for
statement has four items that you would replace with code specific to your application. The required portions of the grammar are the keywords (for
) and symbols (parenthesis and semicolons).
for([initialization]; conditionalExpression; [incrementation]) statementOrStatementBlock
Common Grammar Elements
Program Statements and Statement blocks
Individual instructions are known as Program Statements. The instructions can be short and simple, or they can be long and complex. In either case, the program statement should end in a semicolon (;
), though the grammar allows them to be omitted.
Besides individual instructions, we can group individual statements into Statement Blocks. A statement block is a set of zero or more program statements that are enclosed in a set of curly braces ({ }
). Statement blocks are frequently used with Flow Control Statements (such as the if
and for
statements).
Literal Values
A literal value is a piece of “raw” information or data that is embedded into our code. JavaScript offers several types of literals. The more commonly used ones are as follows.
- String literals - Any text enclosed in single or double quotes.
'Hello'
and"Stewart's home!"
are examples of string literals. - Numeric literals - One or more digits combined to make a number.
42
and3.14159
are examples of numeric literals, where the first one is an integer literal (whole number) and the second is a floating-point literal (the value includes a decimal point). - Boolean literals - JavaScript has two keywords which represent literal boolean values:
true
andfalse
. - Array literals - Any set of values enclosed withint square brackets (
[]
). It can be empty -[]
- or include values -['Ace', 'King', 'Queen'].
- Object literals - Any set of zero or more pairs of property names and values enclosed within curly braces (
{}
). This should not be confused with the use of curly braces that represents a statement block.
There are additional types of literals in JavaScript that are used for specific purposes. These include the following.
- Template literals - A variation of the string literal, but where the text is enclosed with backticks (
`
). - RegExp literals - A pattern for matching string values, enslosed between a pair of slashes (
/
).
For links to more detailed descriptions, see the MDN article on Literals.
Template Strings
The difference between template and string literals is that all of the text inside a string is considered a literal value whereas template literals can include variables or expressions through the use of placeholders. For example, in the following code, everything between the backticks are the literal parts of the string while the ${firstName}
is not.
`Welcome to ${firstName}'s world!`
If the value of firstName
is "Wayne"
, then the resulting text would be Welcome to Wayne's world!
.
Template strings work across multiple lines as well. Consider this example.
welcome = `<h1> Welcome ${firstName}! <span class="badge">${rating}</span></h1>`;
Regular strings have to be entirely on the same physical line in JavaScript. If you want to built your string literal using line breaks, use template strings instead.
Expressions
An Expression is any combination of literal values, identifiers, operators (such as the arithmetic operators), and/or method calls (where the method returns a value). When an expression is processed, a single value is produced. This value can then be used in whatever operation the expressions occurs. For example, the value might be passed into a method as part of a method call, or it might be placed in a variable as part of an assignment statement.
Variable Declarations
Before a variable can be used, it must be declared. How you declare a variable will affect its scope and whether its value can be changed. There are three keywords in JavaScript for declaring variables: var
, let
and const
.
let
keyword MDN
var
keyword MDN
const
keyword MDN
let
and var
The let
statement declares a variable with block-level scope. The var
statement declares a variable with function-scope or global scope.
let variableName [= expression];
var variableName [= expression];
In both cases, variablename
is a programmer-defined identifier (name) to represent a value. An optional initial value may be assigned, as denoted by [= expression]
, where expression
is any valid JavaScript expression. If no value is assigned to the variable, it’s initial value is undefined
. The datatype of the variable is always determined by the value stored in that variable.
const
The const
statement declares a variable whose value cannot be changed. The value must be assigned when the variable is declared.
const variableName = expression;
Again, variablename
is a programmer-defined identifier (name) while expression
is any valid JavaScript expression.
Assignment Operation
variableName assignmentOperator expression
Assignment Operations are operations where a value is assigned or stored in a variable. The parts of the grammar are the
variableName
- the name of the variable that will receive/store the value.assignmentOperator
- one of the following:=
Equals+=
Plus-Equals-=
Minus-Equals*=
Multiply-Equals/=
Divide-Equals%=
Modulus-Equals
expression
- any valid JavaScript expression
An assignment operation is made into an assignment statement by adding a semicolon to the end of the operation. For example,
total = price * quantity;
Flow-Control Statements
Flow-Control Statements are a crucial part of the procedural characteristics within the JavaScript language. They provide the capabilities of alternative paths of logic (if-else
and switch
) and repetition (for
, while
and their variants). They also provide the ability to “jump into” functions through function calls.
All flow-control statement grammars include a reference to statementOrStatementBlock
. When using a Statement Block, the opening curly brace must be on the same line as the initial statement (as per accepted JavaScript style guides). The provided samples are shown with the use of statement blocks.
statementOrStatementBlock
is either a single statement or a single statement block (zero or more statements inside curly braces -{ }
).- A single statement can be on its own line.
- A statement block should have the opening curly brace at then end of the same line as the initial statement and the closing curly brace on its own line.
The following is an example of a statement block with properly positioned curly braces.
if(quantity === 0) { // zero or more lines of code}
Conditional Expressions
Conditional expressions are ones that are ultimately evaluated as being equivalent to true
or false
. In flow-control statements, they often include the use of relational operators and/or boolean operators.
”Truthy” and “Falsy” Values
While JavaScript has a built-in Boolean data type, the evaluation of conditional expressions is not strictly confined to true
or false
values. A conditional expression, in JavaScript, can also be something that is truthy or falsy.
JavaScript regards all of the following as being equivalent to a false
value (and refers to these as falsy values).
undefined
- No value has been assignednull
- The absense of any valueNaN
- Not-a-Number0
- The numeric value zero""
- An empty string{}
- An empty object
Anything that is not false (or falsy) is regarded as equivalent to true
. Besides the true
keyword, some samples of truthy values include the following: 42
, 'bob'
, '\n'
, { key: 'value' }
.
Relational Operators
JavaScript has the following relational operators. These are all binary operators, meaning that there must be some identifier or expression on both sides of the operator
==
- “Is equal to”===
- Strict equality!=
- “Not equal to”!==
- Strict inequality>
- Greater Than>=
- Greater Than or Equal To<
- Less Than<=
- Less Than or Equal To
Boolean Operators
JavaScript has the following boolean operators.
&&
- Logical “And”||
- Logical “Or”!
- Logical “Not”
The logical “And” (&&
) and “Or” (||
) operators are binary operators, meaning that some identifier or expression is required on both sides of the operator. The logical “Not” (!
) operator is a unary operator where an identifier or expression is expected to be on the right-hand side of the operator.
Two “Not” operators can be used to coerce or force an identifier into a strict true
or false
value. For example, in the following code the value of !!myObject
will be forced to true
if myObject
is truthy or false
otherwise.
let myObject;// some intervening code that sets the valuelet success = !!myObject;if(success) { // do something}
To understand the effects of boolean operations, consider the following truth table. It illustrates the resulting value from performing “And” and “Or” operations.
a | b | Logical ANDa && b | Logical ORa || b | Logical NOT!a |
---|---|---|---|---|
true | true | true | true | false |
true | false | false | true | false |
false | true | false | true | true |
false | false | false | false | true |
If-Else ⁉️
if(conditionalExpression) statementOrStatementBlock // true sideelse statementOrStatementBlock // false side
The if-else provides alternate paths of logic, where
conditionalExpression
is an expression whose ultimate data type is abool
.- the
else
portion is optional.
For example:
Switch-Case ⁉️
switch(cardinalExpression){ case matchingExpression1: statementOrStatementBlock break; case matchingExpression2: statementOrStatementBlock break; // ...additional case statements... default: statementOrStatementBlock break;}
The switch provides alternate paths of logic, where
: cardinalExpression
is an expression that produces a single value of any primitive data type (int
, double
, char
, string
or an enum
).
: Each matchingExpression
is a constant value whose data type matches the data type of the cardinalExpression
. A match is determined by the value of the cardinalExpression
being equal to the matchingExpression
. The break
indicates the end of the path of logic for the matching expression.
: statementOrStatementBlock
is either a single statement or a single statement block (zero or more statements inside curly braces - { }
).
: The default
block will execute if cardinalExpression
did not match any of the listed matching expressions.
For example:
For ⁉️
The for
keyword is a flow control structure to provide looping capabilities. It comes in four variations:
for
- The traditional variation of thewhile
statement, where factors affecting the repetition are contained in the parenthesis.for...in
-for...of
-for await...of
-
for
Statement
for([initialization]; conditionalExpression; [incrementation]) statementOrStatementBlock
The for provides repetitive execution of code, where
initialization
is a variable declaration typically including an assignment; the variables identified here should be ones used in theconditionalExpression
. This portion is optional, but when declaring the variable you should use thelet
orvar
keywords.conditionalExpression
is an expression whose ultimate data type is abool
. TheconditionalExpression
is evaluated at the beginning of the loop and may be any “truthy” expression.incrementation
is a modifications to the variable controlling the loop; the variables identified here should be ones used in theconditionalExpression
. This portion is optional.
For example:
for(let count = 0; count < options.length; count++) { // code that you want to repeat}
for
Keyword on MDN
for...of
Statement
for(variable of iterable) statementOrStatementBlock
The for...of
provides repetitive execution of code, where the repetition occurs for each item in the iterable
.
iterable
is any object that represents a collection of values, such as Arrays, strings,NodeList
and similar collections.variable
is an identifier to represent each item in the collection as it loops through the collection’s items/elements; the variable should be declared with alet
,const
orvar
.
For example:
for(let )
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for…of
for...in
Statement
for await...of
Statement
While and Do-While ⁉️
while(conditionalExpression) statementOrStatementBlock
The while provides repetitive execution of code, where
: conditionalExpression
is an expression whose ultimate data type is a bool
. The conditionalExpression
is evaluated at the beginning of the loop.
: statementOrStatementBlock
is either a single statement or a single statement block (zero or more statements inside curly braces - { }
).
- The
statementOrStatementBlock
will only execute as long as thecontitionalExpression
results in atrue
value. - The loop exits when the
conditionalStatement
results in afalse
value.
do statementOrStatementBlockwhile (conditionalExpression);
The do-while provides repetitive execution of code, where
: conditionalExpression
is an expression whose ultimate data type is a bool
. The conditionalExpression
is evaluated at the end of the loop.
: statementOrStatementBlock
is either a single statement or a single statement block (zero or more statements inside curly braces - { }
).
- The
statementOrStatementBlock
will execute at least once and will keep on executing as long as thecontitionalExpression
results in atrue
value. - The loop exits when the
conditionalStatement
results in afalse
value.
Function Calls ⁉️
While the Method Declaration defines a set of instructions, those instructions only run when the method is called from somewhere. The operating system is responsible to call the Main()
method, but after that, all method calls are the responsibility of our program. The grammar of a method call is as follows.
[[ClassName | ObjectName].]MethodName(argumentList)
A Method Call is an expression where
: MethodName
is the programmer-defined name of the method,
: argumentList
is a comma-separated list of values that correspond to the parameters of the method declaration,
: The method is preceded by either a ClassName
(for static
methods) or an ObjectName
(aka: a variable name) that identifies where the method can be “found”; the Member Access Operator (aka: “dot” operator .
) comes right after the ClassName
or ObjectName
. If the method declaration is in the same class as the method call, then the ClassName
/ObjectName
can be omitted.
When a method is an instance method (non-static) and is called without an ObjectName
, the this
keyword is implied. In other words, if the method call looks like
Display("Some Text")
it is interpreted as
this.Display("Some Text")
The this
keyword simply means that the method declaration is in the same class as the method call and that it should be applied to the same instance (this
instance) of the class.
Classes and Class Members ⁉️
As an object-oriented language, classes play a very prominent part of the code we write in JavaScript. It is within classes, for example, that we place variables (also called fields) and methods (which are “named sets of instructions”). One of the first things that classes give us developers is a context or scope for the code that we write. Classes are also building blocks, acting as blueprints for new and complex data types that we as programmers can create as we develop richer and more complex computer programs. Classes permeate all the code that we write in JavaScript and are so fundamental that you can’t even write a “Hello World” program without them.
Class Definition ⁉️
[accessModifier] class ClassName{ // FieldDeclarations // PropertyDeclarations // Constructors // MethodDeclarations}
A Class Definition describes a new data type where
: [accessModifier]
is either public
or internal
. If no access modifier is provided, then the default modifier is internal
.
: ClassName
is the programmer-supplied name for the class (in TitleCase format)
: FieldDeclarations
, PropertyDeclarations
, Constructors
and MethodDeclarations
are all optional and can appear in any order.
See the related grammars below to see how they are defined.
Field Declarations ⁉️
[accessModifier] [static] dataType _FieldName [= constantExpression];
A Field Declaration identifies a static or instance member variable of the class where
: [accessModifier]
is either public
, private
, protected
, or internal
. If no access modifier is provided, then the default modifier is private
.
: [static]
is an optional keyword. If present, the field is shared among all instances of the class. If absent (which is the common case) then the field is an instance member and one is created every time an object based on the class is created.
: dataType
is any built-in data type or the name of a programmer-defined data type.
: _FieldName
is a the name you give to the member variable. By convention, private fields are given an underscore as a prefix to the name.
: constantExpression
is an optional expression that generates data whose value can be determined at compile time. Being a constant expression does not mean that the field is a constant, only that the initial value stored in the field is a constant and can be known at compile time before the program runs.
Property Declarations ⁉️
Properties are a kind of cross between methods and fields. On one hand, they are used in the same way that fields are. When you want to assign (or set) a value to a property, you place the property on the left side of the assignment operator. When you want to use (or get) the properties value, you simply reference the property name just as you would a field name.
Internally, however, the get and set operations are like the bodies of a method, where you can place instructions to retrieve or change the data in the class or object. The technical term for the get and set portions of a property is accessor.1
Explicitly Implemented Property ⁉️
Explicitly implemented properties are properties where the programmer supplies the getter and setter implementations. The bodies of the getter and setter may reference a field (known as a backing store) that holds the actual information. In these cases, the property is working to provide a controlled access to the underlying field’s data.
In other situations, a property may merely have a getter where the body of the getter derives or calculates a value to return from some other source, such as a calculation.
[accessModifier] [static] dataType PropertyName{ get { // Body of getter } set { // Body of setter }}
A Property Declaration identifies a static or instance member of the class where
: [accessModifier]
is either public
, private
, protected
, or internal
. If no access modifier is provided, then the default modifier is private
.
: [static]
is an optional keyword. If present, the Property is shared among all instances of the class. If absent (which is the common case) then the Property is an instance member.
: dataType
is any built-in data type or the name of a programmer-defined data type.
: PropertyName
is a the name you give to the member property.
: Body of getter
is a set of instructions that must ultimately return a value of the same data type as the property.
: Body of the setter
is a set of instructions that can process incoming data that is in the value
keyword. A typical implementation will store that data into the property’s backing store.
Auto-Implemented Property ⁉️
Auto-Implemented properties are properties where the compiler takes care of the getter and setter implementations and also supplies a hidden field as the backing store for the property. The default get implementation is to retrieve the value from the backing store while the default set implementation is to place a value into the backing store.
[accessModifier] [static] dataType PropertyName { get; set; }
A Property Declaration identifies a static or instance member of the class where
: [accessModifier]
is either public
, private
, protected
, or internal
. If no access modifier is provided, then the default modifier is private
.
: [static]
is an optional keyword. If present, the Property is shared among all instances of the class. If absent (which is the common case) then the Property is an instance member.
: dataType
is any built-in data type or the name of a programmer-defined data type.
: PropertyName
is a the name you give to the member property.
Method Declarations ⁉️
[accessModifier] [static] returnType MethodName(ParameterList){ // body of method}
A Method Declaration defines a named set of instructions.
: [accessModifier]
is either public
, private
, protected
, or internal
. If no access modifier is provided, then the default modifier is private
.
: [static]
is an optional keyword. If present, the method is shared among all instances of the class. If absent (which is the common case) then the method is an instance member.
: returnType
is any built-in data type or the name of a programmer-defined data type. The return type signifies the kind of information that the method will return. If the method does not return any information, then the keyword void
is used as the return type.
: MethodName
is a the name you give to the method.
: ParameterList
is a comma-separated list of individual variable declarations.
Constructors ⁉️
[accessModifier] ClassName(ParameterList){ // body of constructor}
A Constructor is a set of instructions used when instantiating (creating) an object.
: [accessModifier]
is either public
, private
, protected
, or internal
. If no access modifier is provided, then the default modifier is private
.
: ClassName
- All constructors use the name of the class to which they belong as the name of the constructor.
: ParameterList
is a comma-separated list of individual variable declarations.
: Classes never return any information - they are simply blocks of instructions used to set up the initial state of the object.
Objects, Classes and More
Object Instantiation ⁉️
new ClassName(argList)
Initializer Lists ⁉️
new ClassName{ initializerList}
Classes and Inheritance ⁉️
[accessModifier] class ClassName : BaseClassOrInterface[, InterfaceList]{ // ClassMembers}
This grammar offers additional details over the earlier Class Definition, where
: BaseClassOrInterface
is the name of either another class or an interface
: InterfaceList
is a comma-separated list of interface names
Footnotes
-
See the Microsoft docs for Restricting Accessor Accessibility ↩