Increment operators ++
and decrement operators --
have two forms:
i++
, i--
) — returns the old value, then modifies the variable++i
, --i
) — first modifies the variable, then returns the new valuelet a = 5;
console.log(a++); // 5 (returns old value)
console.log(a); // 6 (variable changed)
let b = 5;
console.log(++b); // 6 (returns new value)
console.log(b); // 6 (variable already changed)
Key difference: timing of value return relative to variable modification.
Algorithm:
let counter = 10;
// Postfix increment
let oldValue = counter++;
console.log(oldValue); // 10 (old value)
console.log(counter); // 11 (new value)
// Postfix decrement
let score = 100;
let previousScore = score--;
console.log(previousScore); // 100 (old value)
console.log(score); // 99 (new value)
Algorithm:
let counter = 10;
// Prefix increment
let newValue = ++counter;
console.log(newValue); // 11 (new value)
console.log(counter); // 11 (same value)
// Prefix decrement
let score = 100;
let updatedScore = --score;
console.log(updatedScore); // 99 (new value)
console.log(score); // 99 (same value)
Operator | Name | When Modified | What Returns | Example |
---|---|---|---|---|
i++ | Postfix increment | After return | Old value | 5++ → returns 5 , i becomes 6 |
++i | Prefix increment | Before return | New value | ++5 → i becomes 6 , returns 6 |
i-- | Postfix decrement | After return | Old value | 5-- → returns 5 , i becomes 4 |
--i | Prefix decrement | Before return | New value | --5 → i becomes 4 , returns 4 |
// Postfix form (classic loop)
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// Prefix form
for (let i = 0; i < 5; ++i) {
console.log(i); // 0, 1, 2, 3, 4
}
// In loops the result is the same,
// since the return value is not used
let a = 5;
let b = 5;
// Postfix form
let result1 = a++ * 2;
console.log(result1); // 10 (5 * 2)
console.log(a); // 6
// Prefix form
let result2 = ++b * 2;
console.log(result2); // 12 (6 * 2)
console.log(b); // 6
let attempts = 3;
// Postfix form
if (attempts-- > 0) {
console.log(`Attempt ${attempts + 1}`); // "Attempt 3"
console.log(`Attempts left: ${attempts}`); // "Attempts left: 2"
}
let retries = 3;
// Prefix form
if (--retries > 0) {
console.log(`Attempt ${3 - retries}`); // "Attempt 1"
console.log(`Attempts left: ${retries}`); // "Attempts left: 2"
}
let arr = [10, 20, 30, 40, 50];
let index = 2;
// Postfix form
console.log(arr[index++]); // 30 (arr[2])
console.log(index); // 3
index = 2; // reset
// Prefix form
console.log(arr[++index]); // 40 (arr[3])
console.log(index); // 3
let x = 5;
let y = 5;
// Complex expression with postfix
let result1 = x++ + x++ + x++;
// x++ returns 5, x becomes 6
// x++ returns 6, x becomes 7
// x++ returns 7, x becomes 8
// result1 = 5 + 6 + 7 = 18
console.log(result1); // 18
console.log(x); // 8
// Complex expression with prefix
let result2 = ++y + ++y + ++y;
// ++y: y becomes 6, returns 6
// ++y: y becomes 7, returns 7
// ++y: y becomes 8, returns 8
// result2 = 6 + 7 + 8 = 21
console.log(result2); // 21
console.log(y); // 8
let a = 10;
let b = 10;
// Mixing prefix and postfix
let mixed = a++ + ++a - --b + b--;
// a++ returns 10, a becomes 11
// ++a: a becomes 12, returns 12
// --b: b becomes 9, returns 9
// b-- returns 9, b becomes 8
// mixed = 10 + 12 - 9 + 9 = 22
console.log(mixed); // 22
console.log(a); // 12
console.log(b); // 8
let i = 5;
let j = i++ + ++i;
console.log(j);
console.log(i);
Result:
Explanation:
i++
returns 5
, then i
becomes 6
++i
increases i
to 7
and returns 7
j = 5 + 7 = 12
let count = 0;
for (let i = 0; i < 3; i++) {
count += i++;
}
console.log(count);
Result: count = 3
Explanation:
i = 0
, count += 0
(i++ returns 0), i
becomes 1
, then i++
in loop makes i = 2
i = 2
, count += 2
(i++ returns 2), i
becomes 3
, loop endscount = 0 + 2 = 2
Warning: Double increment in loops is bad practice!
let arr = ['a', 'b', 'c', 'd'];
let index = 1;
let result = arr[index++] + arr[++index];
console.log(result);
console.log(index);
Result:
Explanation:
x++
returns 5
, x
becomes 6
5 > 5
is false
++y
increases y
to 6
, returns 6
6 > 5
is true
||
(OR), condition is met// Classic counter
let pageViews = 0;
function trackPageView() {
console.log(`Page view #${++pageViews}`);
// Prefix is convenient when you need the new value
}
// Processing elements in order
let items = ['item1', 'item2', 'item3'];
let currentIndex = 0;
function getNextItem() {
if (currentIndex < items.length) {
return items[currentIndex++]; // Postfix to get current element
}
return null;
}
class IdGenerator {
constructor() {
this.currentId = 0;
}
// Prefix form for generating new ID
generateId() {
return `id_${++this.currentId}`;
}
// Postfix form to get current ID and move to next
getCurrentAndNext() {
return {
current: `id_${this.currentId++}`,
next: `id_${this.currentId}`
};
}
}
const generator = new IdGenerator();
console.log(generator.generateId()); // "id_1"
console.log(generator.generateId()); // "id_2"
// Stack implementation
class Stack {
constructor() {
this.items = [];
this.top = -1;
}
push(item) {
this.items[++this.top] = item; // Prefix: first increment, then use
}
pop() {
if (this.top >= 0) {
return this.items[this.top--]; // Postfix: first use, then decrement
}
return undefined;
}
peek() {
return this.items[this.top];
}
}
// Array search
function findElement(arr, target) {
let left = 0;
let right = arr.length - 1;
let comparisons = 0;
while (left <= right) {
console.log(`Comparison #${++comparisons}`); // Prefix for counting
let mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return { index: mid, comparisons };
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return { index: -1, comparisons };
}
// Debouncing with counter
function createDebouncedFunction(func, delay) {
let timeoutId;
let callCount = 0;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
console.log(`Call #${++callCount}`); // Prefix for new number
func.apply(this, args);
}, delay);
};
}
// Retry limit
function createRetryFunction(func, maxAttempts) {
let attempts = 0;
return async function(...args) {
while (attempts < maxAttempts) {
try {
console.log(`Attempt ${++attempts}`); // Prefix for current attempt
return await func.apply(this, args);
} catch (error) {
if (attempts >= maxAttempts) {
throw new Error(`Maximum attempts exceeded (${maxAttempts})`);
}
console.log(`Attempt ${attempts} failed, retrying...`);
}
}
};
}
In JavaScript there’s no performance difference between ++i
and i++
for primitive types, because:
// Benchmark (results will be approximately the same)
function benchmarkPrefix() {
let sum = 0;
for (let i = 0; i < 1000000; ++i) {
sum += i;
}
return sum;
}
function benchmarkPostfix() {
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return sum;
}
// Execution time will be practically the same
console.time('prefix');
benchmarkPrefix();
console.timeEnd('prefix');
console.time('postfix');
benchmarkPostfix();
console.timeEnd('postfix');
// ✅ Use prefix when you need the new value
let counter = 0;
function getNextId() {
return `user_${++counter}`; // Immediately get new value
}
// ✅ Use postfix when you need the current value
let index = 0;
let items = ['a', 'b', 'c'];
function getNextItem() {
return items[index++]; // Get current element, then move to next
}
// ✅ In loops — no difference, choose by team style
for (let i = 0; i < 10; i++) { /* postfix — classic */ }
for (let i = 0; i < 10; ++i) { /* prefix — C++ style */ }
❌ Mistake: Expecting same behavior
let a = 5;
let b = 5;
// Wrong expectation of same result
let result1 = a++ * 2; // 10 (5 * 2)
let result2 = ++b * 2; // 12 (6 * 2)
// result1 !== result2!
✅ Correct: Understanding the difference in timing of value return
// ❌ Bad: double increment
for (let i = 0; i < 10; i++) {
console.log(i++);
// i is incremented twice per iteration!
}
// ✅ Good: single increment
for (let i = 0; i < 10; i++) {
console.log(i);
}
// ✅ Or alternatively:
for (let i = 0; i < 10; ++i) {
console.log(i);
}
// ❌ Bad: hard to read and understand
let x = 5;
let result = x++ + ++x * x-- - --x;
// ✅ Good: break into steps
let y = 5;
let step1 = y++; // 5, y = 6
let step2 = ++y; // 7, y = 7
let step3 = y--; // 7, y = 6
let step4 = --y; // 5, y = 5
let clearResult = step1 + step2 * step3 - step4;
// ❌ Undefined behavior: don't do this!
let i = 5;
let bad = i++ + i++ + ++i; // Order of evaluation not guaranteed
// ✅ Good: one modification per expression
let j = 5;
let good1 = j++; // 5
let good2 = j++; // 6
let good3 = ++j; // 8
let goodResult = good1 + good2 + good3; // 19
// ⚠️ Careful: may be non-obvious
let attempts = 3;
while (attempts-- > 0) {
console.log(`Attempt ${attempts + 1}`); // Need +1 because of postfix
}
// ✅ More clear:
let retries = 3;
while (retries > 0) {
console.log(`Attempt ${retries}`);
retries--;
}
// Instead of manual increment in loops
const numbers = [1, 2, 3, 4, 5];
// ❌ Old style
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i] * 2);
}
// ✅ Modern style
numbers.forEach(num => console.log(num * 2));
numbers.map(num => num * 2);
numbers.filter(num => num > 2);
// Instead of manual index management
const items = ['a', 'b', 'c', 'd'];
// ❌ With increment
let index = 0;
const first = items[index++];
const second = items[index++];
// ✅ With destructuring
const [firstItem, secondItem, ...rest] = items;
// ✅ Modern approach to counters
function* createCounter(start = 0) {
let count = start;
while (true) {
yield ++count;
}
}
const counter = createCounter();
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
console.log(counter.next().value); // 3
// Or with class
class Counter {
constructor(start = 0) {
this.value = start;
}
next() {
return ++this.value;
}
current() {
return this.value;
}
}
// ✅ Good: clear what's happening
let index = 0;
while (index < items.length) {
processItem(items[index]);
index++;
}
// ⚠️ Less clear
let i = 0;
while (i < items.length) {
processItem(items[i++]);
}
// ✅ Postfix: when you need current value
function popFromStack(stack) {
return stack[--stack.length]; // Prefix: first decrease size
}
function pushToStack(stack, item) {
stack[stack.length++] = item; // Postfix: use current length, then increase
}
// ✅ Prefix: when you need new value
function generateUniqueId() {
return `item_${++globalCounter}`; // Get next ID
}
// ✅ Choose one style for loops and stick to it
// Team prefers postfix:
for (let i = 0; i < length; i++) { /* ... */ }
for (let j = 0; j < count; j++) { /* ... */ }
// Team prefers prefix:
for (let i = 0; i < length; ++i) { /* ... */ }
for (let j = 0; j < count; ++j) { /* ... */ }
// ✅ When using in complex scenarios, add comments
function processQueue() {
// Use postfix to get current item before moving to next
while (queue.length > 0) {
const item = queue[currentIndex++];
process(item);
// Use prefix to increment attempt counter before check
if (++attempts > maxAttempts) {
break;
}
}
}
i++
, i--
) returns old value, then modifies++i
, --i
) modifies first, then returns new valueforEach
, map
, filter
) instead of manual loopsRemember: clarity and maintainability are more important than micro-optimizations. Choose the form that makes your intent clearest to other developers.
Want more articles on interview preparation?
FollowEasyAdvice, bookmark the site, and level up every day 💪