JavaScript Variables and Data Types | let· const
이 글의 핵심
Deep dive into JavaScript variables: let, const, var, scope, hoisting, primitives, type coercion, and common mistakes—with runnable examples.
Introduction
What is a variable?
A variable is a named place to store data. JavaScript is a dynamically typed language: a variable’s “type” is determined by the value it holds.
1. Declarations: let, const, var
let: reassignment allowed
let age = 25;
console.log(age); // 25
age = 26; // reassignment OK
console.log(age); // 26
// Declare now, assign later
let name;
console.log(name); // undefined
name = "Alice";
console.log(name); // Alice
const: no reassignment
const declares a constant binding; you cannot reassign it:
const PI = 3.14159;
console.log(PI); // 3.14159
// Reassignment throws
// PI = 3.14; // TypeError: Assignment to constant variable
// Must initialize at declaration
// const x; // SyntaxError: Missing initializer in const declaration
// Important: object/array *contents* can still change
const person = { name: "Alice", age: 25 };
// The binding `person` cannot be reassigned
// But properties can change
person.age = 26; // ✅ OK (mutate object contents)
console.log(person); // { name: 'Alice', age: 26 }
// person = {}; // ❌ TypeError (rebinding not allowed)
const arr = [1, 2, 3];
// The binding `arr` cannot be reassigned
// Elements can change
arr.push(4); // ✅ OK
console.log(arr); // [1, 2, 3, 4]
arr[0] = 10; // ✅ OK
console.log(arr); // [10, 2, 3, 4]
// arr = []; // ❌ TypeError
What const really means:
// const fixes the *reference* the variable holds
// The referenced object’s contents can still change
const obj = { x: 1 };
// obj points at a specific object in memory
obj.x = 2; // ✅ mutate contents (same reference)
// obj = { x: 2 }; // ❌ new object (new reference)
Deep immutability:
// Object.freeze(): shallow freeze
const person = Object.freeze({ name: "Alice", age: 25 });
// person.age = 26; // ignored (strict mode throws)
console.log(person.age); // 25
// Nested objects are not frozen automatically
const user = Object.freeze({
name: "Alice",
address: { city: "Seoul" } // inner object not frozen
});
user.address.city = "Busan"; // ✅ still mutates nested object
console.log(user.address.city); // Busan
var: legacy (not recommended)
var x = 10;
var x = 20; // redeclaration allowed (problematic!)
console.log(x); // 20
// Function scope (ignores block scope)
if (true) {
var y = 30;
}
console.log(y); // 30 (accessible outside the block!)
// Hoisting surprise
console.log(z); // undefined (not an error!)
var z = 40;
let vs const vs var
| let | const | var | |
|---|---|---|---|
| Reassignment | ✅ | ❌ | ✅ |
| Redeclare (same scope) | ❌ | ❌ | ✅ |
| Scope | Block | Block | Function |
| Hoisting | TDZ | TDZ | undefined |
| Recommended | ✅ | ✅ | ❌ |
TDZ (Temporal Dead Zone): accessing before declaration throws ReferenceError. |
// let/const: TDZ
// console.log(x); // ReferenceError
let x = 10;
// var: hoisted as undefined
console.log(y); // undefined
var y = 20;
2. Scope
Block scope
let and const are block-scoped:
{
let x = 10;
const y = 20;
console.log(x, y); // 10 20
}
// console.log(x); // ReferenceError
// console.log(y); // ReferenceError
if (true) {
let z = 30;
console.log(z); // 30
}
// console.log(z); // ReferenceError
for (let i = 0; i < 3; i++) {
console.log(i); // 0 1 2
}
// console.log(i); // ReferenceError
// Nested blocks
{
let x = 10;
{
let x = 20; // shadowing
console.log(x); // 20
}
console.log(x); // 10
}
// Closures in loops: let creates a new binding per iteration
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 0 1 2
}, 100);
}
// With var, one shared binding
for (var j = 0; j < 3; j++) {
setTimeout(() => {
console.log(j); // 3 3 3
}, 100);
}
Benefits of block scope:
- Fewer accidental collisions
- Variables can be collected when the block ends
- Clearer lifetime
- Safer with closures in loops
Function scope
function test() {
let x = 10;
var y = 20;
if (true) {
let x = 30; // new block-scoped x
var y = 40; // same function-scoped y
console.log(x, y); // 30 40
}
console.log(x, y); // 10 40
}
test();
Global scope
let globalVar = "global";
function test() {
console.log(globalVar);
}
test();
// Browser: window
// var global = "test";
// console.log(window.global);
// Node.js: global
// global.myVar = "test";
3. Data types
Primitive types
JavaScript has seven primitive types.
1. Number
let integer = 42;
let float = 3.14;
let negative = -10;
let inf = Infinity;
let negInf = -Infinity;
let notANumber = NaN;
console.log(10 / 0); // Infinity
console.log("abc" / 2); // NaN
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MIN_SAFE_INTEGER);
2. String
let str1 = "double quotes";
let str2 = 'single quotes';
let str3 = `template literal`;
let name = "Alice";
let age = 25;
let message = `Hello, ${name}! You are ${age}.`;
console.log(message);
let multiline = `line one
line two
line three`;
let escaped = "He said \"Hello\"";
console.log(escaped);
3. Boolean
let isTrue = true;
let isFalse = false;
console.log(10 > 5); // true
console.log(10 === 5); // false
4. undefined
let x;
console.log(x); // undefined
console.log(typeof x); // undefined
function test() {
// no return
}
console.log(test()); // undefined
5. null
let empty = null;
console.log(empty); // null
console.log(typeof empty); // "object" (historical quirk)
let x; // undefined (declared only)
let y = null; // explicit empty
6. Symbol (ES6+)
let sym1 = Symbol("description");
let sym2 = Symbol("description");
console.log(sym1 === sym2); // false (always unique)
let obj = {
[sym1]: "value1"
};
console.log(obj[sym1]); // value1
7. BigInt (ES2020+)
let bigNum = 9007199254740991n;
let bigNum2 = BigInt("9007199254740991");
console.log(bigNum + 1n);
// Cannot mix with Number without conversion
// console.log(bigNum + 1); // TypeError
console.log(bigNum + BigInt(1)); // OK
Reference types
Object
let person = {
name: "Alice",
age: 25,
greet: function() {
console.log(`Hello, I'm ${this.name}.`);
}
};
console.log(person.name);
person.greet();
Array
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "hello", true, null, { name: "Alice" }];
console.log(numbers[0]);
console.log(numbers.length);
Function
function add(a, b) {
return a + b;
}
console.log(typeof add); // function
4. Type checks and conversion
typeof
console.log(typeof 42); // number
console.log(typeof "hello"); // string
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof null); // object (quirk)
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof function(){}); // function
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
Coercion
To string
let num = 42;
let str = String(num);
console.log(str, typeof str);
let str2 = num.toString();
let result = 42 + " apples";
console.log(result);
To number
let str = "42";
let num = Number(str);
console.log(parseInt("42px"));
console.log(parseFloat("3.14abc"));
console.log(parseInt("abc")); // NaN
console.log("42" - 10);
console.log("42" * 2);
console.log(+"42");
To boolean
console.log(Boolean(1));
console.log(Boolean(0));
console.log(Boolean("hello"));
console.log(Boolean(""));
// Falsy values
console.log(Boolean(false));
console.log(Boolean(0));
console.log(Boolean(""));
console.log(Boolean(null));
console.log(Boolean(undefined));
console.log(Boolean(NaN));
// Truthy
console.log(Boolean(1));
console.log(Boolean("hello"));
console.log(Boolean([]));
console.log(Boolean({}));
if ("hello") {
console.log("Truthy!");
}
5. Hoisting
What is hoisting?
Hoisting means declarations are conceptually moved to the top of their scope (behavior differs for var, let/const, and functions).
var hoisting
console.log(x); // undefined
var x = 10;
console.log(x); // 10
// Conceptually similar to:
var x;
console.log(x);
x = 10;
console.log(x);
let/const and the TDZ
// console.log(x); // ReferenceError (TDZ)
let x = 10;
// console.log(y); // ReferenceError
const y = 20;
Function hoisting
greet(); // works (function declaration hoisted)
function greet() {
console.log("Hello!");
}
// sayHello(); // ReferenceError (function expression not hoisted)
const sayHello = function() {
console.log("Hello!");
};
sayHello();
6. Practical examples
Example 1: swapping variables
let a = 10;
let b = 20;
let temp = a;
a = b;
b = temp;
console.log(a, b); // 20 10
let x = 10;
let y = 20;
[x, y] = [y, x];
console.log(x, y); // 20 10
Example 2: a small getType helper
function getType(value) {
if (value === null) return "null";
if (Array.isArray(value)) return "array";
return typeof value;
}
Example 3: safe integer parsing
function safeParseInt(value, defaultValue = 0) {
const parsed = parseInt(value);
return isNaN(parsed) ? defaultValue : parsed;
}
Example 4: config and immutability
const config = {
apiUrl: "https://api.example.com",
timeout: 5000
};
config.timeout = 10000; // OK (mutate object)
const frozenConfig = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000
});
const user = Object.freeze({
name: "Alice",
address: { city: "Seoul" }
});
user.address.city = "Busan"; // nested still mutable
function deepFreeze(obj) {
Object.freeze(obj);
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
deepFreeze(obj[key]);
}
});
return obj;
}
7. Common mistakes
Mistake 1: using var in loops
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
Mistake 2: misunderstanding const
const arr = [1, 2, 3];
arr.push(4);
arr[0] = 100;
Mistake 3: == vs ===
console.log(5 == "5");
console.log(5 === "5");
Mistake 4: accidental string concatenation
console.log("10" + 20);
console.log(Number("10") + 20);
Mistake 5: leaking globals
// count = 0; // creates global in sloppy mode — avoid
let count = 0;
function increment() {
count++;
}
8. Practical tips
Tip 1: const by default
const API_URL = "https://api.example.com";
let retryCount = 0;
Tip 2: meaningful names
let userAge = 25;
let scores = [1, 2, 3];
const MAX_USERS = 100;
Tip 3: strict mode
"use strict";
// x = 10; // ReferenceError
9. Exercises
Exercise 1: rotate three variables
let a = 1;
let b = 2;
let c = 3;
[a, b, c] = [c, a, b];
console.log(a, b, c); // 3 1 2
Exercise 2: toNumber
function toNumber(value, defaultValue = 0) {
const num = Number(value);
return isNaN(num) ? defaultValue : num;
}
Exercise 3: remove falsy values
function removeFalsy(arr) {
return arr.filter(Boolean);
}
Summary
Key points
- Declarations:
constdefault,letwhen rebinding, avoidvarin new code. - Scope: block scope for
let/const, function scope forvar. - Types: seven primitives plus objects, arrays, functions.
- Coercion: prefer explicit
Number/String/Booleanwhen unclear. - Hoisting:
varinitializes asundefined;let/constuse TDZ.
Best practices
- Use
const, thenlet. - Prefer
===over==. - Use
"use strict". - Prefer descriptive names.
- Minimize globals.
Next steps
Related posts
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Deep dive into JavaScript variables: let, const, var, scope, hoisting, primitives, type coercion, and common mistakes—wi…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the JavaScript series index for the full picture.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- JavaScript 시작하기 | 웹 개발의 필수 언어 완벽 입문
- JavaScript 함수 | 함수 선언, 화살표 함수, 콜백, 클로저 완벽 정리
- Get Started with JavaScript
Keywords Covered in This Article (Related Search Terms)
This article covers JavaScript, Variables, let, const, var, Data Types, Hoisting.