JavaScript Exponentiation Operator Explained

This article explores how JavaScript evaluates the exponentiation operator (**), introduced in ECMAScript 2016 (ES7). You will learn its core syntax, associativity rules, type coercion behaviors, differences from Math.pow(), and common edge cases such as handling negative numbers and BigInt values.

Syntax and Basic Evaluation

The exponentiation operator (**) raises the first operand (the base) to the power of the second operand (the exponent).

console.log(2 ** 3); // 8
console.log(5 ** 2); // 25
console.log(4 ** 0.5); // 2 (square root)

The operation produces the same mathematical result as \(base^{exponent}\).

Right-Associativity

Unlike most arithmetic operators in JavaScript that evaluate from left to right, the exponentiation operator is right-associative. When multiple exponentiation operators appear in sequence, JavaScript groups and evaluates them from right to left.

console.log(2 ** 3 ** 2); // 512
// Evaluated as: 2 ** (3 ** 2) -> 2 ** 9 = 512
// NOT: (2 ** 3) ** 2 -> 8 ** 2 = 64

Unary Operator Ambiguity and Syntax Rules

To prevent ambiguity regarding operator precedence, JavaScript does not allow a unary operator (+, -, ~, !, typeof, void, delete) immediately before the base without parentheses.

// SyntaxError: Unary operator used immediately before exponentiation expression
// -2 ** 2; 

// Correct usage:
-(2 ** 2); // -4
(-2) ** 2; // 4

Type Coercion and BigInt Support

The exponentiation operator automatically coerces non-numeric primitives to numbers, following standard JavaScript numeric conversion rules:

console.log("3" ** "2"); // 9
console.log(true ** 3);   // 1
console.log(null ** 2);   // 0
console.log(undefined ** 2); // NaN

BigInt Values

The ** operator works with BigInt primitives, provided both operands are BigInt types. Mixed operations between BigInt and standard numbers throw a TypeError.

console.log(2n ** 3n); // 8n

// Negative exponents with BigInt throw a RangeError
// 2n ** -1n; // RangeError: Cannot mix BigInt and other types, use explicit conversions

Differences Between ** and Math.pow()

While x ** y and Math.pow(x, y) generally produce the same output, there are two primary differences:

  1. BigInt Support: Math.pow() converts all inputs to standard floating-point numbers, whereas ** supports BigInt.
  2. Unary Negation Precedence: Math.pow(-2, 2) accepts negative values directly as parameters, whereas -2 ** 2 requires parentheses (-2) ** 2 to avoid syntax errors.

Exponentiation Assignment Operator (**=)

JavaScript also provides an assignment shorthand, **=, which calculates the exponentiation and assigns the result back to the variable:

let value = 3;
value **= 3; // Equivalent to: value = value ** 3
console.log(value); // 27