The rest of the operators
Finish JavaScript's operator toolbox: ternary choices, type checks, property checks, delete, bitwise flags, shifts, comma, void, and the precedence table.
- 01Pick one of two valuesUse the ternary,
? :, for a small choice, and switch toif/elsewhen choices pile up. - 02Check values and propertiesChoose the right check:
typeof,instanceof,Array.isArray,in, orObject.hasOwn. - 03Read rare operatorsWork out what bitwise flags, shifts, the comma operator,
void, and the precedence table do.
Operators you haven't met yet
An operator is a symbol or a word that works on values. For example, + adds two numbers. You already know many operators:
- Math:
+,-,*, and/. - Assignment:
=and+=. - Comparisons:
===and<. - Logical operators:
&&,||, and!. - Nullish coalescing,
??, and optional chaining,?..
This lesson covers the rest. Some are common, like the ternary, ? :. Some are rare, like void. A few are powerful but easy to misuse, like the bitwise operators.
The goal is not clever code. In the Coding style lesson, you learned to avoid tricks that make readers stop and think. Here you learn what each operator means, so you can read it in real code. You also learn when an if statement or a well-named function would be clearer.
- Use the ternary for one small choice between two values.
- Pick the type check or property check that answers your exact question.
- Bitwise operators turn a number into a 32-bit whole number first. Use them for on/off settings.
- When you are not sure which operator goes first, add round brackets.
A toolbox holds more tools than you use every day. You don't cut wood with a measuring tape. When you know what each tool is for, you pick the right one. You also recognize a tool when someone else has used it.
- In real life: A screwdriver you use every day
- In JavaScript:
+,===,&&, and?? - In real life: A small pocket knife for quick jobs
- In JavaScript: The ternary, for one small choice
- In real life: A measuring tape that tells you what you have
- In JavaScript:
typeof,instanceof,in, andObject.hasOwn - In real life: A tiny special screwdriver for one rare job
- In JavaScript: Bitwise operators, for on/off settings
The lesson goes in this order:
- The ternary, for a small choice.
typeofandinstanceof, for checking what kind of value you have.inanddelete, for checking and removing properties.- Bitwise operators, which work on the 0s and 1s inside a number.
- Shifts, the comma operator, and
void. - A table that shows which operator goes first.
The conditional (ternary) operator
STEP THROUGHThe values an operator works on are called its operands. In 2 + 3, the operands are 2 and 3. Most operators have one or two operands. The conditional operator is the only one with three. That is why people also call it the ternary operator. "Ternary" means "made of three parts". Its shape is:
condition ? valueIfTrue : valueIfFalseRead it from left to right:
- JavaScript checks the
condition. - If the condition is truthy (it counts as true), the result is
valueIfTrue, the part after?. - If not, the result is
valueIfFalse, the part after:.
The whole ternary is an expression: a piece of code that produces a value. So you can store its result in a variable or return it from a function. You can also put it inside a template literal, a string written in backticks.
age >= 18 is true, you get "adult". If it is false, you get "minor".At a concert, a staff member checks your age and hands you one of two wristbands. There is one question and two possible answers. You leave with exactly one wristband. A ternary is good at this kind of choice.
- In real life: The question: "Are you 18 or older?"
- In JavaScript: The condition:
age >= 18 - In real life: Yes: you get an adult wristband
- In JavaScript: The value after
?:"adult" - In real life: No: you get a minor wristband
- In JavaScript: The value after
::"minor" - In real life: The one wristband you leave with
- In JavaScript: The value the ternary gives back
Guess which value the ternary picks. Then press Step to check, one step at a time.
script
const label = age >= 18 ? "adult" : "minor";const sentence = `This visitor is an ${label}.`;console.log(sentence);A good ternary is short enough to read in one go:
const shipping = cartTotal >= 500 ? "free" : "$5";
console.log(`Shipping is ${shipping}.`);Before this code runs, cartTotal is set to 650. Line 1 checks whether cartTotal is 500 or more. It is, so shipping gets "free". Line 2 prints Shipping is free.
Here is one more small example. It picks a delivery price for a smaller cart:
const cartTotal = 420;
const delivery = cartTotal >= 500 ? "free" : "$5";
console.log(delivery);Line 1 stores the cart total, 420. Line 2 checks whether 420 is 500 or more. It isn't, so delivery gets "$5". Line 3 prints $5.
A ternary can hold another ternary inside it. This is called a nested ternary. It makes the reader keep several questions in mind at once:
const label = score >= 90 ? "A" : score >= 80 ? "B" : "keep practicing";
let readableLabel;
if (score >= 90) {
readableLabel = "A";
} else if (score >= 80) {
readableLabel = "B";
} else {
readableLabel = "keep practicing";
}Both versions give the same result. Line 1 is the nested ternary. Lines 3 to 10 do the same job with if and else. Each choice gets its own line, so it is easier to read.
The next lesson, if, else & conditions, covers if in full. For now, use this rule:
- Use a ternary for one small choice between two values.
- Use
if/elsewhen the choice needs more room.
typeof & instanceof
STEP THROUGHtypeof goes in front of a value. It gives back a string that names the kind of value. It works best for primitives: simple values that are not objects, like strings, numbers, and booleans. Its answers are "string", "number", "boolean", "bigint", "symbol", and "undefined". For a function, it says "function". For most objects, including arrays and dates, it just says "object".
instanceof asks a different question. A constructor is a function that makes objects, like Date or Array. value instanceof Date asks: "Was this object made by Date?" An array counts as an Array and also as an Object, because every array is a kind of object. The prototype chain lesson later explains exactly how JavaScript checks this.
Step through the checks. Notice that typeof, instanceof, and Array.isArray each answer a different question.
script
console.log(typeof values[0]);console.log(Array.isArray(values[1]));console.log(values[1] instanceof Array);console.log(values[1] instanceof Object);console.log(values[3] instanceof String);The table below helps you pick a check. Find your question in the first column. Use the check in the second column, and read the warning in the third.
| Your question | Check to use | Watch out for |
|---|---|---|
| Is it a string, and not an object? | typeof value === "string" | new String("x") makes an object, so typeof says "object" for it. |
| Is it missing? | value === null or value === undefined | typeof null gives "object". This is an old mistake in JavaScript. |
| Is it an array? | Array.isArray(value) | typeof [] gives "object", so typeof can't tell. |
| Was it made by a certain constructor? | value instanceof Date | A value from another frame on the page, like an iframe, was made by that frame's own Date, so the check says false. You can learn this detail later. |
typeof null is still objectThis is a very old bug in JavaScript. Fixing it now would break existing websites, so it stays. The Data types lesson first showed it. Because of it, careful code checks for null directly, with value === null.
A common mistake is typeof value === "array". typeof never gives "array", so this check is always false. Use Array.isArray(value) for arrays. Use instanceof only when your question is "Which constructor made this object?"
in & delete
STEP THROUGH"key" in object checks whether JavaScript can find a property with that name on the object. It gives true or false.
It also counts inherited properties. These are properties an object borrows. Every plain object can use some built-in methods, like toString, that it borrows from a shared built-in object. So "toString" in user is true, even though you never added toString to user.
To check only the properties stored on the object itself, use Object.hasOwn(object, "key"). Properties stored on the object itself are called own properties.
user holds only name. It borrows toString() from a built-in object. in looks in both places, so it says true. Object.hasOwn looks in user only, so it says false.delete object.key removes a property from an object. This is useful for an object that you use as a list of named entries. It is not a good way to remove an item from an array. Deleting an array item leaves an empty place, called a hole. The array keeps the same length.
Follow in, Object.hasOwn, and delete. Watch the array hole: the length stays 3, but index 1 is gone.
script
console.log("name" in user);console.log("toString" in user);console.log(Object.hasOwn(user, "toString"));delete user.age;console.log("age" in user);const letters = ["a", "b", "c"];delete letters[1];console.log(letters.length);console.log(1 in letters);delete letters[1] takes out the value at index 1 but leaves its place empty. That empty place is a hole. Nothing moves left, so length stays 3.Some built-in properties are locked: JavaScript does not let you delete them. For example, the prototype property of Object is locked. In older non-strict scripts, delete Object.prototype fails quietly and gives false. In strict mode, it throws a TypeError. Modern modules use strict mode automatically, so a failed delete shows an error instead of doing nothing.
The table compares the three. Read each row as: this code, does it count borrowed properties, and when to use it.
| Code | Does it count borrowed properties? | When to use it |
|---|---|---|
"name" in user | Yes. | When a borrowed property should count too, like checking whether a feature exists. |
Object.hasOwn(user, "name") | No. | When you only care about data stored on this object itself. |
delete user.age | It doesn't check anything. It removes a property. | When you want a property gone from an object. |
For a plain data object, Object.hasOwn is usually clearer than in. It says the key belongs to this object itself.
To remove items from an array, use filter or splice. They close the gap, so the array gets shorter. delete leaves a hole instead.
Bitwise operators: & | ^ ~
INTERACTIVEIn the Numbers & math lesson, you learned that JavaScript stores numbers as 64-bit floating-point values. Bitwise operators work differently:
- First, JavaScript turns the number into a 32-bit whole number. Any fraction is dropped.
- It treats that whole number as a row of 32 bits. A bit is a tiny switch that is either 0 (off) or 1 (on).
- Then the operator works on the bits, one column at a time.
Picture two rows of light switches. & asks which switches are on in both rows. | asks which are on in either row. ^ asks which switches are different. ~ flips every switch in one row.
- In real life: Switch off
- In JavaScript: Bit
0 - In real life: Switch on
- In JavaScript: Bit
1 - In real life: Both switches must be on
- In JavaScript:
& - In real life: At least one switch is on
- In JavaScript:
| - In real life: Exactly one switch is on
- In JavaScript:
^ - In real life: Flip every switch
- In JavaScript:
~
& gives 1 only where both a and b have 1. Only the first column has two 1s, so the answer is 1000, which is 8.const a = 12; // 00001100const b = 10; // 00001010console.log(a & b); // both bits are 1console.log(a | b); // either bit is 1console.log(a ^ b); // bits are differentconsole.log(~a); // flip 32 bitsconsole.log(a << 1); // shift leftconsole.log(a >> 1); // shift rightconsole.log(a >>> 1); // unsigned shift right00001000both bits are 100001110either bit is 100000110bits differ11110011flip all 32 bits00011000slide left00000110slide right, keep the sign00000110slide right, fill with 0a is 12 (00001100 in bits), and b is 10 (00001010 in bits). JavaScript turns both into 32-bit whole numbers first. This playground shows only the last 8 bits, so they fit on the screen. Click a bit to switch it on or off.
~ and >>> still work on all 32 bits. That is why ~12 is negative.The next code shows three results that surprise people. Each one comes from the 32-bit rule:
console.log(2 ** 32 | 0); // 0: wraps to a 32-bit signed integer
console.log(~5); // -6: flip all 32 bits
console.log(-1 >>> 0); // 4294967295: read all bits as unsigned**means "to the power of"; you'll see it again below.2 ** 32is 4294967296. That number needs 33 bits, so its 32-bit version is0.~5flips all 32 bits of 5. The result is-6. For whole numbers,~xalways equals-(x + 1).-1has all 32 bits switched on.>>> 0reads those bits as a number that can't be negative, called an unsigned number. That gives4294967295.
A clear, real use of bitwise operators is flags. A flag is one bit that stands for one yes/no setting, like a permission. Give each permission its own bit. Then:
|combines flags.&tests whether one flag is on.^switches one flag on or off.
const READ = 1; // 001
const WRITE = 2; // 010
const EXECUTE = 4; // 100
let permissions = READ | WRITE;
console.log((permissions & WRITE) !== 0);
permissions = permissions ^ WRITE;
console.log((permissions & WRITE) !== 0);Lines 1 to 3 give each permission its own bit. Line 4 combines READ and WRITE. Line 5 tests the WRITE bit and prints true. Line 6 switches WRITE off with ^. Line 7 tests again and prints false.
Remember the 32-bit rule when you see value | 0. It drops the fraction, but it gives wrong answers for very large numbers. To drop the fraction from a price or any other normal number, use Math.trunc(value).
Shifts, comma & void
STEP THROUGHShift operators slide all 32 bits to the left or to the right:
<<slides bits left. It fills the empty places on the right with 0.>>slides bits right. It keeps the leftmost bit, called the sign bit, which says whether the number is negative. So negative numbers stay negative.>>>slides bits right and fills the left with 0. The result is never negative: it is unsigned.
Before the shift examples, here is one more math operator. ** is the exponent operator. It raises a number to a power: 2 ** 3 means 2 × 2 × 2, which is 8. It is not a bitwise operator, and its result is a normal number.
const boxes = 2 ** 3;
let displayName = "";
displayName ||= "Guest";
console.log(boxes, displayName);Line 1 stores 8 in boxes. Line 2 sets displayName to an empty string. Line 3 uses ||=, the logical OR assignment. It writes "Guest" only when the variable is falsy (it counts as false). An empty string is falsy, so displayName becomes "Guest". Line 4 prints 8 Guest.
This is handy for a missing name. But ||= also replaces values you may want to keep, like 0 or false, because they are falsy too. Use ??= when only null and undefined should count as missing.
Back to shifts. They are useful for flags and for binary formats, which is data stored as raw bits. They are not for everyday math, because they lose anything that doesn't fit in 32 bits. When the number itself matters, use multiplication, division, or BigInt.
console.log(5 << 1); // 10
console.log(5 >> 1); // 2
console.log(-1 >>> 0); // 4294967295In bits, 5 is 101. Line 1 slides it left to 1010 in bits, which is 10. Line 2 slides it right to 10 in bits, which is 2; the rightmost bit falls off. Line 3 reads -1 as an unsigned number, like before.
The last two operators mostly appear in old or unusual code:
- The comma operator runs expressions from left to right and keeps only the last value.
- The
voidoperator runs an expression, throws away its value, and givesundefined.
The comma runs each part in order and keeps the last value. void always gives undefined.
script
i++, j--;const kept = (1, 2, 3);const swallowed = void kept;console.log(i, j, kept, swallowed);Old code sometimes used void 0 to get undefined. It also used <a href="javascript:void(0)"> to make a link do nothing when clicked. Today, use a real <button> with a click handler, and write undefined directly.
The operator precedence table
SORTING GAMEWhen one line has several operators, JavaScript must decide which operator gets its values first. This order is called precedence. You know it from maths: in 2 + 3 * 4, the * goes first, so the answer is 14.
Precedence decides how the code is grouped. It does not always decide which part runs first: JavaScript still works out most operands from left to right. The simple rule for you: when you mix operators you don't know well, add round brackets around the part that should go first.
Read the table from top to bottom. Level 1 goes first, and level 19 goes last. The last column is a short note to help you remember.
| Level | Group | Operators | Note |
|---|---|---|---|
| 1 | Grouping | (...) | Round brackets make the part inside go first. |
| 2 | Properties and calls | ., [], ?., () | Reading a property or calling a function happens before the operators around it. |
| 3 | Update after use | x++, x-- | Gives the current value, then changes the variable. |
| 4 | One-value operators | !, ~, +x, -x, typeof, void, delete | Operators that take one value go before operators that take two. |
| 5 | Power | ** | Groups from the right: 2 ** 3 ** 2 means 2 ** (3 ** 2). |
| 6 | Multiply and divide | *, /, % | Goes before adding and subtracting, like in maths. |
| 7 | Add and subtract | +, - | + also joins strings. |
| 8 | Shifts | <<, >>, >>> | Slide the 32 bits left or right. |
| 9 | Compare and check | <, >, <=, >=, in, instanceof | Compare sizes, or check for a property or a constructor. |
| 10 | Equality | ==, !=, ===, !== | Prefer === and !==. |
| 11 | Bitwise AND | & | 1 where both bits are 1. |
| 12 | Bitwise XOR | ^ | 1 where the two bits are different. |
| 13 | Bitwise OR | | | 1 where either bit is 1. |
| 14 | Logical AND | && | Gives the left side if it is falsy; otherwise the right side. |
| 15 | Logical OR | || | Gives the left side if it is truthy; otherwise the right side. |
| 16 | Nullish coalescing | ?? | Uses the right side only when the left side is null or undefined. |
| 17 | Conditional | condition ? a : b | Picks one of two values. |
| 18 | Assignment | =, +=, ??=, &&=, ||= | Stores a value in a variable. |
| 19 | Comma | , | Runs left to right and keeps the last value. |
Logical assignment works the same way as its longer form:
name ||= "Guest"writes only whennameis falsy.name ??= "Guest"writes only whennameisnullorundefined.
When an assignment is mixed with other operators, round brackets make it clearest.
Now sort some cards. For each one, decide: does it go before +, at the same level as +, or after +? If you are unsure, the feedback tells you where the operator sits.
getTotal()typeof valueprice * taxsubtotal + taxtotal - discountflags << 1ready && savedok ? a : bfirst(), second()
Place each card. You don't need to learn the whole table. The goal is to notice when round brackets would make code clearer.
Where you'll use this
In everyday front-end code, these operators show up in a few places:
- A ternary picks a CSS class, a label, or a short message:
const tone = error ? "danger" : "success"; typeofchecks data that comes from outside your code, like a server's answer, when it might be a string, a number, or a function.Array.isArraychecks data you didn't create before you call array methods on it.Object.hasOwnreads plain object data when borrowed properties should not count.- Bitwise flags appear in libraries, file permissions, graphics, and some browser features. You will read them more often than you write them.
function describeUpload(file) {
const kind = Array.isArray(file.tags) ? "tagged" : "untagged";
const owner = Object.hasOwn(file, "owner") ? file.owner : "unknown";
return `A ${kind} file owned by ${owner}.`;
}Line 2 uses a ternary with Array.isArray to pick "tagged" or "untagged". Line 3 uses a ternary with Object.hasOwn: it reads file.owner only when owner is stored on file itself. Line 4 builds the sentence.
Common misconceptions
"A ternary is always shorter, so it is always better."
Shorter is not the same as clearer. A ternary is good for one small label choice. When there are more than two choices, use if / else or an object that maps each case to its value.
"typeof tells me the exact type of everything."
typeof null is "object". Arrays and dates are "object" too. Use a more exact check, like Array.isArray, when you need one.
"in means the property is stored on the object."
in also counts borrowed properties, like toString. Use Object.hasOwn to check only the properties stored on the object itself.
"delete removes an array item, like splice."
delete leaves a hole, and the length stays the same. Use splice or filter when you want a shorter array.
"Bitwise operators are just faster math."
They turn numbers into 32-bit whole numbers first, which can change the result. Tricks like ~~x hide that change and make code harder to read, which goes against the Coding style lesson.
"If I know the precedence table, I don't need brackets."
Experienced developers still add round brackets when mixed operators could slow a reader down. Clear code is more important than a short line.
Practice: the rest of the operators
5 EXERCISESWork out which value the ternary picks before you run the code.
const age = 15;
const label = age >= 18 ? "adult" : "minor";
console.log(label);15 >= 18 is false, so the ternary gives "minor". The console prints minor.
Rewrite the nested ternary as an if / else if / else chain.
const label = score >= 90 ? "A" : score >= 80 ? "B" : "keep practicing";let label;
if (score >= 90) {
label = "A";
} else if (score >= 80) {
label = "B";
} else {
label = "keep practicing";
}The if version gives each choice its own line, so it is easier to read. The answer box only asks for the first keyword: if.
Predict the two values the code prints.
const READ = 1;
const WRITE = 2;
const EXECUTE = 4;
let permissions = READ | EXECUTE;
console.log((permissions & WRITE) !== 0);
permissions = permissions | WRITE;
console.log((permissions & WRITE) !== 0);The first check prints false, because the WRITE bit is off. permissions | WRITE switches it on, so the second check prints true.
Which call checks only the properties stored on user itself?
const user = { name: "Ada" };
console.log(Object.hasOwn(user, "toString"));Use Object.hasOwn(user, "toString") when borrowed properties should not count. It prints false for this object.
Predict both lines. Here the hidden change to a 32-bit number matters.
console.log(~5);
console.log(-1 >>> 0);~5 flips all 32 bits, which gives -6. -1 has all 32 bits on. Read as unsigned, that is 4294967295.
Quiz: check your understanding
8 QUESTIONSTake your time. Every answer explains why it is right or wrong. The code questions use what JavaScript really prints.
Question 1 of 8What does the ternary print?
Read the code, then predictconst age = 20; console.log(age >= 18 ? "adult" : "minor");Choose an answer to see the explanation.
Question 2 of 8Which check is the reliable way to detect arrays?
Choose an answer to see the explanation.
Question 3 of 8What do
inandObject.hasOwnprint?Read the code, then predictconst user = { name: "Ada" }; console.log(("toString" in user) + " " + Object.hasOwn(user, "toString"));Choose an answer to see the explanation.
Question 4 of 8What does deleting an array element print?
Read the code, then predictconst letters = ["a", "b", "c"]; delete letters[1]; console.log(letters.length + " " + (1 in letters));Choose an answer to see the explanation.
Question 5 of 8What do these bitwise expressions print?
Read the code, then predictconsole.log(~5 + " " + (-1 >>> 0));Choose an answer to see the explanation.
Question 6 of 8Which expression tests whether the WRITE flag is on?
Choose an answer to see the explanation.
Question 7 of 8What do the comma and void print?
Read the code, then predictconst value = (1, 2, 3); console.log(value + " " + (void value));Choose an answer to see the explanation.
Question 8 of 8Which operator in this lesson goes last, at the bottom of the precedence table?
Choose an answer to see the explanation.
Key takeaways
- A ternary picks one of two values:
condition ? valueIfTrue : valueIfFalse. typeofis best for primitives. UseArray.isArrayfor arrays, andinstanceofto ask which constructor made an object.inalso counts borrowed properties.Object.hasOwnchecks only the object itself.deleteremoves object properties. On arrays it leaves holes, andlengthstays the same.- Bitwise operators turn numbers into 32-bit whole numbers. Flags are a good use; clever number tricks are not.
- The comma operator and
voidare rare. Learn to recognize them, but don't reach for them first. - Precedence decides which operator goes first, but round brackets show your meaning to other people.
Remember the one-liner.
The "other operators" are special tools. Use one when it makes a choice, a check, a flag, or a grouping clearer than the longer way of writing it.
Up next: if, else & conditions, where a choice can hold whole blocks of code.