JavaScript Values

JavaScript Values

A test passes when it should fail. A form accepts “0” as a missing field. A defect report says “the amount is wrong” but the number looks correct on screen. Almost every one of these traces back to a misunderstanding of how JavaScript values actually behave — not a logic error in the business rule itself. A JavaScript value is any piece of data a variable holds: a number, a string, a boolean, an object, or one of a small set of special values like null and undefined. This guide explains what those values are, how JavaScript compares and coerces them, and where QA, BA, and IT professionals run into value-related defects without realizing that’s what they’re looking at.

What Is a Value in JavaScript?

Every variable in JavaScript holds a value, and every value belongs to one of two categories: primitive or reference. This distinction is not academic — it determines how JavaScript compares two values, how a value behaves when passed into a function, and why two things that look identical on a screen can fail an equality check in an automated test.

A primitive value is copied by its actual content every time it’s assigned or passed around. A reference value — objects, arrays, functions — is copied by a pointer to a location in memory. Two objects with identical contents are not equal to JavaScript unless they are literally the same object in memory. This single fact causes more false-positive and false-negative test failures than almost any other JavaScript behavior.

Primitive Values vs. Reference Values

Characteristic Primitive Values Reference Values
Examples string, number, boolean, null, undefined, symbol, bigint object, array, function
Stored as Actual value Reference (pointer) to memory location
Equality check Compares actual content Compares memory address, not content
Mutability Immutable — a “changed” string is a new value Mutable — properties can change without creating a new reference
Common QA impact Straightforward equality assertions Deep-equality assertions needed; naive === fails on identical-looking objects

This is why a test asserting expectedResponse === actualResponse on two JSON objects with identical fields will fail every time, even when the data is correct. The fix is a deep-equality comparison, not a defect in the API. Recognizing this distinction before filing a bug saves a round trip with the development team.

Where QA, BA, and IT Professionals Actually Encounter JavaScript Values

You rarely write raw value-comparison code from scratch. You read it inside test assertions, form validation logic, and API response handlers, and you need to recognize when a “defect” is really expected JavaScript value behavior.

In Form Validation Testing

Front-end form validation routinely checks whether a field “has a value” using a shorthand like if (!fieldValue). That shorthand treats an empty string, zero, null, undefined, and NaN as equally “missing.” A numeric field where zero is a legitimate answer — a copay amount, a dependent count, a lab result — will get flagged as empty by this pattern. This is one of the most common form-validation defects in production applications, and it is a value-type issue, not a UI issue.

In API Response Assertions

REST APIs return JSON, and JSON values map to JavaScript values on arrival: strings, numbers, booleans, null, arrays, and objects. A test asserting a field equals a specific value needs to know the expected type before writing the assertion. A field that arrives as the string "100" instead of the number 100 will fail a strict equality check even though the data is functionally correct — and this exact mismatch is a frequent, low-severity-looking defect that actually signals a serialization bug worth escalating.

In Truthy/Falsy Conditional Logic

JavaScript evaluates every value as either “truthy” or “falsy” inside a conditional statement, even values that aren’t booleans. This is convenient for developers and dangerous for testers who don’t know the full falsy list, because a defect hiding inside this logic often looks like a business rule failure rather than a JavaScript quirk.

The Seven Primitive Value Types in JavaScript

JavaScript has seven primitive types plus the object type that everything else builds on. Knowing all seven — not just the three everyone remembers — prevents misdiagnosing defects involving the less common ones.

Type Example Where Testers See It
String "claim-4521" IDs, names, free-text fields
Number 1042.50 Amounts, counts, dates as timestamps
Boolean true / false Flags — eligibility, active status, feature toggles
Undefined undefined A variable declared but never assigned; a missing object property
Null null A deliberate “intentionally empty” value, often set by a developer
Symbol Symbol("id") Rare — unique object keys, internal framework code
BigInt 9007199254740993n Large integer IDs beyond safe Number precision

The null versus undefined distinction deserves specific attention because it is one of the most common sources of miscommunication between BAs writing acceptance criteria and developers implementing them. undefined means “this was never set.” null means “this was explicitly set to nothing.” An acceptance criterion that says a field should be “empty” is ambiguous unless it specifies which one — and that ambiguity produces defects that are really requirements gaps, not coding errors. This is exactly the kind of distinction worth nailing down when writing acceptance criteria for any field involving optional or nullable data.

Truthy and Falsy Values: The Source of Silent Defects

Exactly six values are falsy in JavaScript. Every other value — including an empty array, an empty object, and the string "0" — is truthy. Memorizing this short list prevents hours of confused debugging.

The complete falsy list:

false, 0, -0, "" (empty string), null, undefined, and NaN. That’s the entire list. An empty array [] and an empty object {} are both truthy, which surprises most people the first time they encounter it.

The practical defect pattern: a developer writes if (patientCopay) intending to check “does a copay value exist,” but a $0 copay evaluates as falsy and gets treated as if no copay was set. The system might display “no copay information available” for a patient who legitimately owes nothing, which is a materially different message with billing implications. This kind of defect frequently gets filed as a data issue and bounced back and forth before someone identifies it as a falsy-value logic bug in the conditional itself.

Type Coercion: Why == and === Give Different Answers

JavaScript offers two equality operators, and they do not behave the same way. This single difference is responsible for a disproportionate share of subtle defects in production applications.

Comparison == (loose equality) === (strict equality)
"100" == 100 true — string coerced to number false — different types
0 == false true false
null == undefined true false
"" == 0 true false

