an expression is a phrase that an interpreter
can evaluate to produce a value
complex expressions are built from simpler expressions
the most common way to build a complex expression out of simpler expressions is with an operator
an operator combines the values of its operands (usually two of them) in some way and evaluates to a new value
are the simplest expressions
stand alone - don't include any simpler expressions
constant or literal values
1.77 // number literal
"hello" // string literal
/pattern/ // regular expression literal
certain language keywords
true // evaluates to the boolean true value
false // evaluates to the boolean false value
null // evaluates to the null value
this // evaluates to the "current" object
variable references
message // evaluates to the value of the variable message
j // evaluates to the value of the variale j
undefined // undefined is a global variable, not a keyword
called object literals and array literals
are not primary expressions, because they include a number of subexpressions that specify property and element values
are expressions whose value is a newly created object or array
the expressions in an array/object initializer
are evaluated each time the initializer is evaluated,
this means that the value of an array/object initializer expression may be different each time it is evaluated ( they need not have constant values: they can be arbitrary JavaScript expressions)
array initializer
a comma-separated list of expressions contained within square brackets
var empty_array = []; //an empty array, no expressions inside brackets means no elements
var array = [1+2,3+4]; //a 2-element array, first element is 3 and the second is 7
var matrix = [[1,2],[3]]; //elements can be array initializers themselves
var sparse_array = [1,,,,5]; //contains 5 elements, including 3 undefined elements
var array = [1,2,3,] //3 elements, a single trailing comma allowed after last expression
arrays can be nestedobject initializer
a comma-separated list of expressions contained within curly brackets, where each subexpression is prefixed with a propertyname and a colon
var empty_object = {}; //an empty object with no properties
var object = { x:2.3,y:-1.2 } //an object with 2 properties
var rectangle = { upperLeft: { x: 2, y: 2 },
loweRight: { x: 4, y: 5 }, };
objects can be nesteda function definition expression defines a JavaScript function
- is a function literal
the value of a function expression is the newly defined function
typically consist of the keyword function followed by a comma-separated list of zero or more identifiers (the parameter names) in parentheses and a block of code (the function body) in curly braces
// function which returns the square of the value passed to it
var square = function(x) { return x * x; }
they can also include a name for the function
a property access expression evaluates to the value of an object property or an array element
two syntaxes are defined for property access:
expression . identifier
where expression specifies the object, and the identifier specifies the name of the desired property
expression [ expression ]
the first expression (the object or array) is followed by another expression
(name of the desired property of the index of the desired array element) in square brackets
var o = {x:7,y:{z:3}}; // example object
o.x; // => 7, property x of the expression o
o.x.z; // => 3, property z of the expression o.y
o["x"]; // => 7, property x of the object o
var a = [4,o,[5,6]]; // example array that contains the object o
a[0]; // => 4, element at index 0 of expression a
a[2]["1"]; // => 6, element at index 1 of expression a[2]
a[1].x; // => 7, property x of the expression a[1]
the expression before the . or [ is first evaluated first,
.identifier => the value of the property named by that identifier is looked up
and becomes the overall value of the expression
[expression] => the expression is evaluated and converted to a string and the overall value of the expression is then the value of the property named by that string
! in either case, if the named property does not exist, then the value of the property access expression is undefined
.identifier can be used only when the property you want to access has a name that is a legal identifier, and when you know the name when you write the program
[ expression ] must be used if the property name is a reserved word or includes spaces or punctuation characters, when it is a number (for arrays) or when it is not static but is itself the result of a computation
an invocation expression is the syntax for calling (or executing) a function or method
start with a function expression that identifies the function to be called, which is followed by an open parenthesis, a comma-separated list of zero or more argument expressions, and a close parenthesis
f(0) // f is the function expression, 0 is the argument expression
Math.max(x,y,z) // Math.max is the function, x,y and z are the arguments
a.sort() // a.sort is the function, there are no arguments
when an invocation expression is evaluated:
function expression is evaluated first, if value is not a callable object => TypeError is thrown
argument expressions are evaluated to produce argument values,
which are assigned in order to the parameter names specified when function was defined
body of the function is executed
=> if return statement is used to return a value then that value becomes the value of the invocation expression,
otherwise value of invocation expression is undefined
! method invocation - if the expression before parentheses is a property access expression
=> the object becomes the value of the this parameter during function execution
invocation expressions that are not method invocations normally use the global object as value of the this keyword
an object creation expression creates a new object and invokes a function (called a constructor) to initialize the properties of that object
they are like invocation expressions
except that they are prefixed with the keyword new
if no arguments are passed to the constructor function in an object creation expression, the empty pair of parentheses can be omitted
new Object;
new Date;
when an object creation expression is evaluated
a new empty object is created ( just like the one created by the object initializer {})
the specified function is invoked with the specified arguments,
passing the new object as the value of the this keyword
(the function can then use this to initialize the properties of the newly created object)
functions written for use as constructors do not return a value, and the
value of the object creation expression is the newly created and initialized object
if a constructor does return an object value, that value becomes the value of the object
creation expression and the newly created object is discarded
are used for arithmetic expressions, comparison expressions,
logical expressions, assignment expressions and more
are represented by
punctuation characters (most of them)
keywords
can be categorized by
number of operands
precedence
associativity
Operator |
Operation |
Associativity |
Number of operands |
Types |
++ |
pre- or post-increment |
right-to-left |
1 |
lval → num |
-- |
pre- or post-decrement |
right-to-left |
1 |
lval → num |
- |
negate number |
right-to-left |
1 |
num → num |
+ |
convert to number |
right-to-left |
1 |
num → num |
~ |
invert bits |
right-to-left |
1 |
int → int |
! |
invert boolean value |
right-to-left |
1 |
bool → bool |
delete |
remove a property |
right-to-left |
1 |
lval → bool |
typeof |
determine type of operand |
right-to-left |
1 |
any → str |
void |
return undefined value |
right-to-left |
1 |
any → undef |
Operator |
Operation |
Associativity |
Number of operands |
Types |
*, / , % |
multiply, divide, remainder |
left-to-right |
2 |
num, num → num |
+, - |
add, subtract |
left-to-right |
2 |
num, num → num |
+ |
cncatenate strings |
left-to-right |
2 |
str, str → str |
<< |
shift left |
left-to-right |
2 |
int, int → int |
>> |
shift right with sign extension |
left-to-right |
2 |
int, int → int |
>>> |
shift right with zero extension |
left-to-right |
2 |
int, int → int |
<, <=, >, >= |
compare in numeric order |
left-to-right |
2 |
num, num → bool |
<,<=, >, >= |
compare in alphabetic order |
left-to-right |
2 |
str, str → bool |
instanceof |
test object class |
left-to-right |
2 |
obj, func → bool |
in |
test whether property exists |
left-to-right |
2 |
str, obj → bool |
Operator |
Operation |
Associativity |
Number of operands |
Types |
== |
test for equality |
left-to-right |
2 |
any, any → bool |
!= |
test for inequality |
left-to-right |
2 |
any, any → bool |
=== |
test for strict equality |
left-to-right |
2 |
any, any → bool |
!=== |
test for strict inequality |
left-to-right |
2 |
any, any → bool |
& |
compute bitwise AND |
left-to-right |
2 |
int, int → int |
^ |
compute bitwise XOR |
left-to-right |
2 |
int, int → int |
| |
compute bitwise OR |
left-to-right |
2 |
int, int → int |
&& |
compute logical AND |
left-to-right |
2 |
any, any → any |
|| |
compute logical OR |
left-to-right |
2 |
any, any → any |
? : |
choose 2nd or 3rd operand |
right-to-left |
3 |
bool, any, any → any |
= |
assign to a variable or property |
right-to-left |
2 |
lval, any → any |
*=,/+,%=,+=, -=, &=,^=,|=,<<=,>>=, >>>= |
operate and assign |
right-to-left |
2 |
lval, any → any |
, |
discard first operand, return second |
left-to-right |
2 |
any, any → any |
by the number of operands they expect (their arity)
operators are:
unary
convert a single expression into a single, more complex expression
binary
combine two expressions into a single, more complex expression
ternary
combines three expressions into a single expression
(only the conditional operator ? : )
Operand and result type
some operators work on values of any type
but
most expect their operands to be of a specific type
and
most operators return (or evaluate to) a value of a specific type
operators usually convert the type of their operands as needed
"3" * "5" // "*" expects numeric operands nut the expression is legal because the
//operands can be converted to number => result is teh number 15
and
some operators behave differently depending on the type of the operands used with them
1 + 2 // => 3, the "+" operator adds numeric values
"Hello" + "Joan" // => "Hello Joan", the "+" operator concatenates strings
"Hello" + 1 // => "Hello 1", is a
Lval
lvalue is a historical term that means
“an expression that can legally appear
on the left side of an assignment expression”
lvalues in JavaScript
variables
properties of objects
elements of arrays
ECMAScript specification allows built-in functions to return lvalues
but does not define any functions that behave that way
evaluating 2 * 3 never affects the state of the program and any future computation it performs will be unaffected by that evaluation
however some expressions have side effects
and their evaluation may affect the result of future evaluations
assignment operators
assigning a value to a variable or property changes the value of any expression that uses that variable or property
increment ++ / decrement -- operators
perform implicit assignment
delete operator
deleting a property is like - but not the same as - assigning undefined
no other JavaScript operators have side effects
but function invocation and object creation expressions will have side effects if any of the operators used in the function or constructor body have side effects
controls the order in which operations are performed
operators with higher precedence are performed before those
with lower precedence
w = x + y * z; // * has higher precedence than + and = has lowest precedence
operator precedence can be overridden with the explicit use of parentheses
w= (x + y) * z; // parantheses force the addition from the previous example
specifies the order in which
operations of the same precedence are performed
left-to-right associativity
means that operations are performed from left to right
w = x - y - z; // is the same as w = ((x - y) - z)
w = x = y = z; // is the same as w = (x = (y = z))
a?b:c?d:e?f:g; // is the same as a?b:(c?d:(e?f:g))
operator precedence and associativity specify the order in which operations are performed in a complex expression
but
they do not specify the order in which the subexpressions are evaluated
JavaScript always evaluates expressions in strictly left-to-right order
w = x + y * z; // 1. subexpression w is eveluated first, followed by x,y and z
// 2. values of y and z are multiplied, added to the value of x and
// assigned to the variable/property specified by the expression w
order of evaluation only makes a difference if any of the expressions being evaluated has side effects that affect the value of another expression
if expression x increments a variable that is used by expression z, then the fact that x is evaluated before z is important
* multiplication
/ division
% modulo (remainder after division)
- subtraction
+ addition
unary operators
bitwise operators
* / % -
evaluate their operands
convert the values to numbers if necessary
and
compute the product, quotient, remainder, or difference
between the values
non-numeric operands that cannot convert to numbers convert to the NaN value
if either operand is (or converts to) NaN the result of the operation is also NaN
! in JavaScript all numbers are floating-point so all division operations have floating-point results
also remember that neither of these cases raises an errors:
division by zero yields positive or negative infinity,
0/0 evaluates to NaN
the binary + operator
adds numeric operands
or
concatenates string operands
1 + 2 // => 3, both operands are numbers
"1" + "2" // => "12", both operands are strings
What happens in any other case,
when it is not so obvious what the + operator does ?
=> type conversion is necessary
1 + "2" // => "12", concatenation after number-to-string conversion
1 + {} // => "1[object Object]", concatenation after object-to-string
true + true // => 2, addition after boolean-to-number conversion
2 + null // => 2, addition after null converts to 0
2 + undefined // => NaN, addition after undefined converts to NaN
1 + 2 + "apples"; // => "3 apples"
1 + (2 + "apples"); // => "12 apples"
modify the value of a single operand to produce a new value
all have high precedence
and
are all right-associative
+ / - / ++ / --
convert their single operand to a number, if necessary
Unary plus (+)
converts its operand to a number (or to NaN) and returns that converted value
when used with an operand that is already a number, it doesn’t do anything
Unary minus (-)
converts its operand to a number, if necessary, and then changes the sign of teh result
Increment (++)
its single operand must be an lvalue (variable, element of an array, property of an object)
converts its operand to a number, adds 1 to that number, and assigns the incremented value back
the return value depends on its position relative to the operand (pre-/post- increment)
Increment (++)
when used before the operand it increments the operand and evaluates to the incremented value of that operand
when used after the operand it increments its operand but evaluates to the unincremented value of that operand
var i = 1, j = ++i; // i and j are both 2
var i = 1, j = i++; // i is 2, j is 1
! ++x is not always the same as x = x+1 as ++ never performs string concatenation var x = "1";
++x; // => number 2
x = x + 1; // => string "11"
perform low-level manipulation of the bits in the binary representation of numbers
don't perform traditional arithmetic operations
but
operate on numeric operands and return a numeric value
perform Boolean algebra on the individual bits of the operands,
behaving as if each bit in each operand were a boolean value (1=true, 0=false)
expect integer operands and behave as if those values were represented as 32-bit integers rather than 64-bit floating-point values
convert their operands to numbers, if necessary, and then coerce the numeric values to
32-bit integers by dropping any fractional part and any bits beyond the 32nd
! not commonly used in JavaScript programming
Bitwise AND (&)
performs boolean AND on each bit of its integer arguments
a bit is set in the result only if the corresponding bit is set in both operands
0x1234 & 0x00FF // evaluates to 0x0034
Bitwise OR (|)
performs boolean OR operation on each bit of its integer arguments
a bit is set in the result if the corresponding bit is set in one or both of the operands
0x1234 | 0x00FF // evaluates to 0x12FF
Bitwise XOR (^)
performs a Boolean exclusive OR operation on each bit of its integer arguments
a bit is set in this operation’s result if a corresponding bit is set in one (but not both) of the two operand
0xFF00 ^ 0xF0F0 // evaluates to 0x0FF0
Bitwise NOT (~)
operates by reversing all bits in the integer operand it appears before
because of signed integers representation => is equivalent to changing the sign and subtracting 1
~0x0F // evaluates to 0xFFFFFFF0
Shift left (<<)
Shift right with sign (>>)
Shift right with zero fill (>>>)
JavaScript supports
==
===
operators
What is the difference ?
= assignment
== equality
=== strict equality
! NOT TO BE CONFUSED
== and === operators
check whether two values are the same
using two different definitions of sameness
both accept operands of any type
both return true if their operands are the same
and false if they are different
BUT
=== checks whether its two operands are “identical”
using a strict definition of sameness
== checks whether its two operands are “equal”
using a more relaxed definition of sameness that allows type conversions
strict equality operator ===
evaluates its operands and then compares the two
values performing no type conversion
if the two values have different types, they are not equal
if both values are null or both undefined, they are equal
if both values are the boolean value true or both false, they are equal
if one or both values is NaN, they are not equal
! remember NaN is never equal to any other value, including itself
if both values are numbers and have the same value, they are equal (also 0 === -0)
if both values are strings and contain exactly the same 16-bit values in the same positions, they are equal
! same visual appearance, but different encoding => strings are not considered equal
if both values refer to the same object, array or function, they are equal
if they refer to different objects they are not equal even if both objects have identical properties
! remember that objects are compared by reference, not by value
equality operator ==
acts like strict equality operator, but is less strict
in case the values of the two operands are not the same type
it attempts some type conversions and tries the comparison again
if the two values have the same type, test them for strict equality
if they don't have the same type, the == operator uses the following rules:
- one value is null and the other undefined, they are equal
- one value is number and the other one a string, convert string to number
and try to compare again
- either value is true, convert it to 1 and try the comparison again
- either value is false, convert it to 0 and try the comparison again
- one value is an object and the other is a number or string, convert the object
to a primitive and try the comparison again
- ! any other combinations of values are not equal
inequality operators
!=
returns false if two values are equal to each other according
to == and returns true otherwise
!==
returns false if two values are strictly
equal to each other and returns true otherwise
test the relative order (numerical or alphabetical) of their two operands
less than (<)
true if its first operand is less than its second operand, otherwise false
greater than (>)
true if its first operand is greater than its second operand, otherwise false
less than or equal (<=)
true if its first operand is less than or equal to its second operand, otherwise false
greater than or equal (>=)
true if its first operand is greater than or equal to its second operand, otherwise false
operands may be of any type
but
comparison can be performed only on numbers and strings
so
operands that are not numbers or strings are converted
as follows:
- if either operand evaluates to an object, it is converted to a primitive value
- after conversion if both operands are strings, the two strings are compared using alphabetical order
- otherwise, if at least one operand is not a string, both operands are converted to numbers and compared numerically
! 0 and -0 are considered equal
! Infinity / -Infinity are larger / smaller than any number other than itself
! if either operator is or converts to NaN, comparison operator always return false
as strings are sequences of 16-bit values,
string comparison is just a numerical comparison of the values in the two strings
the numerical encoding order defined by Unicode
may not match the traditional collation order used in any particular language or locale
so
for a more robust string-comparison algorithm,
the String.localeCompare() method should be used
string comparison is case-sensitive
so
for case-insensitive comparisons, converting strings using
String.toLowerCase() or String.toUpperCase() is necessary
the <= (less than or equal) and >= (greater than or equal)
operators do not rely on the equality or strict equality operators for determining whether two values are “equal”
the less-than-or-equal operator is simply defined as “not greater
than” and the greater-than-or-equal operator is defined as “not less than”
the one exception occurs when either operand is (or converts to) NaN, in which case all four comparison operators return false
expects a left-side operand that is or can be converted to a string and
a right-side operand that is an object
evaluates to true if the left-side value is the name of a property of the right-side object
var person = { name: John, age: 27 }; // define an object
"age" in person // => true, object has property named "age"
"gender" in person // => false, object has no "gender" property
"toString" in person // => true, object inherits toString method
var data = [4,5,6]; // an array with elemnts 0,1 and 2
"0" in data // => true, array has an element "0"
1 in data // => true, numbers are converted to strings
3 in data // => false, no element 3
expects a left-side operand that is an object and a right-side
operand that identifies a class of objects
evaluates to true if the left-side object is an instance of the right-side class and to false otherwise
in JavaScript, classes of objects are defined by the constructor function that initializes them
var a = new Array(); // create new object with the Array() constructor
a instanceof Array; // => true, a is an array
a instanceof Object; // => true, all arrays are instances of Object
a instanceof Number; // => false, a is not a Number object
! all objects are instances of Object
instanceof considers the "superclasses" when deciding whether an object is an instance of a class
if the left-side operand is not an object, instanceof returns false
if the right-hand side is not a function, instanceof throws a TypeError
this operator works using the "prototype chain", JavaScript's inheritance mechanism which will be discussed later
logical operators
&& || !
perform Boolean algebra
and
are often used in conjunction with the relational operators
to combine two relational expressions into one more complex expression
performs Boolan AND on its operands
&& returns true only if both are true and false otherwise
but && does not require that its operands be boolean
null/undefined/0/-0/NaN/"" are falsy values, all other values are truthy
operands are truthy/falsy values?
&& returns a truthy value if both are truthy values and a falsy value otherwise
but what exactly is the returned value?
how does the && operator work?
&& starts by evaluating its left operand
if this value is falsy the entire expression is falsy,
so this is the value that is returned
! the value of the right expression is not even evaluated
if value is truthy, the overall value of the expression
depends of the value of the right operand
whose value is the value returned
performs Boolean OR on its operands
|| returns a truthy value if either one of its operands is truthy
and a falsy value only if both are falsy values
but what exactly is the returned value?
|| starts by evaluating the value of the left operand
if the value is truthy, this value is the returned value
otherwise, the right operand is evaluated
whose value is the returned value
! || may or may not evaluate the second operators so,
as with the && operator, expressions with side effects (assignments, increments, decrements or function invocations) should be avoided unless you purposely want to exploit this
an idiomatic use of the || operator is to select
the first truthy value in a set of alternatives (even to supply defaults)
is a unary operator placed before a single operand
its purpose is to invert the boolean value of its operand
unlike && and ||, this operator converts
its operand to a boolean value,
which means it always returns true or false
! any value can be converted to its boolean
equivalent by applying this operator twice
as a unary operator, ! has high precedence
! (p && q) === !p || !q
!(p || q) === !p && !q
the = operator is used to assign a value to a variable or property
the left-side operand is expected to be an lvalue (variable, object property, array element)
the right-side operand is expected to be an arbitrary value of any type
the value of an assignment expression is the value of the right-side operand
as side effect, the future references to the variable/property evaluate to the value
are usually quite simple expressions, but can sometimes be used as part of larger expressions
! = has very low precedence and parentheses are usually necessary in such cases
"=" has very right-to-left associativity , which means that when multiple assignment operators appear in an expression, they are evaluated from right to left
i=j=k=0; => all get the values 0
besides the normal = assignment operator
a number of other assignment operators that provide shortcuts by combining assignment with some other operation are supported
+= -= *= /= %= <<= >>= >>>= &= |= ^=
in most cases, the expression
x op= y
where op is an operator, is equivalent to the expression
x = x op y
in the first line, the expression x is evaluated once
in the second it is evaluated twice
the two cases will differ only if x includes side effects such as a function call or an increment operator
like in many interpreted languages, there is the ability to interpret strings of JavaScript source code, evaluating these to produce a value
this is done with the global function eval()
dynamic evaluation of strings of source code is a powerful language feature that is almost never necessary in practice, so one should think carefully about whether using it is really needed
eval() is a function, but it is included in the expressions part because it really should have been an operator
language designers and interpreter writers have been placing restrictions on it that make it more and more operator-like
modern JS interpreters perform a lot of code analysis and optimization
the problem with eval() is that the code it evaluates is, in general, unanalyzable
generally speaking, if a function calls eval(), the interpreter cannot optimize that function
expects one argument :
if passed value is any value other than a string, it simply returns that value
if a string is passed, it attempts to parse the string as JavaScript code:
if it fails, it throws a SyntaxError
if it successfully parses the string, then it evaluates the code and returns the value of the last expression or statement in the string or undefined if the last expression or statement had no value; if the string throws an exception, the eval() propagates that expression
eval() uses the variable environment of the code that calls it
it looks up the values of variables and defines new variables
and functions in the same way that local code does
! the code passed to eval must make syntactic sense on its own
...global, local etc...
the only ternary operator in JS
the operands may be of any type
the first operand goes before "?"
and is evaluated and interpreted as a boolean
if the value is truthy then the second operand, which goes between "?" and ":", is evaluated and its value returned
if the values of the first operand is falsy, the third operand, which goes after ":", is evaluated and its value returned
only one of the second and third operands is evaluatd, never both
this is often a handy shortcut for an "if" (equivalent but more compact)
is a unary operator that is placed before its single operand
the operand can be of any type
its value is a a string that specifies the type of the operand
x typeof x
undefined "undefined"
null "object"
true or false "boolean"
any number or NaN "number"
any string "string"
any function "function"
any nonfunction native object "object"
any host object an implementation-defined string, but not “undefined”, “boolean”, “number”, or “string”.
is a unary operator that appears before its single operand, which may be of any type
is unusual and infrequently used: it evaluates its operand, then discards the value and returns undefined
since the operand value is discarded, using the void operator makes sense only if the operand has side effects
the most common use is in a client-side javascript: URL, where it
allows you to evaluate an expression for its side effects without the browser displaying the value of the evaluated expression
is a binary operator whose operands may be of any type
it evaluates its left operand, evaluates its right operand, and then returns the value of the right operand
the left-hand expression is always evaluated, but its value is discarded, which means that it only makes sense to use the comma operator when the left-hand expression has side effects
the only situation in which the comma operator is commonly used is with a for loop that has multiple loop variables