[Medium] π Primitive vs Reference Types
1. What are Primitive Types and Reference Types?β
μμ νμ (Primitive Types)κ³Ό μ°Έμ‘° νμ (Reference Types)μ΄λ 무μμΈκ°?
JavaScriptμ λ°μ΄ν° νμ μ μμ νμ κ³Ό μ°Έμ‘° νμ λ κ°μ§λ‘ λλ©λλ€. λ©λͺ¨λ¦¬ μ μ₯ λ°©μκ³Ό μ λ¬ λμμ λ³Έμ§μ μΈ μ°¨μ΄κ° μμ΅λλ€.
μμ νμ (Primitive Types)β
νΉμ§:
- **μ€ν(Stack)**μ μ μ₯
- μ λ¬ μ κ° μ체λ₯Ό 볡μ¬(Call by Value)
- λΆλ³(Immutable)
7κ°μ§ μ’ λ₯:
// 1. String(λ¬Έμμ΄)
const str = 'hello';
// 2. Number(μ«μ)
const num = 42;
// 3. Boolean(λΆλ¦¬μΈ)
const bool = true;
// 4. Undefined
let undef;
// 5. Null
const n = null;
// 6. Symbol(ES6)
const sym = Symbol('unique');
// 7. BigInt(ES2020)
const bigInt = 9007199254740991n;
μ°Έμ‘° νμ (Reference Types)β
νΉμ§:
- **ν(Heap)**μ μ μ₯
- μ λ¬ μ μ°Έμ‘°(λ©λͺ¨λ¦¬ μ£Όμ)λ₯Ό 볡μ¬(Call by Reference)
- κ°λ³(Mutable)
μ’ λ₯:
// 1. Object(κ°μ²΄)
const obj = { name: 'John' };
// 2. Array(λ°°μ΄)
const arr = [1, 2, 3];
// 3. Function(ν¨μ)
const func = function () {};
// 4. Date
const date = new Date();
// 5. RegExp
const regex = /abc/;
// 6. Map, Set, WeakMap, WeakSet (ES6)
const map = new Map();
const set = new Set();
2. Call by Value vs Call by Referenceβ
κ°μ μν μ λ¬(Call by Value) vs μ°Έμ‘°μ μν μ λ¬(Call by Reference)
κ°μ μν μ λ¬(Call by Value) - μμ νμ β
λμ: κ° μ체λ₯Ό 볡μ¬νλ©°, 볡μ¬λ³Έμ μμ ν΄λ μλ κ°μ μν₯μ μ£Όμ§ μλλ€.
// μμ νμ
: κ° μ λ¬
let a = 10;
let b = a; // κ° λ³΅μ¬
b = 20; // b μμ
console.log(a); // 10 (μν₯ μμ)
console.log(b); // 20
λ©λͺ¨λ¦¬ λ€μ΄μ΄κ·Έλ¨:
βββββββββββ
β Stack β
βββββββββββ€
β a: 10 β β λ
립μ μΈ κ°
βββββββββββ€
β b: 20 β β λ
립μ μΈ κ° (λ³΅μ¬ ν μμ )
βββββββββββ
μ°Έμ‘°μ μν μ λ¬(Call by Reference) - μ°Έμ‘° νμ β
λμ: λ©λͺ¨λ¦¬ μ£Όμλ₯Ό 볡μ¬νλ©°, λ λ³μκ° κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν¨λ€.
// μ°Έμ‘° νμ
: μ°Έμ‘° μ λ¬
let obj1 = { name: 'John' };
let obj2 = obj1; // λ©λͺ¨λ¦¬ μ£Όμ 볡μ¬
obj2.name = 'Jane'; // obj2λ₯Ό ν΅ν΄ μμ
console.log(obj1.name); // 'Jane' (μν₯ λ°μ!)
console.log(obj2.name); // 'Jane'
console.log(obj1 === obj2); // true (κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν΄)
λ©λͺ¨λ¦¬ λ€μ΄μ΄κ·Έλ¨:
βββββββββββ ββββββββββββββββββββ
β Stack β β Heap β
βββββββββββ€ ββββββββββββββββββββ€
β obj1 ββββΌβββββββββββββββββββ>β { name: 'Jane' } β
βββββββββββ€ β β
β obj2 ββββΌβββββββββββββββββββ>β (κ°μ κ°μ²΄) β
βββββββββββ ββββββββββββββββββββ
3. Common Quiz Questionsβ
μμ£Ό λμ€λ ν΄μ¦ λ¬Έμ
λ¬Έμ 1: μμ νμ μ μ λ¬β
function changeValue(x) {
x = 100;
console.log('ν¨μ λ΄ x:', x);
}
let num = 50;
changeValue(num);
console.log('ν¨μ μΈ num:', num);
ν΄λ¦νμ¬ λ΅ λ³΄κΈ°
// ν¨μ λ΄ x: 100
// ν¨μ μΈ num: 50
μ€λͺ :
numμ μμ νμ (Number)- ν¨μμ μ λ¬ν λ κ°μ 볡μ¬νλ―λ‘,
xμnumμ λ 립μ μΈ λ³μ xλ₯Ό μμ ν΄λnumμ μν₯μ μ£Όμ§ μμ
// μ€ν νλ¦
let num = 50; // Stack: num = 50
changeValue(num); // Stack: x = 50 (볡μ¬)
x = 100; // Stack: x = 100 (xλ§ μμ )
console.log(num); // Stack: num = 50 (μν₯ μμ)
λ¬Έμ 2: μ°Έμ‘° νμ μ μ λ¬β
function changeObject(obj) {
obj.name = 'Changed';
console.log('ν¨μ λ΄ obj.name:', obj.name);
}
let person = { name: 'Original' };
changeObject(person);
console.log('ν¨μ μΈ person.name:', person.name);
ν΄λ¦νμ¬ λ΅ λ³΄κΈ°
// ν¨μ λ΄ obj.name: Changed
// ν¨μ μΈ person.name: Changed
μ€λͺ :
personμ μ°Έμ‘° νμ (Object)- ν¨μμ μ λ¬ν λ λ©λͺ¨λ¦¬ μ£Όμλ₯Ό 볡μ¬
objμpersonμ κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν΄objλ₯Ό ν΅ν΄ κ°μ²΄ λ΄μ©μ μμ νλ©΄personλ μν₯μ λ°μ
// λ©λͺ¨λ¦¬ λ€μ΄μ΄κ·Έλ¨
let person = { name: 'Original' }; // Heap: κ°μ²΄ μμ± @0x001
changeObject(person); // Stack: obj = @0x001 (μ£Όμ 볡μ¬)
obj.name = 'Changed'; // Heap: @0x001.name = 'Changed'
console.log(person.name); // Heap: @0x001.name (κ°μ κ°μ²΄)
λ¬Έμ 3: μ¬ν λΉ vs μμ± μμ β
function test1(obj) {
obj.name = 'Modified'; // μμ± μμ
}
function test2(obj) {
obj = { name: 'New Object' }; // μ¬ν λΉ
}
let person = { name: 'Original' };
test1(person);
console.log('A:', person.name);
test2(person);
console.log('B:', person.name);
ν΄λ¦νμ¬ λ΅ λ³΄κΈ°
// A: Modified
// B: Modified ('New Object'κ° μλ!)
μ€λͺ :
test1: μμ± μμ
function test1(obj) {
obj.name = 'Modified'; // β
μλ κ°μ²΄μ μμ±μ μμ
}
// personκ³Ό objλ κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν€λ―λ‘ μμ λ¨
test2: μ¬ν λΉ
function test2(obj) {
obj = { name: 'New Object' }; // β objμ μ°Έμ‘°λ§ λ³κ²½
}
// objλ μ΄μ μ κ°μ²΄λ₯Ό κ°λ¦¬ν€μ§λ§, personμ μ¬μ ν μλ κ°μ²΄λ₯Ό κ°λ¦¬ν΄
λ©λͺ¨λ¦¬ λ€μ΄μ΄κ·Έλ¨:
// test1 μ΄μ
person ββββ> { name: 'Original' }
obj ββββ> { name: 'Original' } (κ°μ κ²)
// test1 μ΄ν
person ββββ> { name: 'Modified' }
obj ββββ> { name: 'Modified' } (κ°μ κ²)
// test2 μ€ν
person ββββ> { name: 'Modified' } (λ³κ²½ μμ)
obj ββββ> { name: 'New Object' } (μ κ°μ²΄)
// test2 μ’
λ£ ν
person ββββ> { name: 'Modified' } (μ¬μ ν λ³κ²½ μμ)
// objλ μλ©Έλκ³ , μ κ°μ²΄λ κ°λΉμ§ 컬λ μ
λ¨
λ¬Έμ 4: λ°°μ΄μ μ λ¬β
function modifyArray(arr) {
arr.push(4);
console.log('1:', arr);
}
function reassignArray(arr) {
arr = [5, 6, 7];
console.log('2:', arr);
}
let numbers = [1, 2, 3];
modifyArray(numbers);
console.log('3:', numbers);
reassignArray(numbers);
console.log('4:', numbers);
ν΄λ¦νμ¬ λ΅ λ³΄κΈ°
// 1: [1, 2, 3, 4]
// 3: [1, 2, 3, 4]
// 2: [5, 6, 7]
// 4: [1, 2, 3, 4]
μ€λͺ :
modifyArray: μλ λ°°μ΄μ λ΄μ©μ μμ νλ―λ‘numbersκ° μν₯μ λ°μreassignArray: λ§€κ°λ³μμ μ°Έμ‘°λ§ λ³κ²½νλ―λ‘numbersλ μν₯ μμ
λ¬Έμ 5: λΉκ΅ μ°μ°β
// μμ νμ
λΉκ΅
let a = 10;
let b = 10;
console.log('A:', a === b);
// μ°Έμ‘° νμ
λΉκ΅
let obj1 = { value: 10 };
let obj2 = { value: 10 };
let obj3 = obj1;
console.log('B:', obj1 === obj2);
console.log('C:', obj1 === obj3);
ν΄λ¦νμ¬ λ΅ λ³΄κΈ°
// A: true
// B: false
// C: true
μ€λͺ :
μμ νμ : κ°μ λΉκ΅
10 === 10; // true (κ°μ΄ λμΌ)
μ°Έμ‘° νμ : λ©λͺ¨λ¦¬ μ£Όμλ₯Ό λΉκ΅
obj1 === obj2; // false (λ€λ₯Έ κ°μ²΄, λ€λ₯Έ μ£Όμ)
obj1 === obj3; // true (κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν΄)
λ©λͺ¨λ¦¬ λ€μ΄μ΄κ·Έλ¨:
obj1 ββββ> @0x001: { value: 10 }
obj2 ββββ> @0x002: { value: 10 } (λ΄μ©μ κ°μ§λ§ μ£Όμκ° λ€λ¦)
obj3 ββββ> @0x001: { value: 10 } (obj1κ³Ό κ°μ μ£Όμ)
4. Shallow Copy vs Deep Copyβ
μμ λ³΅μ¬ vs κΉμ 볡μ¬
μμ 볡μ¬(Shallow Copy)β
μ μ: 첫 λ²μ§Έ λ¨κ³λ§ 볡μ¬νλ©°, μ€μ²©λ κ°μ²΄λ μ¬μ ν μ°Έμ‘° μνμ΄λ€.
λ°©λ² 1: μ κ° μ°μ°μ(Spread Operator)β
const original = {
name: 'John',
address: { city: 'Taipei' },
};
const copy = { ...original };
// 첫 λ²μ§Έ λ¨κ³ μμ : μλ κ°μ²΄μ μν₯ μμ
copy.name = 'Jane';
console.log(original.name); // 'John' (μν₯ μμ)
// μ€μ²© κ°μ²΄ μμ : μλ κ°μ²΄μ μν₯!
copy.address.city = 'Kaohsiung';
console.log(original.address.city); // 'Kaohsiung' (μν₯ λ°μ!)
λ°©λ² 2: Object.assign()β
const original = { name: 'John', age: 30 };
const copy = Object.assign({}, original);
copy.name = 'Jane';
console.log(original.name); // 'John' (μν₯ μμ)
λ°©λ² 3: λ°°μ΄μ μμ 볡μ¬β
const arr1 = [1, 2, 3];
// λ°©λ² 1: μ κ° μ°μ°μ
const arr2 = [...arr1];
// λ°©λ² 2: slice()
const arr3 = arr1.slice();
// λ°©λ² 3: Array.from()
const arr4 = Array.from(arr1);
arr2[0] = 999;
console.log(arr1[0]); // 1 (μν₯ μμ)
κΉμ 볡μ¬(Deep Copy)β
μ μ: μ€μ²©λ κ°μ²΄λ₯Ό ν¬ν¨ν λͺ¨λ λ¨κ³λ₯Ό μμ ν 볡μ¬νλ€.
λ°©λ² 1: JSON.parse + JSON.stringify (κ°μ₯ μΌλ°μ )β
const original = {
name: 'John',
address: { city: 'Taipei' },
hobbies: ['reading', 'gaming'],
};
const copy = JSON.parse(JSON.stringify(original));
// μ€μ²© κ°μ²΄ μμ : μλ κ°μ²΄μ μν₯ μμ
copy.address.city = 'Kaohsiung';
console.log(original.address.city); // 'Taipei' (μν₯ μμ)
copy.hobbies.push('coding');
console.log(original.hobbies); // ['reading', 'gaming'] (μν₯ μμ)
μ νμ¬ν:
const obj = {
date: new Date(), // β λ¬Έμμ΄λ‘ λ³νλ¨
func: () => {}, // β 무μλ¨
undef: undefined, // β 무μλ¨
symbol: Symbol('test'), // β 무μλ¨
regexp: /abc/, // β {}λ‘ λ³νλ¨
circular: null, // β μν μ°Έμ‘° μ μλ¬ λ°μ
};
obj.circular = obj; // μν μ°Έμ‘°
JSON.parse(JSON.stringify(obj)); // μλ¬ λλ λ°μ΄ν° μμ€
λ°©λ² 2: structuredClone() (λͺ¨λ λΈλΌμ°μ )β
const original = {
name: 'John',
address: { city: 'Taipei' },
date: new Date(),
};
const copy = structuredClone(original);
// Date λ± νΉμ κ°μ²΄λ μ ννκ² λ³΅μ¬ κ°λ₯
console.log(copy.date instanceof Date); // true
μ₯μ :
- β Date, RegExp, Map, Set λ± μ§μ
- β μν μ°Έμ‘° μ§μ
- β μ±λ₯μ΄ μ’μ
μ νμ¬ν:
- β ν¨μ λ―Έμ§μ
- β Symbol λ―Έμ§μ
λ°©λ² 3: μ¬κ·λ‘ κΉμ λ³΅μ¬ κ΅¬νβ
function deepClone(obj) {
// nullκ³Ό λΉκ°μ²΄ μ²λ¦¬
if (obj === null || typeof obj !== 'object') {
return obj;
}
// λ°°μ΄ μ²λ¦¬
if (Array.isArray(obj)) {
return obj.map((item) => deepClone(item));
}
// Date μ²λ¦¬
if (obj instanceof Date) {
return new Date(obj);
}
// RegExp μ²λ¦¬
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// κ°μ²΄ μ²λ¦¬
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
// μ¬μ© μμ
const original = {
name: 'John',
address: { city: 'Taipei' },
hobbies: ['reading'],
date: new Date(),
};
const copy = deepClone(original);
copy.address.city = 'Kaohsiung';
console.log(original.address.city); // 'Taipei' (μν₯ μμ)
λ°©λ² 4: Lodash μ¬μ©β
import _ from 'lodash';
const original = {
name: 'John',
address: { city: 'Taipei' },
};
const copy = _.cloneDeep(original);
μμ λ³΅μ¬ vs κΉμ λ³΅μ¬ λΉκ΅β
| νΉμ± | μμ λ³΅μ¬ | κΉμ λ³΅μ¬ |
|---|---|---|
| λ³΅μ¬ λ¨κ³ | 첫 λ²μ§Έ λ¨κ³λ§ | λͺ¨λ λ¨κ³ |
| μ€μ²© κ°μ²΄ | μ¬μ ν μ°Έμ‘° | μμ ν λ 립 |
| μ±λ₯ | λΉ λ¦ | λλ¦Ό |
| λ©λͺ¨λ¦¬ | μ μ | λ§μ |
| μ¬μ© μλλ¦¬μ€ | λ¨μν κ°μ²΄ | 볡μ‘ν μ€μ²© ꡬ쑰 |
5. Common Pitfallsβ
νν ν¨μ
ν¨μ 1: λ§€κ°λ³μ μ λ¬λ‘ μμ νμ μ λ³κ²½ν μ μλ€κ³ μ°©κ°β
// β μλͺ»λ μ΄ν΄
function increment(num) {
num = num + 1;
return num;
}
let count = 5;
increment(count);
console.log(count); // 5 (6μ΄ λμ§ μμ!)
// β
μ¬λ°λ₯Έ μμ±λ²
count = increment(count); // λ°νκ°μ λ°μμΌ ν¨
console.log(count); // 6
ν¨μ 2: μ¬ν λΉμΌλ‘ μΈλΆ κ°μ²΄λ₯Ό λ³κ²½ν μ μλ€κ³ μ°©κ°β
// β μλͺ»λ μ΄ν΄
function resetObject(obj) {
obj = { name: 'Reset' }; // λ§€κ°λ³μμ μ°Έμ‘°λ§ λ³κ²½
}
let person = { name: 'Original' };
resetObject(person);
console.log(person.name); // 'Original' (리μ
λμ§ μμ!)
// β
μ¬λ°λ₯Έ μμ±λ² 1: μμ± μμ
function resetObject(obj) {
obj.name = 'Reset';
}
// β
μ¬λ°λ₯Έ μμ±λ² 2: μ κ°μ²΄ λ°ν
function resetObject(obj) {
return { name: 'Reset' };
}
person = resetObject(person);
ν¨μ 3: μ κ° μ°μ°μκ° κΉμ 볡μ¬λΌκ³ μ°©κ°β
// β μλͺ»λ μ΄ν΄
const original = {
user: { name: 'John' },
};
const copy = { ...original }; // μμ 볡μ¬!
copy.user.name = 'Jane';
console.log(original.user.name); // 'Jane' (μν₯ λ°μ!)
// β
μ¬λ°λ₯Έ μμ±λ²: κΉμ 볡μ¬
const copy = JSON.parse(JSON.stringify(original));
// λλ
const copy = structuredClone(original);
ν¨μ 4: constμ λν μ€ν΄β
// constλ μ¬ν λΉλ§ μ λλ κ²μ΄μ§, λΆλ³μ΄ μλ!
const obj = { name: 'John' };
// β μ¬ν λΉ λΆκ°
obj = { name: 'Jane' }; // TypeError: Assignment to constant variable
// β
μμ± μμ κ°λ₯
obj.name = 'Jane'; // μ μ λμ
obj.age = 30; // μ μ λμ
// μ§μ ν λΆλ³μ μνλ€λ©΄
const immutableObj = Object.freeze({ name: 'John' });
immutableObj.name = 'Jane'; // μ‘°μ©ν μ€ν¨ (strict λͺ¨λμμλ μλ¬)
console.log(immutableObj.name); // 'John' (μμ λμ§ μμ)
ν¨μ 5: 루ν λ΄ μ°Έμ‘° λ¬Έμ β
// β νν μ€μ
const arr = [];
const obj = { value: 0 };
for (let i = 0; i < 3; i++) {
obj.value = i;
arr.push(obj); // λͺ¨λ κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν΄!
}
console.log(arr);
// [{ value: 2 }, { value: 2 }, { value: 2 }]
// λͺ¨λ κ°μ κ°μ²΄μ΄λ©° μ΅μ’
κ°μ μ λΆ 2
// β
μ¬λ°λ₯Έ μμ±λ²: λ§€λ² μ κ°μ²΄ μμ±
const arr = [];
for (let i = 0; i < 3; i++) {
arr.push({ value: i }); // λ§€λ² μ κ°μ²΄ μμ±
}
console.log(arr);
// [{ value: 0 }, { value: 1 }, { value: 2 }]
6. Best Practicesβ
λͺ¨λ² μ¬λ‘
β κΆμ₯ λ°©λ²β
// 1. κ°μ²΄λ₯Ό 볡μ¬ν λ λͺ
μμ μΌλ‘ λ³΅μ¬ λ°©λ² μ¬μ©
const original = { name: 'John', age: 30 };
// μμ λ³΅μ¬ (λ¨μν κ°μ²΄)
const copy1 = { ...original };
// κΉμ λ³΅μ¬ (μ€μ²© κ°μ²΄)
const copy2 = structuredClone(original);
// 2. ν¨μμμ λΆμμ©μΌλ‘ λ§€κ°λ³μλ₯Ό μμ νμ§ μκΈ°
// β λμ¨
function addItem(arr, item) {
arr.push(item); // μλ λ°°μ΄ μμ
}
// β
μ’μ
function addItem(arr, item) {
return [...arr, item]; // μ λ°°μ΄ λ°ν
}
// 3. constλ₯Ό μ¬μ©νμ¬ μλμΉ μμ μ¬ν λΉ λ°©μ§
const config = { theme: 'dark' };
// config = {}; // μλ¬ λ°μ
// 4. λΆλ³ κ°μ²΄κ° νμν λ Object.freeze μ¬μ©
const constants = Object.freeze({
PI: 3.14159,
MAX_SIZE: 100,
});
β νΌν΄μΌ ν λ°©λ²β
// 1. λ§€κ°λ³μ μ λ¬λ‘ μμ νμ
μ μμ νλ € νμ§ μκΈ°
function increment(num) {
num++; // β ν¨κ³Ό μμ
}
// 2. μμ 볡μ¬μ κΉμ 볡μ¬λ₯Ό νΌλνμ§ μκΈ°
const copy = { ...nested }; // β κΉμ 볡μ¬λΌκ³ μ°©κ°
// 3. 루νμμ κ°μ κ°μ²΄ μ°Έμ‘°λ₯Ό λ°λ³΅ μ¬μ©νμ§ μκΈ°
for (let i = 0; i < 3; i++) {
arr.push(obj); // β λͺ¨λ κ°μ κ°μ²΄λ₯Ό κ°λ¦¬ν΄
}
7. Interview Summaryβ
λ©΄μ μμ½
λΉ λ₯Έ μκΈ°β
μμ νμ (Primitive):
- String, Number, Boolean, Undefined, Null, Symbol, BigInt
- κ° μ λ¬(Call by Value)
- Stackμ μ μ₯
- λΆλ³(Immutable)
μ°Έμ‘° νμ (Reference):
- Object, Array, Function, Date, RegExp, etc.
- μ°Έμ‘° μ λ¬(Call by Reference)
- Heapμ μ μ₯
- κ°λ³(Mutable)
λ©΄μ λ΅λ³ μμβ
Q: JavaScriptλ Call by ValueμΈκ° Call by ReferenceμΈκ°?
JavaScriptλ λͺ¨λ νμ μ λν΄ Call by Valueμ΄μ§λ§, μ°Έμ‘° νμ μ΄ μ λ¬νλ "κ°"μ λ©λͺ¨λ¦¬ μ£Όμμ λλ€.
- μμ νμ : κ°μ 볡μ¬λ³Έμ μ λ¬νλ―λ‘ μμ ν΄λ μλ κ°μ μν₯ μμ
- μ°Έμ‘° νμ : μ£Όμμ 볡μ¬λ³Έμ μ λ¬νλ―λ‘ μ£Όμλ₯Ό ν΅ν΄ μλ κ°μ²΄λ₯Ό μμ κ°λ₯
- λ¨, μ¬ν λΉ(μ£Όμ λ³κ²½)μ μλ κ°μ²΄μ μν₯μ μ£Όμ§ μμ