Loading the catalog…
Loading the catalog…
개발자를 위한 넓고 얕은 컴퓨터 지식, 넓얕컴지 JavaScript의 객체는 단순한 key-value 저장소 가 아니다. 프로퍼티마다 별도의 속성이 있고, 객체끼리 프로토타입으로 연결되며, 배열과 함수조차 이 객체 모델 위에서 동작한다. 여기에 반복자(Iterator), Symbol, Generator, 모듈까지 연결하면 JavaScript라는 언어가 데이터를 어떻게 조직하고 확장하는지 보이기 시작한다. JavaScript 1에서는 값 → 변수 → 스코프 → 실행 컨텍스트 → 렉시컬 환경 → 클로저 → this 라는 코드 실행 과정 을 따라갔다. 이번에는 시선을 객체 쪽으로 돌린다. 객체 → 프로퍼티 → 생성자 → 프로토타입 → 클래스 → 배열 → 컬렉션 → 반복 프로토콜 → Generator → 오류 → 모듈 이 흐름을 이해하면 class 나 map() 같은 문법을 사용하는 수준을 넘어 JavaScript가 데이터를 어떤 모델로 표현하고 공유하는지 설명할 수 있다. 1️⃣ JavaScript 객체는 무엇일까? 1-1. 객체(Object)와 프로퍼티(Property) ⭐️⭐️⭐️ JavaScript 객체는 프로퍼티(Property)의 집합이다. 프로퍼티는 키(Key)와 값(Value)의 조합으로 구성된다. const user = { name: "Esther", age: 28, hello() { return `Hello ${this.name}`; } }; name , age , hello 가 프로퍼티 키이고 각각 "Esther" , 28 , 함수 객체가 프로퍼티 값이다. JavaScript 객체의 프로퍼티 키는 기본적으로 문자열(String) 또는 Symbol이다. 숫자로 작성해도 일반 객체에서는 문자열 형태의 키로 취급된다. const obj = { 1: "one" }; obj[1]; obj["1"]; 두 접근은 같은 프로퍼티를 가리킨다. 1-2. 점 표기법과 대괄호 표기법(Dot & Bracket Notation) ⭐️⭐️ user.name; user["name"]; 둘 다 프로퍼티에 접근한다. 다만 런타임에 결정되는 키를 사용하려면 대괄호 표기법이 필요하다. const key = "name"; user[key]; 객체를 단순한 구조체처럼만 생각하면 이 특징을 놓치기 쉽다. JavaScript 객체는 실행 중에도 프로퍼티를 추가·삭제하고 동적으로 탐색할 수 있는 구조 다. 1-3. 계산된 프로퍼티 이름(Computed Property Name) ⭐️ 객체를 만들 때도 표현식을 프로퍼티 키로 사용할 수 있다. const key = "username"; const user = { [key]: "Esther" }; 결과적으로 user.username 에 접근할 수 있다. 이 기능은 설정 객체나 동적으로 구성되는 데이터에서 자주 사용된다. 2️⃣ 객체의 프로퍼티에는 숨겨진 속성이 있다 겉으로 보면 다음 프로퍼티는 단순히 name → Esther 처럼 보인다. const user = { name: "Esther" }; 하지만 ECMAScript의 객체 모델에서 데이터 프로퍼티는 값뿐 아니라 여러 속성을 가진다. 2-1. 프로퍼티 어트리뷰트(Property Attribute) 대표적인 속성은 다음과 같다. [[Value]] [[Writable]] [[Enumerable]] [[Configurable]] 개념적으로 다음과 같다. name ├─ Value → "Esther" ├─ Writable → true ├─ Enumerable → true └─ Configurable → true writable 은 값 변경 가능 여부, enumerable 은 열거 대상 여부, configurable 은 삭제하거나 프로퍼티 정의를 변경할 수 있는지와 관련된다. 일반적인 객체 리터럴로 만든 프로퍼티에서는 대부분 신경 쓰지 않아도 되지만 라이브러리·프레임워크·메타프로그래밍을 이해하려면 중요한 개념이다. 2-2. 프로퍼티 디스크립터(Property Descriptor) 프로퍼티의 속성은 Object.getOwnPropertyDescriptor() 로 확인할 수 있다. Object.getOwnPropertyDescriptor(user, "name"); 반대로 Object.defineProperty() 를 이용하면 프로퍼티 속성을 직접 지정할 수도 있다. Object.defineProperty(user, "id", { value: 10, writable: false, enumerable: false, configurable: false }); 이렇게 만든 id 는 일반적인 프로퍼티와 다른 규칙을 가진다. 2-3. 데이터 프로퍼티와 접근자 프로퍼티(Data & Accessor Property) 프로퍼티가 항상 값을 직접 저장하는 것은 아니다. const user = { firstName: "Esther", lastName: "Kang", get fullName() { return `${this.firstName} ${this.lastName}`; } }; fullName 은 값을 직접 가지고 있는 데이터 프로퍼티가 아니라 Getter를 통해 계산되는 접근자 프로퍼티(Accessor Property)다. Setter를 이용하면 값의 변경 과정도 통제할 수 있다. 2-4. [[Get]]과 [[Set]] user.name 이라는 단순한 코드 뒤에서도 객체 내부에서는 프로퍼티를 찾고 값을 읽는 일련의 동작이 일어난다. ECMAScript 명세에서는 이런 내부 동작을 [[Get]] , [[Set]] 같은 내부 메서드(Internal Method)로 표현한다. 개발자가 obj.[[Get]]() 처럼 직접 호출하는 것은 아니다. [[...]] 표기는 실제 JavaScript API가 아니라 ECMAScript 명세에서 객체 내부 동작을 설명하기 위한 표기 다. 이 개념은 곧 프로토타입 체인과 연결된다. 3️⃣ 객체의 프로퍼티를 어떻게 찾을까? ⭐️⭐️⭐️ 객체에서 프로퍼티를 찾을 때 JavaScript는 객체 자신만 확인하지 않는다. 먼저 객체 자신을 찾고, 없다면 연결된 프로토타입으로 이동한다. 이 구조가 JavaScript 객체 상속의 핵심이다. 3-1. 자체 프로퍼티(Own Property)와 상속 프로퍼티(Inherited Property) ⭐️⭐️⭐️ 객체 자신이 직접 가지고 있는 프로퍼티를 자체 프로퍼티(Own Property)라고 한다. 프로토타입에서 찾아오는 프로퍼티는 상속 프로퍼티(Inherited Property)다. const user = { name: "Esther" }; "name" in user; "toString" in user; 둘 다 true 가 될 수 있다. name 은 객체 자체에 있고 toString 은 프로토타입 체인에서 찾을 수 있기 때문이다. 3-2. Object.hasOwn() ⭐️⭐️ 객체 자체가 가진 프로퍼티인지 확인하려면 Object.hasOwn() 을 사용할 수 있다. Object.hasOwn(user, "name"); // true Object.hasOwn(user, "toString"); // false 따라서 in 과 Object.hasOwn() 은 같은 검사를 하는 것이 아니다. in → 프로토타입 체인까지 검색 Object.hasOwn() → 객체 자신만 검사 3-3. Object.keys(), values(), entries() ⭐️⭐️ 객체를 순회할 때 자주 사용한다. Object.keys(user); Object.values(user); Object.entries(user); 각각 열거 가능한 자체 프로퍼티의 키, 값, [key, value] 쌍을 가져온다. 프로토타입의 프로퍼티까지 가져오는 것은 아니다. 4️⃣ 생성자 함수와 new 4-1. 객체를 만드는 방법 ⭐️⭐️ JavaScript에서 객체를 만드는 방법은 하나가 아니다. const user = {}; const user = new Object(); const user = Object.create(...); const user = new User(); 가장 흔한 방법은 객체 리터럴 {} 이지만 여러 객체가 동일한 초기화 규칙과 동작을 가져야 한다면 생성자 함수(Constructor Function)를 사용할 수 있다. 4-2. 생성자 함수(Constructor Function) ⭐️⭐️⭐️ function User(name) { this.name = name; } const user1 = new User("Esther"); const user2 = new User("Reina"); 같은 User 생성자를 이용해 여러 객체를 만들 수 있다. 4-3. new는 실제로 무엇을 할까? ⭐️⭐️⭐️ new User() 는 단순히 User() 를 호출하는 것과 다르다. 개념적으로 다음 과정이 일어난다. new User() ↓ 새 객체 생성 ↓ 새 객체의 [[Prototype]] → User.prototype 연결 ↓ this → 새 객체 ↓ User 함수 실행 ↓ 새 객체 반환 이 과정에서 생성자 함수와 프로토타입이 연결된다. 4-4. 생성자(Constructor)와 비생성자(Non-constructor) JavaScript의 함수 객체라고 모두 new 와 함께 사용할 수 있는 것은 아니다. 개념적으로 함수에는 호출 가능한 내부 동작인 [[Call]] 이 있고 일부 함수는 new 호출이 가능한 [[Construct]] 까지 가진다. 일반 함수 선언과 일부 함수 표현식은 생성자가 될 수 있지만 화살표 함수는 생성자 함수로 사용할 수 없다. const User = () => {}; new User(); // TypeError 4-5. new.target 함수가 new 와 함께 호출되었는지 확인할 수 있는 new.target 도 존재한다. function User() { console.log(new.target); } 프레임워크 애플리케이션에서 자주 직접 사용할 기능은 아니지만 생성자 호출 모델을 이해하는 데 도움이 된다. 5️⃣ 프로토타입(Prototype) ⭐️⭐️⭐️ JavaScript의 객체지향 모델에서 가장 중요한 개념 중 하나다. JavaScript는 다른 객체를 자신의 프로토타입으로 연결하고 그 연결을 이용해 프로퍼티와 메서드를 공유한다. 『Deep Dive』에서도 프로토타입을 객체 상속과 중복 제거의 핵심 메커니즘으로 다룬다. 5-1. prototype과 [[Prototype]]은 다르다 ⭐️⭐️⭐️ 가장 많이 혼동하는 부분이다. 생성자 함수의 prototype 프로퍼티: User.prototype 인스턴스 객체 내부의 [[Prototype]] : user.[[Prototype]] 개념적으로 다음 관계가 만들어진다. User │ │ prototype ▼ User.prototype ▲ │ [[Prototype]] │ user User.prototype 은 일반 객체다. user 가 가진 내부 [[Prototype]] 연결이 그 객체를 가리킨다. 5-2. 프로토타입에 메서드를 두는 이유 ⭐️⭐️⭐️ 다음 생성자를 생각해보자. function User(name) { this.name = name; this.hello = function () { return this.name; }; } 객체를 1,000개 만들면 hello 함수도 인스턴스마다 생성될 수 있다. 대신 프로토타입에 두면 공유할 수 있다. function User(name) { this.name = name; } User.prototype.hello = function () { return this.name; }; user1 ─┐ user2 ─┼──▶ User.prototype.hello() user3 ─┘ 프로토타입은 상속만을 위한 개념이 아니라 여러 객체가 동작을 공유하는 메커니즘 이다. 5-3. 프로토타입 체인(Prototype Chain) ⭐️⭐️⭐️ user.toString(); user 자신에게 toString 이 없다면 JavaScript는 상위 프로토타입을 탐색한다. user ↓ User.prototype ↓ Object.prototype ↓ null 이 연결이 프로토타입 체인이다. 프로퍼티 검색은 가장 가까운 객체부터 위로 올라간다. 5-4. 프로퍼티 섀도잉(Property Shadowing) ⭐️⭐️ 프로토타입에 name 이 존재하지만 객체 자신에게 같은 프로퍼티를 만들면 객체 자신의 값이 먼저 발견된다. user.name ← 발견 ↓ User.prototype.name 상위 프로퍼티가 삭제된 것은 아니다. 하위 객체의 프로퍼티가 검색 과정에서 가리고 있는 것이다. 5-5. Object.create() ⭐️⭐️⭐️ 특정 객체를 프로토타입으로 가지는 객체를 직접 만들 수도 있다. const parent = { hello() { return "hello"; } }; const child = Object.create(parent); 이 경우: child ↓ [[Prototype]] parent ↓ Object.prototype 생성자 함수 없이도 객체 사이의 프로토타입 관계를 만들 수 있다는 점에서 JavaScript의 상속은 본질적으로 객체 간 연결 이라는 사실이 잘 드러난다. 5-6. instanceof ⭐️⭐️ user instanceof User 이를 단순히 User 생성자로 만들어졌는지 기록을 확인한다 라고 이해하면 부족하다. 본질적으로는 User.prototype 이 user 의 프로토타입 체인 안에 존재하는지를 확인한다. user ↓ User.prototype ← 존재? ↓ Object.prototype 그래서 프로토타입 관계를 변경하면 instanceof 결과도 달라질 수 있다. 6️⃣ 클래스(Class) ⭐️⭐️⭐️ ES6 이후 JavaScript에서도 class 문법을 사용할 수 있다. class User { constructor(name) { this.name = name; } hello() { return this.name; } } 겉모습은 Java 같은 클래스 기반 언어와 비슷하지만 JavaScript의 객체 상속 모델이 프로토타입에서 다른 방식으로 바뀐 것은 아니다. class 역시 프로토타입 객체 모델 위에서 동작한다. 6-1. 클래스 메서드는 어디에 있을까? ⭐️⭐️⭐️ class User { hello() {} } hello() 가 각각의 인스턴스 안에 복제되는 것이 아니다. 개념적으로: User.prototype.hello 에 존재하며 여러 인스턴스가 공유한다. 6-2. constructor ⭐️⭐️ constructor 는 new 로 객체가 만들어질 때 인스턴스를 초기화한다. class User { constructor(name) { this.name = name; } } 6-3. 정적 메서드(Static Method) ⭐️⭐️ class User { static create() { return new User("default"); } } 정적 메서드는 인스턴스가 아니라 클래스 자체에서 사용한다. User.create(); user.create() 와는 다른 위치에 존재한다. 6-4. 인스턴스 필드와 정적 필드(Instance & Static Field) ⭐️⭐️ 현대 JavaScript에서는 클래스 필드도 선언할 수 있다. class User { role = "user"; static count = 0; } role 은 인스턴스마다 존재하고 count 는 클래스 자체에 연결된다. 6-5. private field ⭐️⭐️ class Account { #balance = 0; } # 을 이용한 private field는 클래스 외부에서 직접 접근할 수 없다. 과거에 클로저로 구현하던 정보 은닉과는 다른 언어 수준의 private 상태 다. 6-6. extends와 super ⭐️⭐️⭐️ class Admin extends User { constructor(name) { super(name); } } extends 는 상속 구조를 만들고 super 는 부모 생성자나 부모 메서드에 접근한다. 내부에서는 인스턴스뿐 아니라 생성자 함수 쪽의 프로토타입 관계도 함께 구성된다. 6-7. class는 단순히 문법적 설탕인가? 프로토타입 기반이라는 핵심은 맞지만 그냥 prototype 문법을 예쁘게 바꾼 것뿐 이라고 하면 부족하다. 클래스는 new 없이 호출할 수 없고, 클래스 내부 코드는 엄격 모드(Strict Mode)로 실행되며, extends , super , private field 같은 클래스 전용 규칙도 제공한다. 따라서 프로토타입 기반 객체 모델 위에 클래스 지향 개발 경험을 제공하는 언어 기능 이라고 이해하는 것이 좋다. 7️⃣ 엄격 모드(Strict Mode) JavaScript에는 과거의 느슨한 언어 동작을 제한하는 엄격 모드가 있다. "use strict"; 엄격 모드에서는 선언하지 않은 변수에 값을 할당해 암묵적인 전역 변수를 만드는 것 같은 위험한 동작이 오류가 된다. 일반 함수 호출의 this 가 undefined 가 되는 것도 중요한 차이다. 현대 개발에서는 모든 파일에 "use strict" 를 직접 작성하는 경우보다 ES 모듈이나 클래스 문법을 사용하면서 자연스럽게 엄격 모드가 적용되는 경우가 많다. ES 모듈은 기본적으로 엄격 모드로 실행된다. :chatgpt-content-reference{index="3"} 8️⃣ 배열(Array)은 일반적인 배열과 조금 다르다 ⭐️⭐️⭐️ C의 배열처럼 JavaScript Array를 같은 타입의 값이 메모리에 연속해서 저장된 자료구조 라고 생각하면 정확하지 않다. JavaScript Array는 객체 모델 위에서 특별한 동작을 제공하는 자료구조다. const arr = [10, 20, 30]; arr[0]; arr.length; 배열 인덱스는 배열 특유의 규칙을 따르는 프로퍼티 키와 연결된다. 8-1. 서로 다른 타입도 넣을 수 있다 ⭐️⭐️ const arr = [ 10, "hello", true, {}, function () {} ]; JavaScript 자체는 같은 배열 안에 서로 다른 타입의 값을 넣는 것을 막지 않는다. 실무에서는 데이터 형태의 일관성을 유지하는 편이 코드 이해와 엔진 최적화 측면에서 좋다. 8-2. length는 단순한 개수 변수가 아니다 ⭐️⭐️⭐️ const arr = [1, 2, 3]; arr.length = 1; 이렇게 하면 뒤쪽 요소가 제거될 수 있다. 반대로: arr.length = 100; 이라고 한다고 100개의 실제 값이 생기는 것은 아니다. 8-3. 희소 배열(Sparse Array) ⭐️⭐️ JavaScript 배열은 중간 인덱스가 비어 있는 상태를 가질 수 있다. const arr = []; arr[100] = "hello"; console.log(arr.length); // 101 실제 요소 하나만 넣었는데 length 는 101이 된다. 이런 희소 배열은 반복 메서드별 동작 차이나 성능 문제를 만들 수 있으므로 의도적으로 만드는 것은 보통 피한다. 9️⃣ 배열 메서드는 원본 변경 여부가 중요하다 ⭐️⭐️⭐️ JavaScript의 배열 메서드를 외울 때 단순히 무슨 기능인가 만 기억하면 부족하다. 원본 배열을 변경하는가(Mutating), 새로운 값을 반환하는가(Non-mutating) 를 함께 봐야 한다. 9-1. 원본을 변경하는 메서드 대표적으로: push() pop() shift() unshift() splice() sort() reverse() 등이 있다. const arr = [3, 1, 2]; arr.sort(); console.log(arr); // 원본 변경 9-2. 새로운 배열을 반환하는 메서드 대표적으로: map() filter() slice() concat() flat() flatMap() 등이 있다. 현대 JavaScript에는 원본을 변경하지 않는 toSorted() , toReversed() , toSpliced() , with() 같은 메서드도 추가되었다. 9-3. sort()의 비교 함수(Comparator) ⭐️⭐️⭐️ JavaScript의 sort() 는 기본적으로 값을 문자열 기준으로 정렬한다. [1, 10, 2].sort(); 숫자 정렬이라면 비교 함수를 명시하는 것이 좋다. arr.sort((a, b) => a - b); a - b < 0 이면 a가 앞, > 0 이면 b가 앞에 위치한다. 🔟 배열 고차 함수(Higher-order
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
[넓얕컴지 #6] JavaScript 2 객체는 어떻게 동작할까: 프로토타입, 클래스, 배열, 이터러블과 모듈. 개발자를 위한 넓고 얕은 컴퓨터 지식, 넓얕컴지 JavaScript의 객체는 단순한 key-value 저장소 가 아니다. 프로퍼티마다 별도의 속성이 있고, 객체끼리 프로토타입으로 연결되며, 배열과 함수조차 이 객체 모델 위에서 동작한다. 여기에 반복자(Iterator), Symbol, Generator, 모듈까지 연결하면 JavaScript라는 언어가 데이터를 어떻게 조직하고 확장하는지 보이기 시작한다. JavaScript 1에서는 값 → 변수 → 스코프 → 실행 컨텍스트 → 렉시컬 환경 → 클로저 → this 라는 코드 실행 과정 을 따라갔다. 이번에는 시선을 객체 쪽으로 돌린다. 객체 →…
Open source