Most JavaScript style guides, including Airbnb’s widely adopted standard, recommend === by default specifically because ==‘s coercion rules are inconsistent enough that even experienced developers can’t reliably predict every outcome. When you see == in a code review or a test assertion, treat it as a flag worth asking about, not a stylistic detail. If a defect report says “the comparison passed when it shouldn’t have,” check the equality operator before checking the data.

Scenario: Eligibility Flag Bug in Healthcare IT

A patient portal displays insurance eligibility status pulled from a payer API. The front-end logic reads if (eligibilityCode) to decide whether to show “Eligible” or “Status Unavailable.” For most patients this works. For a specific payer integration, the eligibility code for confirmed-ineligible patients comes back as the number 0, not a string or a null.

Because 0 is falsy, every genuinely ineligible patient sees “Status Unavailable” instead of an accurate ineligibility message — a UX and compliance concern, since patients need accurate coverage information before a visit. QA testing with mock data that always used null for the ineligible case never caught this, because null and 0 both evaluate as falsy and produced the same visible behavior in every test run.

The fix required explicit type checking — if (eligibilityCode !== undefined && eligibilityCode !== null) — rather than relying on truthy/falsy shorthand. The test gap was equally important: test data needs to cover the full range of falsy values individually, not just one representative “empty” case.

This is a recurring gap in QA automation framework design — test data sets built around one “empty” placeholder value miss defects that only appear with a different falsy value in the same conditional path.

Scenario: Transaction Amount Coercion in Financial IT

A payment reconciliation dashboard compares transaction amounts pulled from two systems: an internal ledger returning numbers, and a third-party processor’s API returning amounts as strings, formatted as "1042.50". The comparison logic uses ==, which coerces the string to a number before comparing, and the dashboard reports no discrepancies.

A later audit finds real discrepancies the dashboard missed. The root cause: == coercion silently converts values like "1e3" — valid scientific notation the processor occasionally returns for round amounts — into the number 1000, matching correctly by accident, but a malformed string like "1,042.50" with a comma coerces to NaN, and NaN == NaN is always false in JavaScript, which the comparison logic silently treated as “not equal, flag it” rather than raising an explicit parsing error.

The result was inconsistent: some malformed values got flagged as discrepancies, masking the real issue as noise, while the team ignored a growing list of false positives instead of identifying the actual comma-formatting bug in the processor’s export. Switching to explicit Number.parseFloat() conversion with dedicated null/NaN validation before comparison — instead of relying on == coercion — surfaced the real parsing defect within a day.

The general principle: whenever a comparison spans two systems with different serialization formats, coercion-based equality checks either mask real defects or manufacture false ones. Explicit type conversion with validation is not extra work — it is the only way to get a comparison you can trust.

Common Mistakes and Edge Cases When Reading JavaScript Value Code

NaN is never equal to itself. NaN === NaN evaluates to false. To check for NaN, code must use Number.isNaN(value), not an equality comparison. If you see a direct comparison against NaN anywhere in validation logic, it is a bug waiting to surface.

Comparing objects by reference, not content. Two separately created objects with identical properties are never === equal. Test frameworks like Jest provide toEqual() specifically for deep-content comparison, separate from toBe(), which checks reference identity. Using the wrong one produces either false failures or false passes.

Array truthy checks hiding empty-array bugs. Because [] is truthy, if (resultsArray) passes even when the array has zero elements. The correct check is if (resultsArray.length > 0). A defect where “no results” silently renders as if results exist is almost always this exact pattern.

Implicit conversion in template literals and concatenation. Using the + operator with a mix of strings and numbers produces string concatenation, not addition, if either operand is a string. "5" + 3 produces "53", not 8. This shows up constantly in dynamically built UI text where a numeric field accidentally displays as concatenated digits instead of a sum.

Optional chaining changes what “missing” looks like. Modern JavaScript’s ?. operator returns undefined instead of throwing an error when accessing a property on a null or undefined object. Code written before this feature was standard often has manual null checks that now duplicate what the operator already handles — worth flagging in a code review as a simplification opportunity, not a defect, but a maintainability note relevant to software testing life cycle planning around regression risk.

Edge case worth knowing: typeof null returns "object", not "null". This is a long-standing, well-documented quirk in the language itself, not a bug in your codebase. If a type-check function relies on typeof alone to distinguish null from a real object, it needs an explicit === null check alongside it.

Quick Reference: Reading JavaScript Value Code by Role

QA / Test Automation Engineer

  • Test every falsy value individually, not one “empty” placeholder
  • Use deep-equality assertions for objects, not ===
  • Flag any == found in production conditional logic
Business Analyst

  • Specify null vs. undefined explicitly in acceptance criteria
  • Ask whether “zero” is a valid business value before it’s treated as empty
  • Confirm expected data type (string vs. number) for every numeric field
IT Support / Ops Analyst

  • Check for silent NaN results before assuming a calculation is correct
  • Watch for type mismatches across system integrations (string vs. number)
  • Note where legacy code uses == instead of === during triage

The next time a defect report says a value “looks right but the comparison failed” — or the reverse, a comparison passed when the data was clearly wrong — check the value type and the equality operator before assuming the business logic is broken. In JavaScript, the value’s type is often the real story, and knowing the falsy list and the coercion rules by heart turns a half-day investigation into a five-minute diagnosis.


Further reading: MDN’s JavaScript Data Types and Structures reference is the standard technical specification for value types. For the formal language specification behind equality and coercion behavior, see the ECMA-262 ECMAScript Language Specification.

Download the JavaScript Values & Type Coercion Cheat Sheet (PDF)

The full falsy-value list, == vs === comparison table, and five defect patterns testers and BAs run into most.

Get the Free Cheat Sheet →

Scroll to Top