[[Prototype]]는 자바스크립트 객체가 상속받는 부모 객체(프로토타입) 을 가리키는 내부 슬롯(internal slot)이야.
쉽게 말하면:
“이 객체가 어디에서 속성/메서드를 상속받는가?”
를 나타내는 숨겨진 연결 고리(link).
아주 중요한 개념이라 정확하게 설명해줄게.
✅ 1. 객체에는 보이지 않는 내부 슬롯이 있음
JS 객체에는 다음처럼 키-값 쌍 외에도
엔진 내부에서 관리하는 숨겨진 슬롯이 존재함.
그 중 하나가 바로 [[Prototype]].
예:
const obj = { a: 1 };
console.log(obj);
개발자 도구에서 펼쳐 보면:
a: 1
[[Prototype]]: Object
이렇게 보이지?
바로 이게 “프로토타입 체인”의 출발점.
✅ 2. [[Prototype]]은 상속용 링크
예를 들어:
const obj = {};
obj.toString();
obj에는 toString() 이 없음.
그런데 실행 가능함 → 왜?
→ obj.[[Prototype]] = Object.prototype 이기 때문.
그래서 JS는 다음 순서로 찾음:
- obj 내부에서 toString 찾기
- 없다 → obj.[[Prototype]] 에서 찾기
- 또 없다 → 그 프로토타입의 프로토타입에서 찾기
- 최상위까지 올라가서 없으면 에러
이 과정을 프로토타입 체인(prototype chain) 이라고 해.
✅ 3. [[Prototype]]은 JS 코드로 직접 접근 불가
이 슬롯은 언어 명세에서 내부 슬롯으로 지정된 것이라
직접 이런 식으로 접근할 수 없음:
obj.[[Prototype]] // ❌ 문법 오류
그래서 대신 써야 하는 게 다음 방법들.
✅ 4. [[Prototype]] 접근 / 설정하는 법(공식)
✔ Object.getPrototypeOf(obj)
→ 프로토타입 조회
const proto = Object.getPrototypeOf(obj);
✔ Object.setPrototypeOf(obj, proto)
→ 프로토타입 변경
Object.setPrototypeOf(obj, anotherObj);
✅ 5. __proto__ 는 [[Prototype]]의 비표준 getter/setter
개발자 도구에서는
[[Prototype]] 대신 __proto__ 로도 초기엔 표시했어.
obj.__proto__
이건 원래 비표준이었지만
너무 많이 쓰여서 현재 “웹 호환성” 때문에 유지됨.
그러나 본질적으로:
- __proto__ 는 단순히
→ [[Prototype]]에 접근하는 getter/setter 역할을 함 - 실제 내부 슬롯 이름은 [[Prototype]] (중괄호 포함)
✅ 6. 모든 객체는 프로토타입을 가진다(예외: Object.create(null))
기본 객체는 이렇게 연결됨:
obj → Object.prototype → null
배열은:
[] → Array.prototype → Object.prototype → null
함수는:
function(){} → Function.prototype → Object.prototype → null
Q. obj -> Obj.prototype -> null
이건 자바스크립트의 가장 핵심적인 개념이라, 완전 처음 본 사람도 이해할 수 있게 단계별로 설명해줄게.
🔥 먼저 결론 요약
이 말은:
자바스크립트의 모든 객체는 부모 객체(프로토타입)를 가지며, 이런 부모-자식 구조가 연결되어 있다. 최종 부모는 null이다.
이걸 프로토타입 체인이라고 한다.
이제 하나씩 뜯어보자.
✅ 1. Object는 “모든 객체의 근본(base) 생성자”다
브라우저 JS에서 Object 는 다음처럼 생긴 기본 생성자 함수다:
즉,
const obj = {};
이렇게 객체 하나 만들면
JS 내부적으로는 이렇게 한 것과 동일함:
const obj = new Object();
그래서 모든 일반 객체는 Object로부터 만들어진다.
✅ 2. Object.prototype 는 “모든 객체가 상속받는 공통 부모 객체”
JS는 상속을 클래스가 아니라 프로토타입 객체로 구현함.
즉, {} 로 만든 모든 객체는 다음을 공유함:
Object.prototype
여기에는 우리가 항상 쓸 수 있었던 이런 메서드들이 들어 있음:
- toString()
- hasOwnProperty()
- valueOf()
- isPrototypeOf()
예:
const obj = {};
obj.toString; // 있음 (하지만 obj 자체에는 없음!)
이 때문에 JS는 상속 관계를 이렇게 그려놓음:
obj
↓ 상속(프로토타입 링크)
Object.prototype
✅ 3. 왜 Object.prototype 의 프로토타입은 null 인가?
Object.prototype 은 프로그래밍 우주에서 가장 상위에 있는 최상위 객체임.
즉,
- 더 위로 올라갈 부모가 없음
- 그래서 프로토타입 체인의 끝이다
명세에서 이렇게 정의됨:
obj가 상속하는 부모 객체의 부모 객체는 === 없음
Object.getPrototypeOf(Object.prototype) === null // true
그래서 프로토타입 체인은 항상 마지막에 null로 끝남.
💡 이제 전체 그림
객체 하나:
const obj = { a: 1 };
브라우저가 내부적으로 이렇게 연결함:
obj
|
| (프로토타입)
↓
Object.prototype
|
↓
null
의미:
- obj 안에서 toString() 같은 걸 찾으면 없으니
→ Object.prototype 안에서 찾는다 - Object.prototype 에도 없으면 null 이므로
→ 여기서 탐색 종료
그게 바로 “연결되었다” 라는 의미.
📌 진짜 코드로 보기
const obj = { a: 1 };
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true console.log(Object.getPrototypeOf(Object.prototype)); // null
🧩 비슷한 구조: 배열은?
배열도 내부적으로는 이런 구조:
[]
↓
Array.prototype
↓
Object.prototype
↓
null
즉 배열도 결국 Object를 부모로 하는 구조.
🔥 초간단 요약
- Object = 기본 객체 생성자(가장 근본)
- Object.prototype = 모든 객체가 공통으로 상속받는 부모 객체
- null = 부모가 없다는 의미 → 프로토타입 체인 끝
- “연결됨” = 객체가 부모 객체로 이어져 있어 부모의 속성을 상속받는 관계
원하면:
- 함수의 prototype 과 객체의 [[Prototype]] 차이
- 클래스 문법이 실제로 어떻게 Object.prototype 체인으로 컴파일되는지
- 프로토타입 체인이 실제로 동작하는 알고리즘
도 이어서 설명해줄게!
🔥 핵심 정리
- [[Prototype]]은 JS 객체가 상속받는 부모 객체에 대한 참조
- JS 코드에서는 직접 접근 불가 (내부 슬롯)
- 개발자 도구에서는 보이지만, 실제 프로퍼티가 아님
- 상속 탐색 체인(프로토타입 체인)의 핵심
- __proto__ 는 [[Prototype]]에 접근하는 비표준 API
- 공식 접근은 Object.getPrototypeOf(), Object.setPrototypeOf()
원하면:
- 프로토타입 체인이 실제로 동작하는 상세 알고리즘
- 클래스를 사용했을 때도 내부적으로 어떻게 [[Prototype]]이 구성되는지
- 함수의 prototype 과 객체의 [[Prototype]] 차이
- 프로토타입 오염 공격(Prototype Pollution)
까지도 설명해줄게!
Q. obj.toString
질문한 내용 정확히 짚어서 설명해줄게.
✅ 1. obj.toString 과 obj.toString() 은 완전히 다르다
✔ obj.toString
→ 함수 자체를 가져오는 것(참조)
→ 실행 X
→ 단지 "이름이 toString인 함수가 여기 있다" 를 반환할 뿐
✔ obj.toString()
→ 그 함수를 실행해서 반환값을 받는 것
즉:
둘은 차원이 다르다.
✅ 2. 그럼 왜 예제로 obj.toString 을 썼냐?
예시의 목적은 “obj 객체 안에는 toString이 없지만, 프로토타입에서 상속받아서 존재한다” 를 보여주기 위한 것이라
실행이 아니라 존재 여부 확인만 한 거야.
예시 다시 보자:
여기서 의미는:
- obj 내부에는 toString 속성이 없다
- 하지만 obj.toString 을 찍어보면 뭔가 나온다
- → 이는 obj가 Object.prototype에서 상속받기 때문
즉 “프로토타입 체인으로 인해 찾을 수 있다” 는 걸 보여주기 위한 예시지
"코드를 실행시켰다"는 의도가 아니었어.
✅ 3. 실행하지 않아도 함수가 보인다 → 왜냐면 JS는 "값"으로 함수도 가진다
JS에서 함수도 객체이기 때문에
그냥 “값”처럼 변수에 담거나 표시할 수 있어.
예:
function f() {}
console.log(f);
이렇게 해도 함수가 출력됨(실행 X).
obj.toString 도 같은 원리:
console.log(obj.toString);
→ toString 함수 객체가 출력됨
→ 함수 본문이 보이거나 [Function: toString] 처럼 표시됨
하지만
console.log(obj.toString());
→ toString 함수 실행 결과(ex. "[object Object]")가 출력됨.
🔥 결론 요약
| 표현 | 의미 |
| obj.toString | toString 함수를 “가져온 것” (실행 X) |
| obj.toString() | toString 함수를 “실행한 것” |
예시에서 obj.toString 을 사용한 것은
함수 실행이 목적이 아니라
“obj가 프로토타입 체인으로부터 toString을 가지고 있다”
를 설명하기 위한 것이었음.
원하면:
- 함수도 객체라는 것의 깊은 구조
- 함수 객체의 내부 슬롯([[Call]], prototype 등)
- toString이 실제로 어디서 오는지(Object.prototype.toString 내부 동작)
도 설명해줄게!
Q. [object Object]좋아, 이건 자바스크립트를 처음 배우는 사람이라도 이해할 수 있게 완전 기초부터 설명해줄게.
🔥 결론부터
기본 객체({})의 toString() 결과가
이 나오는 이유:
기본 Object의 프로토타입(Object.prototype)이 정의한 “객체를 -> 문자열로 표현하는 기본 규칙” 때문이다.
그 규칙이 바로:
[object 타입이름 ]
여기서 타입이름이 Object이기 때문에
[object Object]
이 나오는 것.
✅ 1. toString() 은 Object.prototype.toString 이다
const obj = {}; 같은 기본 객체는 자신만의 toString이 없음.
따라서 프로토타입 체인에서 부모의 메서드 toString를 가져옴:
obj.toString()
→ Object.prototype.toString.call(obj)
즉, 진짜 실행되는 함수는 Object.prototype.toString.
✅ 2. Object.prototype.toString 의 규칙
명세(ECMAScript Spec)에 이렇게 정의됨:
객체를 -> 문자열로 변환할 때
"[object " + 내부 클래스 이름 + "]"
형식으로 반환한다.
여기서 내부 클래스 이름(internal class name)은
기본 객체의 경우 "Object" 이다.
그래서:
"[object " + "Object" + "]"
→ [object Object]
📌 그럼 내부 클래스 이름(internal [[Class]])은 뭔데?
JS 내부에는 객체에 대한 “내부 타입 태그”가 존재한다.
예:
| 값 | 내부 class | toString 결과 |
| {} | "Object" | [object Object] |
| [] | "Array" | [object Array] |
| /abc/ | "RegExp" | [object RegExp] |
| new Date() | "Date" | [object Date] |
| null | "Null" | [object Null] |
| undefined | "Undefined" | [object Undefined] |
그래서:
[].toString() → " " 로 join된 문자열이라 Array의 toString이 쓰임
Object.prototype.toString.call([]) → "[object Array]"
Object.prototype.toString.call(null) → "[object Null]"
🔥 즉 [object Object]는 그냥 "기본 객체입니다" 라는 의미
사람들이 자주 헷갈려해서 정리하면:
{} = [object Object]
= “object 타입의 object다”
= {} 같은 기본 객체를 문자열로 바꾼 기본 표현
즉 HTML이나 구조를 의미하는 게 아니라
정말로 그냥 문자열이다.
👉 직접 확인해보기
const obj = {};
console.log(obj.toString());
// → [object Object]
console.log(Object.prototype.toString.call(obj));
// → [object Object]
배열은?
console.log(Object.prototype.toString.call([])); // → [object Array]
날짜는?
console.log(Object.prototype.toString.call(new Date())); // → [object Date]
📌 왜 JSON.stringify(obj) 하면 { "a": 1 } 처럼 나오는데, toString은 이상하게 나와?
이건 목적이 다르기 때문.
| 함수 | 목적 |
| JSON.stringify() | 객체를 -> JSON 텍스트로 변환 |
| toString() | 객체의 "기본 문자열 표현" 생성(디버깅 중심) |
그래서 JSON.stringify는 의미 있는 구조를 출력하지만
toString은 타입 정보만 제공한다.
🔥 최종 정리
- {} 의 toString() 은 Object.prototype.toString 호출
- 이 함수는 내부적으로 "Object" 라는 타입 태그를 읽음
- 반환 포맷이 [object 타입이름]
- 그래서 결과는 [object Object]
즉, JS의 기본 객체를 문자열로 바꿨을 때의 표준 문자열 표현일 뿐이다.
원하면:
- 왜 Array의 기본 toString은 문자열 join인지
- 왜 Date의 toString은 사람이 읽을 수 있는 날짜인지
- Object.prototype.toString의 명세(구체적 알고리즘)
이런 것도 자세하게 알려줄게!
Q. Object.prototype.toString.call(obj)
좋아, 이 부분은 자바스크립트 함수 호출 방식(this 바인딩) 때문에 이해가 필요해.
차근차근 아주 쉽게 설명해줄게.
🎯 결론부터
Object.prototype.toString.call(obj)
은 toString 함수를 강제로 obj 를 this 로 해서 실행하는 문법이다.
즉:
toString.call(obj)
= "toString 함수야, 너의 this 를 obj 로 해서 실행해!"
그래야 Object.prototype.toString 이 obj 의 내부 타입 정보를 읽어서
[object Object], [object Array], [object Null] 같은 결과를 만들 수 있다.
✨ 1. 왜 .call(obj) 를 쓰는 거야?
자바스크립트 함수는 내부적으로 this 값을 기준으로 동작한다.
예를 들어:
obj.toString()
이렇게 하면
obj 가 this가 된다.
하지만 아래는 다름:
Object.prototype.toString()
이렇게 하면 this 가 Object.prototype 그 자체가 되어버린다.
→ 우리가 검사하려는 obj 의 타입을 알려주지 않음.
그래서 강제로 this 를 바꿈:
Object.prototype.toString.call(obj);
→ “이 toString 함수야, this = Object.prototype이 아니라, obj 로 해! = [object Object]”
✨ 2. 왜 이렇게 해야 해?
⬅ 일반적인 toString 들은 재정의되어 있음
예를 들어 배열:
console.log([1,2,3].toString());
// "1,2,3" (배열 전용 toString)
하지만 우리는 “배열인지 아닌지"를 알아내고 싶음.
이럴 때 배열이 가진 toString 말고,
Object.prototype.toString(최상단 조상) 의 기능을 쓰려면:
Object.prototype.toString.call([1,2,3]); // → "[object Array]"
즉:
배열의 toString이 아니라- Object.prototype.toString 을 강제로 “배열에 적용”하는 방식.
✨ 3. call() 은 어디에 쓰는 문법?
자바스크립트 모든 함수에는
- call(thisArg, ...args)
- apply(thisArg, argsArray)
- bind(thisArg)
같은 메서드가 존재함.
이 중 call 은 “즉시 실행 + this 지정” 용도.
예:
function say() {
console.log(this.name);
}
const obj = { name: "홍길동" };
say.call(obj);
// "홍길동"
obj.say();
say() 를 obj 에 붙인 적 없는데도
this 를 obj 로 잡아서 실행됨.
✨ 4. 다시 정리하면…
obj.toString()
obj 가 가진 toString 을 실행
(this = obj)
Object.prototype.toString.call(obj)
"Object 최상위 객체가 가진 toString 를 obj 에 적용해서 실행"
(this = obj)
= obj 내부 타입을 정확하게 추출하는 방법
🎉 최종 정리
| 표현 | 의미 |
| obj.toString() | obj 고유의 toString 호출 |
| Object.prototype.toString.call(obj) | Object의 기본 toString을 obj 를 this로 삼아 실행 → 내부 타입 검사할 때 사용 |
그래서 이걸 사용하면
Object.prototype.toString.call([]) → "[object Array]" // [].Object.prototype.toString
Object.prototype.toString.call(null) → "[object Null]" // null.Object.prototype.toString
Object.prototype.toString.call(undefined) → "[object Undefined]" // undefined.Object.prototype.toString
처럼 어떤 값이든 정확한 타입 문자열을 얻을 수 있다.
원하면:
- call/apply/bind 차이점
- 왜 null 과 undefined 도 타입 표시가 되는지
- [[Class]] 내부 태그의 원리
이런 것도 더 깊게 설명해줄게!
Q. [[Prototype]]: Object
좋아, 이 부분은 **“객체의 타입(type)”**과 **“객체의 부모 프로토타입([[Prototype]])”**이 섞여서 헷갈리는 상황이야.
지금 헷갈리는 지점들을 하나하나 정확하게 분리해서 설명해줄게.
🎯 최종 핵심 결론 5줄 요약
- obj의 타입(type) 은 "object" 이고
- obj가 상속받는 부모 객체([[Prototype]]) 는 Object.prototype 이다.
- 개발자도구는 Object.prototype 을 축약해서 “Object” 로 보여준다.
- 일반 객체 리터럴 {}는 내부적으로 Object.prototype 을 부모로 가진다.
- 타입과 프로토타입은 완전히 다른 개념이다.
이제 하나씩 상세히 설명해줄게.
1️⃣ obj 의 “타입(Type)” 은 무엇인가?
JS의 typeof 연산자를 보자:
const obj = { a: 1 };
typeof obj; // "object"
즉 obj 의 타입은 항상 “object” 이다.
이건 단순 문자열이야. 내부 동작과는 전혀 무관.
2️⃣ 그럼 “[prototype] 은 뭐야?”
자바스크립트 객체는 상속 구조(프로토타입 체인) 를 가진다.
const obj = { a: 1 } 를 만들면 실제 내부는:
obj
a: 1
[[Prototype]] → Object.prototype
즉, obj 는 부모로 Object.prototype 을 갖는다.
프로토타입이란:
- 상속받는 부모 객체
- 메서드(toString, valueOf 등)들이 들어있는 곳
- obj.toString() 을 하면 obj 에 없으므로 Object.prototype.toString 을 가져와 실행한다
3️⃣ 그런데 왜 개발자 도구는 "Object" 라고 표시함?
DevTools 는 실제 내부 값인 Object.prototype 을
쉬운 이름으로 보여주기 위해 “Object” 라고 표시하는 것.
즉:
- JS 엔진 내부 객체 이름: Object.prototype
- 브라우저 DevTools에서 표시하는 이름: Object
왜냐하면:
- Object.prototype 전체를 표시하면 너무 길고 개발자를 헷갈리게 할 수 있음
- "이 프로토타입은 Object 라는 빌트인 객체에서 왔어요" 라고 의미를 전달하는 축약 표현
브라우저마다 약간 다르게 표현하지만
전부 “Object” 라는 라벨로 표시하는 편이 더 일반적.
4️⃣ “왜 obj 의 타입을 개발자 도구에서 표시 안 해?”
타입은 매우 단순한 정보이고,
- typeof obj
- 내부 [[Type]] = Object (ECMAScript 내부)
이런 정보는 개발자 도구 객체 펼침 UI에서는 잘 안 보여준다.
왜냐하면:
- 타입은 코드에서 typeof 로 확인하면 됨
- 프로토타입은 개발자가 실수하거나 구조를 확인해야 할 때 더 중요한 디버깅 정보라서 DevTools에서 강조해서 보여줌
즉, DevTools 의 목적이 타입을 보여주는 것이 아니라
상속 관계(prototype chain)를 시각적으로 보여주는 것.
5️⃣ “왜 부모 객체를 Object라고 표시하고, Object.prototype 이라고 안 해?”
정확히 말하면:
- 실제 부모 객체는 Object.prototype
- DevTools 는 “이 프로토타입은 Object 라는 빌트인 객체의 prototype입니다” 의 의미로
“Object” 라고 라벨만 붙여서 보여줌
즉, 진짜 값은 Object.prototype 이고
DevTools 의 UI 표현은 “Object”.
6️⃣ 일반 객체(obj)의 타입과 부모 객체 관계 다시 정리
| typeof obj | "object" |
| obj 의 [[Prototype]] | Object.prototype |
| 개발자 도구 표시 | [[Prototype]]: Object |
| 부모 객체의 실제 이름 | Object.prototype |
❗ “일반 객체 obj 는 Object 로부터 만들어졌잖아. 그럼 이미 부모 아닌가?”
여기서 중요한 구분:
✔ Object (함수)
JS 기본 빌트인 생성자 함수
function Object() {}
✔ Object.prototype (부모 객체)
Object 로 만든 인스턴스들이 상속받는 실제 부모 객체
✔ obj = {} (자식 객체)
Object.prototype 을 부모로 갖는 일반 객체
즉:
Object (함수)
└── Object.prototype (부모 객체)
└── obj = {} (인스턴스)
그래서 상속 구조는:
obj < Object.prototype < null
🔥 비유로 이해하면 쉽다
- Object 함수 = "사람을 찍어내는 공장"
- Object.prototype = "사람들이 공통으로 가진 유전자(부모 DNA)"
- obj = {} = 공장에서 만들어진 사람
그래서 DevTools 는 사람(obj)을 클릭하면
"이 사람의 부모 DNA는 Object야" 라고 표시하는 느낌.
📌 마지막으로 직관적으로 정리
obj 의 타입
- "object"
- 코드에서 typeof 로 확인하는 정보
obj 의 프로토타입([[Prototype]])
- Object.prototype (부모 객체)
- obj 가 상속하는 메서드들이 들어있는 곳
- 개발자 도구는 라벨을 “Object” 로 표시할 뿐
원하면:
- 왜 Function.prototype 도 객체인지
- 프로토타입 체인이 실제로 메서드를 찾는 알고리즘
- ES6 클래스와 프로토타입의 관계
이런 것도 더 깊게 설명해줄 수 있어!
'개발자도구' 카테고리의 다른 글
| URL path가 폴더명처럼 보일 때, 폴더 전체를 불러오는 것일까? & md(markdown) 파일 렌더링 방식 (0) | 2025.12.01 |
|---|---|
| https://global.apis.naver.com 은 어떤 도메인? 엔드포인트 & 게이트웨이 & CORS (0) | 2025.11.24 |
| https://pagead2.googlesyndication.com 는 무슨 도메인? (0) | 2025.11.23 |
| Access-Control-Allow-Credentials & Access-Control-Allow-Origin (0) | 2025.11.17 |
| Connection: keep-alive & Keep-Alive: timeout=30 (0) | 2025.11.15 |
| Host 헤더는 https:// & path 없이 도메인 이름만? (0) | 2025.11.13 |
| Accept-Encoding 헤더 / Content-Encoding 헤더 / Vary 헤더: Accept-Encoding 의 관계 (0) | 2025.11.13 |
| 서비스 워커(Service Worker) :: 전역 객체 self (0) | 2025.10.29 |