This chapter gives an overview of operators.
Operators and Objects
All operators Type Coercion) their operands to appropriate types.Most operators only work with primitive values (e.g., arithmetic operators and comparison operators).
There is no way to overload or customize operators in JavaScript, not even equality.
Assignment Operators
There are several ways to use the
- Assigns to a variable
x
that has previously been declared var x = value
- Combines a variable declaration with an assignment
obj.propKey = value
- Sets a property
obj['propKey'] = value
- Sets a property
arr[index] = value
- Sets an array element[10]
An assignment is an expression that evaluates to the assigned value. That allows you to chain assignments. For example, the following statement assigns 0
to both y
and x
:
x
=
y
=
0
;
Compound Assignment Operators
A compound assignment operator is written as op=
, where op
is one of several binary operators and =
is the assignment operator. The following two expressions are equivalent:
myvar
op
=
value
myvar
=
myvar
op
value
In other words, a compound assignment operator op=
applies op
to both operands and assigns the result to the first operand. Let’s look at an example of using the plus operator (+
) via compound assignment:
- > var x = 2;
- > x += 3
- 5
- > x
- 5
- Arithmetic operations (see Arithmetic Operators):
*=
,/=
,%=
,+=
,-=
- Bitwise operations (see ):
<<=
,>>=
,>>>=
,&=
,^=
,|=
- String concatenation (see Concatenation: The Plus (+) Operator):
+=
JavaScript has two ways of determining whether two values are equal:
- Strict equality (
===
) and strict inequality (!==
) consider only values that have the same type to be equal. - Normal (or “lenient”) equality (
==
) and inequality (!=
) try to convert values of different types before comparing them as with strict (in)equality.
Lenient equality is problematic in two regards. First, how it performs conversion is confusing. Second, due to the operators being so forgiving, type errors can remain hidden longer.
Always use strict equality and avoid lenient equality. You only need to learn about the latter if you want to know why it should be avoided.
Equality is not customizable. Operators can’t be overloaded in JavaScript, and you can’t customize how equality works. There are some operations where you often need to influence comparison—for example, Array.prototype.sort()
(see ). That method optionally accepts a callback that performs all comparisons between array elements.
Strict Equality (===, !==)
Values with different
undefined === undefined
null === null
- Two numbers:
x
===
x
// unless x is NaN
+
0
===
-
0
NaN
!==
NaN
// read explanation that follows
- Two booleans, two strings: obvious results
- Two objects (including arrays and functions):
x === y
if and only ifx
andy
are the same object; that is, if you want to compare different objects, you have to implement your own comparison algorithm:
- > var b = {}, c = {};
- > b === c
- false
- > b === b
- true
- Everything else: not strictly equal.
Pitfall: NaN
The special number value NaN
(see ) is not
- > NaN === NaN
- false
Thus, you need to use other means to check for it, which are described in Pitfall: checking whether a value is NaN.
Strict inequality (!==)
x
!==
y
is equivalent to the negation of a strict equality comparison:
!
(
x
===
y
)
Normal (Lenient) Equality (==, !=)
Otherwise, if the operands are:
undefined
andnull
, then they are considered leniently equal:
- > undefined == null
- true
- A string and a number, then convert the string to a number and compare both operands via strict equality.
- A boolean and a nonboolean, then convert the boolean to a number and compare leniently (again).
- An object and a number or a string, then try to convert the object to a primitive (via the algorithm described in ) and compare leniently (again).
Otherwise—if none of the aforementioned cases apply—the result of the lenient comparison is false
.
Lenient inequality (!=)
An inequality comparison:
x
!=
y
is equivalent to the negation of an equality comparison:
!
(
x
==
y
)
Pitfall: lenient equality is different from conversion to boolean
Step 3 means that equality and conversion toConverting to Boolean) work differently. If converted to boolean, numbers greater than 1 become true
(e.g., in if
statements). But those numbers are not leniently equal to true
. The comments explain how the results were computed:
- > 2 == true // 2 === 1
- false
- > 2 == false // 2 === 0
- false
- > 1 == true // 1 === 1
- true
- > 0 == false // 0 === 0
- true
Similarly, while the empty
- > '' == false // 0 === 0
- true
- > '1' == true // 1 === 1
- true
- > '2' == true // 2 === 1
- false
- > 'abc' == true // NaN === 1
- false
Pitfall: lenient equality and strings
Some of the leniency can be useful, depending
- > 'abc' == new String('abc') // 'abc' == 'abc'
- true
- > '123' == 123 // 123 === 123
- true
Other cases are problematic, due to how JavaScript converts strings to numbers (see Converting to Number):
- > '\n\t123\r ' == 123 // usually not OK
- true
- > '' == 0 // 0 === 0
- true
Pitfall: lenient equality and objects
If you compare an object to a nonobject, it is converted to a primitive, which leads to strange results:
- > {} == '[object Object]'
- true
- > ['123'] == 123
- true
- > [] == 0
- true
However, two objects are only equal if they are they same object. That means that you can’t really compare two wrapper objects:
- > new Boolean(true) === new Boolean(true)
- false
- > new Number(123) === new Number(123)
- false
- > new String('abc') == new String('abc')
- false
Use case: checking for undefined or null
While this is a compact way of writing this check, it confuses beginners, and experts can’t be sure whether it is a typo or not. Thus, if you want to check whether x
has a value, use the standard check for truthiness (covered in Truthy and Falsy Values):
if
(
x
)
...
If you want to be more precise, you should perform an explicit check for both values:
if
(
x
!==
undefined
&&
x
!==
null
)
...
Use case: working with numbers in strings
if
(
x
==
123
)
...
The preceding checks whether x
is either 123
or '123'
. Again, this is very compact, and again, it is better to be explicit:
if
(
(
x
)
===
123
)
...
Use case: comparing wrapper instances with primitives
Lenient equals lets you compare primitives with wrapped primitives:
- > 'abc' == new String('abc')
- true
There are three reasons against this approach. First, lenient
- > new String('abc') == new String('abc')
- false
Second, you should avoid wrappers anyway. Third, if you do use them, it is better to be explicit:
if
(
wrapped
.
valueOf
()
===
'abc'
)
...
Ordering Operators
JavaScript knows the following ordering operators:
- Less than (
<
) - Less than or equal (
<=
) - Greater than (
>
) - Greater than or equal (
>=
)
These operators work for
- > 7 >= 5
- true
- > 'apple' < 'orange'
- true
For strings, they are not very useful, because they are case-sensitive and don’t handle features such as accents well (for details, see Comparing Strings).
The Algorithm
You evaluate a comparison:
x
<
y
by taking the following steps:
- Ensure that both operands are primitives. Objects
obj
are converted to primitives via the internal operationToPrimitive(obj, Number)
(refer to ), which callsobj.valueOf()
and, possibly,obj.toString()
to do so. - If both operands are strings, then compare them by lexicographically comparing the 16-bit code units (see Chapter 24) that represent the JavaScript characters of the string.
- Otherwise, convert both operands to numbers and compare them numerically.
The other ordering operators are handled similarly.
The Plus Operator (+)
Roughly, the plus operator examines its operands. If one of them is a string, the other is also converted to a string and both are concatenated:
- > 'foo' + 3
- 'foo3'
- > 3 + 'foo'
- '3foo'
- > 'Colors: ' + [ 'red', 'green', 'blue' ]
- 'Colors: red,green,blue'
Otherwise, both operands are converted to numbers (see ) and added:
- > 3 + 1
- 4
- > 3 + true
- 4
That means that the order in which you evaluate matters:
- > 'foo' + (1 + 2)
- 'foo3'
- > ('foo' + 1) + 2
- 'foo12'
The Algorithm
You evaluate an addition:
value1
+
value2
by taking the following steps:
- Ensure that both operands are primitives. Objects
obj
are converted to primitives via the internal operationToPrimitive(obj)
(refer to ), which callsobj.valueOf()
and, possibly,obj.toString()
to do so. For dates,obj.toString()
is called first. - If either operand is a string, then convert both to strings and return the concatenation of the results.
- Otherwise, convert both operands to numbers and return the sum of the results.
The following operators only have operands of a single type and also produce results of that type. They are covered elsewhere.
Boolean operators:
- Binary logical operators (see ):
x
&&
y
,
x
||
y
- Logical Not (see Logical Not (!)):
!
x
Number operators:
- Arithmetic operators (see ):
x
+
y
,
x
-
y
,
x
*
y
,
x
/
y
,
x
%
y
++
x
,
--
x
,
x
++
,
x
--
-
x
,
+
x
- Bitwise operators (see Bitwise Operators):
~
x
x
&
y
,
x
|
y
,
x
^
y
x
<<
y
,
x
>>
y
,
x
>>>
y
Special Operators
Here we will review special operators, namely the conditional, comma, and void
operators.
The Conditional Operator ( ? : )
If the condition is true
, the result is if_true
; otherwise, the result is if_false
. For example:
var
x
=
(
obj
?
obj
.
prop
:
null
);
The parentheses around the operator are not needed, but they make it easier to read.
«
left
»
,
«
»
The comma operator evaluates both operands and returns the result of right
. Roughly, it does for expressions what the semicolon does for statements.
This example demonstrates that the second operand becomes the result of the operator:
- > 123, 'abc'
- 'abc'
- > var y = (x++, 10);
- > x
- 1
- > y
- 10
The comma operator is confusing. It’s better to not be clever and to write two separate statements whenever you can.
The void Operator
The syntax for the void
operator
void
«
expr
»
which evaluates expr
and returns undefined
. Here are some examples:
- > void 0
- undefined
- > void (0)
- undefined
- > void 4+7 // same as (void 4)+7
- NaN
- > void (4+7)
- undefined
- > var x;
- > x = 3
- 3
- > void (x = 5)
- undefined
- > x
- 5
Thus, if you implement void
as a function, it looks as follows:
function
myVoid
(
expr
)
{
return
undefined
;
}
The void
operator is associated closely with its operand, so use parentheses as necessary. For example, void 4+7
binds as (void 4)+7
.
What is void used for?
Under ECMAScript 5, void
is rarely useful. Its main
void 0
as a synonym forundefined
- The latter can be changed, while the former will always have the correct value. However,
undefined
is reasonably safe from being changed under ECMAScript 5, which makes this use case less important (for details, see Changing undefined). - Discarding the result of an expression
- In some situations, it is important to return
undefined
as opposed to the result of an expression. Thenvoid
can be used to discard that result. One such situation involvesjavascript:
URLs, which should be avoided for links, but are useful for bookmarklets. When you visit one of those URLs, many browsers replace the current document with the result of evaluating the URL’s “content,” but only if the result isn’tundefined
. Hence, if you want to open a new window without changing the currently displayed content, you can do the following:
javascript
:
void
window
.
open
(
"http://example.com/"
)
- Prefixing an IIFE
- An IIFE must be parsed as an expression. One of several ways to ensure that is by prefixing it with
void
(see ).[11]
Why does JavaScript have a void operator?
I added the void
operator to JS before Netscape 2 shipped to make it easy to discard any non-undefined value in a javascript: URL.[]
Categorizing Values via typeof and instanceof
If you want toChapter 8) in JavaScript:
- The
typeof
operator distinguishes primitives from objects and determines the types of primitives. - The
instanceof
operator determines whether an object is an instance of a given constructor. Consult for more information on object-oriented programming in JavaScript.
typeof: Categorizing Primitives
The typeof
operator:
typeof
«
value
»
returns a string describing what kind of value value
is. Here are some examples:
- > typeof undefined
- 'undefined'
- > typeof 'abc'
- 'string'
- > typeof {}
- 'object'
- > typeof []
- 'object'
typeof
is used to distinguish primitives and objects and to categorize primitives (which cannot be handled by instanceof
). Unfortunately, the results of this operator are not completely logical and only loosely correspond to the types of the ECMAScript specification (which are explained in JavaScript’s Types):
Pitfall: typeof null
Unfortunately, typeof null
is 'object'
. This is considered a bug (null
is not a member of the internal type Object), but it can’t be fixed, because doing so would break existing code. You thus have to be wary of null
. For example, the following function checks whether value
is an object:
function
isObject
(
value
)
{
return
(
value
!==
null
&&
(
typeof
value
===
'object'
||
typeof
value
===
'function'
));
}
Trying it out:
- > isObject(123)
- false
- > isObject(null)
- false
- > isObject({})
- true
The history of typeof null
The first JavaScript engine represented JavaScript values as 32-bit words.
The type tag for objects was 000. In order to represent the value null
, the engine used the machine language NULL pointer, a word where all bits are zero. typeof
checked the type tag to determine the type of value, which is why it reported null
to be an object.[13]
Checking whether a variable exists
typeof
x
===
'undefined'
has two use cases:
- It determines whether
x
isundefined
. - It determines whether the variable
x
exists.
Here are examples of both use cases:
- > var foo;
- > typeof foo === 'undefined'
- true
- > typeof undeclaredVariable === 'undefined'
- true
For the first use case, comparing directly with undefined
is usually a better choice. However, it doesn’t work for the second use case:
- > var foo;
- > foo === undefined
- true
- > undeclaredVariable === undefined
- ReferenceError: undeclaredVariable is not defined
instanceof: Checking Whether an Object Is an Instance of a Given Constructor
The instanceof
operator:
«
value
»
instanceof
«
Constr
»
determines whether value
has been created by the constructor Constr
or a subconstructor. Here are some examples:
- > {} instanceof Object
- true
- > [] instanceof Array // constructor of []
- true
- > [] instanceof Object // super-constructor of []
- true
As expected, instanceof
is false
for the nonvalues undefined
and null
:
But it is also false
for all other primitive values:
- > 'abc' instanceof Object
- false
- > 123 instanceof Object
- false
For details on instanceof
, consult .
The following three operators work on objects. They are explained elsewhere:
new
(see )- Invoke a constructor—for example,
new Point(3, 5)
delete
(see Deleting properties)- Delete a property—for example,
delete obj.prop
in
(see )- Check whether an object has a given property—for example,
'prop' in obj
[10] Strictly speaking, setting an array element is a special case of setting a property.
[] Thanks to Brandon Benvie (@benvie), who told me about using for IIFEs.
[12] Source: