728x90
프로포타입에 대해서
자바스크립트는?
- 자바스크립트는 명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어
- 자바스크립트는 클래스 기반 객체 지향 프로그래밍 언어보다 효율적이고 더 강력함
- 자바스크립트는 객체 기반의 프로그래밍 언어이며 자바스크립트를 이루고 잇는거의 모든 것이 객체
- 원시타입 값을 제외한 나머지 값(함수, 배열, 정규 표현식 등) 모두 객체
ES6 클래스
- 클래스도 함수이며 기존 프로토타입 기반 패턴의 문법적 설탕
- 클래스를 프로토타입 기반 객체 생성 패턴의 단순한 문법적 설탕으로 보기보다는 새로운 객체 생성 매커니즘으로 봐야함
- 클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않음
- 클래스는 생성자 함수보다 엄격하며 클래스는 생성자 함수에서는 제공하지 않는 기능 제공
1. 객체지향 프로그래밍
- 객체지향 프로그래밍은 프로그램을 명령어 또는 함수 목록으로 보는 전통적인 명령형 프로그래밍의 절차지향적 관점에서 벗어나 여러개의 독립적 단위
- 객체지향 프로그래밍은 독립적인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임
- 객체지향 프로그래밍은 객체의 상태를 나타내는 데이터와 데이터 조작하는 동작을 하나의 논리적인 단위로 묶어 생각
- 객체지향 프로그래밍은 실세계의 실체를 인식하는 철학적 사고를 프로그래밍에 접목하려는 시도에서 시작됨
- 실체는 특징이나 성질을 나타내는 속성을 가지고 있고 이를 통해 실체를 인식, 구별 가능
- 추상화 : 다양한 속성 중 프로그램에 필요한 속성만 간추려 내어 표현하는 것
- 객체 : 속성을 통해 여러개의 값을 하나의 단위로 구성한 복합적인 자료구조
- 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조
- 객체의 상태 데이터 : 프로퍼티
- 객체의 동작 : 메서드
- 객체는 자신의 고유 기능을 수행하면서 다른 객체와 관계성 가질 수 있음
- 다른 객체와 메세지를 주고 받거나 데이터 처리 가능
- 다른 객체의 상태 데이터나 동작을 상속받아 사용
- 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조
2. 상속과 프로토 타입
a. 상속
- 상속 : 객체지향 프로그래밍의 핵심 개념
- 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
- 자바스크립트는 프로토 타입을 기반으로 상속을 구현하여 불필요한 중복 제거
- 중복 제거 방법 : 기존의 코드 적극 재사용 => 개발 비용 ✕
b. 자바스크립트는 프로토 타입을 기반으로 상속 구현
- Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입, 즉 상위 객체 역할을 하는 Circle.prototype의 모든 프로퍼티와 메서드를 상속 받음
- getArea 메서드는 단 하나만 생성되어 프로토 타입인 Circle.prototype의 메서드로 할당되어 있음
- Circle 생성자 함수가 생성하는 모든 인스턴스는 getArea 메서드를 상속받아 사용 가능
- 자신의 상태를 나타내는 radius 프로퍼티만 개별적으로 소유하고 내용이 동일한 메서드는 상속을 통해 공유하여 사용
c. 상속은 코드의 재사용 관점에서 유용함
- 생성자 함수가 생성할 모든 인스턴스가 공통적으로 사용할 프로퍼티나 메서드를 프로토타입에 미리 구현해두면 생성자함수가 생성할 모든 인스턴스는 별도 구현 없이 상위 객체인 프로토타입의 자산 공유해 사용 가능
3. 프로토 타입 객체 = 프로토타입
- 프로토타입 객체
- 프로토타입 객체 : 객체지향 프로그래밍의 근간을 이루는 객체 간 상속을 구현하기 위해 사용
- 프로토타입은 어떤 객체의 상위 객체의 역할을 하는 객체로 다른 객테에 공유 프로퍼티(메서드) 제공
- 프로토타입을 상속받은 하위 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용 가능
- [[Prototype]]
- 모든 객체는 [[Prototype]] 이라는 내부 슬롯 가지며 이 내부 슬롯 값은 프로토타입의 참조(null인경우도 있음)
- [[Prototype]]에 저장되는 프로토타입은 객체 생성 방식에 의해 결정
- 객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고 [[Prototype]]에 저장됨
- 예를들어
- 객체 리터럴에 의해 생성된 객체의 프로토타입은 Object.prototype이고 생성자 함수에 의해 생성된 객체의 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어있는 객체
- 모든 객체는 하나의 프로토타입을 갖고 있고 모든 프로토타입은 생성자 함수와 연결되어 있음
- 객체와 프로토타입과 생성자함수는 연결되어있음
- [[Prototype]]내부 슬롯에는 직접 접근할 수 없지만 __proto__접근자 프로퍼티를 통해 자신의 프로토타입, 즉 자신의 [[Prototype]] 내부슬롯이 가리키는 프로토타입에 간접 접근 가능
- 프로토타입은 자신의 constructor 프로퍼티를 통해 생성자 함수에 접근 가능
- 생성자 함수는 자신의 prototype 프로퍼티를 통해 프로토타입에 접근 가능
a. __proto__ 접근자 프로퍼티
- 모든 객체는 __proto__ 접근자 프로퍼티 통해 자신의 프로토 타입, 즉[[Prototype]] 내부 슬롯에 간접 접근 가능
- __proto__ 접근자 프로퍼티를 통해 person 객체의 [[Prototype]] 내부 슬롯이 가리키는 객체인 Object.prototype에 접근한 결과를 크롬 브라우저가 콘솔에 표시한 것
a-1. __proto__는 접근자 프로퍼티다.
- 내부 슬롯은 프로퍼티가 아님 => 자바스크립트는 직접 접근, 호출 불가능
- 그래서 [[Prototype]] 내부슬롯의 값, 프로토타입에 __proto__접근자 프로퍼티로 간접적으로 접근 가능
- 접근자 프로퍼티 : 자체적으로 값[[Value]] 프포터티 어트리뷰트를 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수 [[Get]], [[Set]] 프로퍼티 어트리뷰트로 구성된 프로퍼티
- object.prototype의 접근자 프로퍼티인 __proto__는 getter/setter함수라고 부르는 접근자 함수[[Get]], [[Set]] 프로퍼티 어트리뷰트에 할당된 함수를 통해 [[Prototype]] 내부 슬롯의 값, 프로토타입을 취득하거나 할당
- __proto__접근자 프로퍼티를 통해 프로토타입에 접근하면 내부적으로__proto__ 접근자 프로퍼티의 getter함수인 [[Get]] 호출
- __proto__ 접근자 프로퍼티를 통해 새로운 프로토타입 할당하면 __proto__ 접근자 프로퍼티의 setter 함수인 [[Set]] 호출
a-2. __proto__ 접근자 프로퍼티는 상속을 통해 사용된다.
- Object.prototype
- __proto__ 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티
- 모든 객체는 상속을 통해 Object.prototype.__proto__ 접근자 프로퍼티 사용 가능
- 모든 객체는 프로토타입의 계층 구조인 프로토타입 체인에 묵여 있음
- 자바스크립트 엔진은 객체의 프로퍼티(메서드 포함)에 접근하려고 할때 해당 객체에 접근하려는 프로퍼티가 없다면 __proto__접근자 프로퍼티가 가리키는 참조를 따라 자신의 부모 역할 하는 프로토타입의 프로퍼티를 순차적으로 검색
- 프로토타입 체인의 종점, 프로토 타입체인의 최상위 객체는 Object.prototype이며 이 객체의 프로퍼티와 메서드는 모든 객체에 상속됨
a-3. __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
- 프로토타입에 접근하기 위해 접근자 프로퍼티를 사용하는 이유는 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위함
- 서로가 자신의 프로토타입이 되는 비정상적인 프로토타입 체인이 만들어지면 __proto__ 접근자 프로퍼티는 에러 발생
- 순환참조하는 프로토타입 체인이 만들어지면 프로토타입 체인 종점이 존재하지 않기 때문에 프로토타입 체인에서 프로퍼티 검색할 때 무한 루프에 빠짐
- 아무런 체크 없이 무조건적으로 프로토타입을 교체할 수 없도록 __proto__ 접근자 프로퍼티를 통해 프로토타입 접근, 교체 해야함
- 프로토타입 체인은 단방향 링크드리스트로 구현되어야함
- 프로퍼티 검색방향이한쪽으로만 흘러야함
a-4. __proto__ 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.
- __proto__ 접근자 프로퍼티는 ES5까지 ECMAScript 사양에 포함되지 않는 비표준 -> ES6부터 표준
- 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 잇는 것은 아니기때문에 __proto__ 접근자 프로퍼티를 직접 사용 권장 ✕
- 직접 상속을 통해 Object.prototype을 상속받지 않는 객체를 생성할 수도 있기 때문에 __proto__ 접근자 프로퍼티를 사용할 수 없는 경우가 있음
- 프로토타입의 참조를 취득하고 싶은 경우 Object.getPrototypeOf 메서드를 사용 =>get Object.prototype__proto__ 일치
- 프로토 타입 교체하고 싶은 경우 Object.setPrototypeOf 메서드 사용=> set Object.prototype__proto__ 일치
b. 함수 객체의 prototype 프로퍼티
- 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 객체(인스턴스)의 프로토타입 가리킴
- 생성자 함수로서 호출할 수 없는 함수인 non-constructor인 화살표함수와 ES6 메서드 축약 표현으로 정의한 메서드는 prototype 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않음
- 생성자 함수로 호출하기 위해 정의하지 않은 일반 함수(함수 선언문, 함수 표현식)도 prototype 프로퍼티를 소유하지만 객체를 생성하지않는 일반 함수의 prototype 프로퍼티는 아무 의미 없음
- 모든 객체가 가지고 있는(Object.prototype으로부터 상속받은) __proto__ 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리킴 하지만 프로퍼티를 사용하는 주체가 다름
c. 프로토타입의 constructor 프로퍼티와 생성자 함수
- 모든 프로토타입은 constructor 프로퍼티 가짐
- constructor 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수 가리킴
- 이 연결은 생성자 함수가 생성될 때 즉 함수 객체가 생성될 때 이뤄짐
- 예시
- Person 생성자 함수는 me 객체 생성
- me 객체는 프로토타입의 constructor 프로퍼티 통해 생성자 함수와 연결됨
- me 객체에는 constructor 프로퍼티가 없지만 me 객체의 프로토타입인 Person.prototype에는 constructor 프로퍼티가 있음
- me 객체는 프로토 타입인 Person.prototype의 constructor 프로퍼티를 상속받아 사용
4. 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입
a. 생성자 함수와 프로토타입
- 생성자 함수에 의해 생성된 인스턴스는 프로토타입의 constructor 프로퍼티에 의해 생성자 함수와 연결됨
- constructor 프로퍼티가 가리키는 생성자 함수는 인스턴스를 생성한 생성자 함수
- 리터럴 표기법에 의한 객체 생성 방식과 같이 명시적으로 new 연산자와 함께 생성자 함수를 호출해 인스턴스를 생성하지 않는 객체 생성방식도 있음
- 리터럴 표기법에 의해 생성된 객체도 프로토타입이 존재함
- 하지만 리터럴 표기법에 의해 생성된 객체의 경우 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자함수라고 단정할 수 없음
b. 추상 연산
- Object 생성자 함수에 인수를 전달하지 않거나 undefined 또는 null을 인수로 전달하면서 호출하면 내부적으로 추상 연산 OrdinaryObjectCreate를 호출해 Object.prototype을 프로토타입으로 갖는 빈 객체 생성
- 추상 연산
- 추상연산은 ECMAScript 사양에서 내부 동작의 구현 알고리즘을 표현한 것
- ECMAScript 사양에서 설명을 위해 사용되는 함수와 유사한 의사코드
c. OrdinaryObjectCreate와 Function 생성자 함수
- OrdinaryObjectCreate
- 객체 리터럴이 평가될 땐 추상 연산 OrdinaryObjectCreate 호출해 빈 객체 생성하고 프로퍼티 추가
- Object 생성자 함수 호출과 객체 리터럴의 평가는 추상 연산 OrdinaryObjectCreate 호출해 객체 생성하는 점은 동일하나 new.target 확인이나 프로퍼티 추가하는 처리 등 세부 내용이 다름
- 객체 리터럴에 의해 생성된 객체는 Object 생성자 함수가 생성한 객체가 아님
- Function 생성자 함수
- Function 생성자 함수 호출해 생성한 함수는 렉시컬 스코프를 만들지 않고 전역 함수인것처럼 스코프 생성하며 클로저도 만들지 않음
- 함수 선언문과 함수 표현식을 평가해 함수 객체를 생성한 것은 Function 생성자 함수가 아님
- 하지만 constructor 프로퍼티를 통해 확인해보면 foo 함수는 Function 생성자 함수임
d. 리터럴 표기법
- 리터럴 표기법에 의해 생성된 객체도 상속을 위해 프로토타입 필요
- 리터럴 표기법에 의해 생성된 객체도 가상적인 생성자 함수를 가짐
- 프로토 타입은 생성자 함수와 더불어 생서되며 prototype, constructor 프로퍼티에 의해 연결되어있기 때문
- 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재함
- 리터럴 표기법에 의해 생성된 객체는 생성자 함수에 의해 생성된 객체는 아님
- 리터럴표기법으로 생성한 객체도 생성자 함수로 생성한 객체와 본질적인 면에서는 큰 차이 없음
- 프로토타입의 constructor 프로퍼티를 통해 연결되어있는 생성자 함수를 리터럴 표기법으로 생성한 객체를 생성한 생성자 함수로 생각해도 큰 무리 없음
5. 프로토타입의 생성 시점
- 객체는 리터럴 표기법 또는 생성자 함수에 의해 생성되므로 결국 모든 객체는 생성자 함수와 연결되어있음
- 생성자 함수는 사용자가 직접 정의한 사용자 정의 생성자 함수와 자바스크립트가 기본 제공하는 빌트인 생성자 함수로 구분
- Object.create 메서드와 클래스에 의한 객체 생성
- Object.create 메서드와 클래스로 생성한 객체도 생성자 함수와 연결되어 있음
- 프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성
- 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재하기 때문
a. 사용자 정의 생성자 함수와 프로토타입 생성 시점
- 내부 메서드 [[Construct]]를 갖는 함수 객체는 new 연산자와 함께 생성자 함수로 호출 가능
- 생성자 함수로서 호출할 수 있는 함수, constructor는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 생성
- non-constructor는 프로토타입이 생성되지 않음
- 함수선언문은 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행됨
- 함수 선언문으로 정의된 Person 생성자 함수는 어떤 코드보다 먼저 평가되어 함수 객체가 됨
- 이때 프로토타입도 더불어 생성됨
- 생성된 프로토타입은 Person 생성자 함수의 protorype 프로퍼티에 바인딩 됨
- 생성된 프로토타입은 오직 constructor 프로퍼티만을 갖는 객체
- 프로토타입도 객체이고 모든 객체는 프로토타입을 가지므로 프로토타입도 자신의 프로토타입을 가짐
- 생성된 프로토타입의 프로토타입은 Object.prototype
- 빌트인 생성자 함수가 아닌 사용자 정의 생성자 함수는 자신이 평가되어 함수 객체로 생성되는 시점에 프로토타입도 생성되고 생성된 프로토타입은 언제나 Object.prototype
b. 빌트인 생성자 함수와 프로토타입 생성 시점
- Object, String, Number, Function, Array, RegExp, Date, Promise 등 빌트인 생성자 함수도 일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입 생성
- 모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 생성됨
- 생성된 프로토타입은 빌트인 생성자 함수의 prototype 프로퍼티에 바인딩됨
전역 객체
- 전역 객체는 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 생성되는 특수한 객체
- 전역 객체는 클라이언트 사이드 환경에서는 window, 서버사이드 환경에서는 global 객체 의미
- 전역 객체는 표준 빌트인 객체들과 환경에 따른 호스트 객체 클라이언트 Web API 또는 Node.js의 호스트 API, var 키워드로 선언한 전역 변수와 전역 함수를 프로퍼티로 가짐
- Math, Reflect, JSON을 제외한 표준 빌트인 객체는 모두 생성자 함수
- 표준 빌트인 객체인 Object도 전역 객체의 프로퍼티, 전역 객체가 생성되는 시점에 생성
객체가 생성되기 이전에 생성자 함수와 프로토타입은 이미 객체화되어 존재
- 이후 생성자 함수 또는 리터럴 표기법으로 객체 생성하면 프로토타입은 생성된 객체의 [[Prototype]] 내부 슬롯에 할당
- 이로써 생성된 객체는 프로토타입을 상속받음
6. 객체 생성 방식과 프로토타입의 결정
객체의 생성 방법
- 객체 리터럴
- Object 생성자 함수
- 생성자 함수
- Object.create aptjem
- ES6 클래스
세부적인 객체 생성 방식의 차이는 있으나 추상 연산 OrdinatyObjectCreate에 의해 생성된다는 공통점 있음
- 추상 연산 OrdinatyObjectCreate는 필수적으로 자신이 생성할 객체의 프로토 타입을 인수로 전달 받음
- 그리고 자신이 생성할 객체에 추가할 프로퍼티 목록을 옵션으로 전달 가능
- 추상 연산 OrdinatyObjectCreate는 빈 객체를 생성 후 객체에 추가할 프로퍼티 목록이 인수로 전달된 경우 프로퍼티를 객체에 추가
- 인수로 전달받은 프로토타입을 자신이 생성한 객체의 [[Prototype]] 내부 슬롯에 할당한 후 생성한 객체 반환
- 프로토타입은 추상 연산 OrdinatyObjectCreate 전달되는 인수에 의해 결정됨
- 이 인수는 객체가 생성되는 시점에 객체 생성 방식에 의해 결정됨
a. 객체 리터럴에 의해 생성된 객체의 프로토타입
자바스크립트 엔진은 객체 리터럴을 평가해 객체를 생성할 때 추상 연산 OrdinatyObjectCreate 호출
- 이때 추상 연산 OrdinatyObjectCreate에 전달되는 프로토타입은 Object.prototype
- 객체 리터럴에 의해 생성되는 객체의 프로토타입은 Object.prototype
객체 리터럴에 의해 생성된 obj 객체는 Object.prototype을 프로토 타입으로 갖게 되며 Object.prototype을 상속 받음
- obj 객체는 constructor 프로퍼티와 hasOwnProperty 메서드 등을 소유하지 않지만 자신의 프로토타입인 Object.prototype의 constructor 프로퍼티와 hasOwnProperty 메서드를 자신의 자산인 것 처럼 자유롭게 사용 가능
- obj 객체가 자신의 프로토타입인 Object.prototype 객체를 상속받았기 때문
b. Object 생성자 함수에 의해 생성된 객체의 프로토타입
- Object 생성자 함수를 인수 없이 호출하면 빈객체 생성
- 객체 리터럴 방식은 객체 리터럴 내부에 프로퍼티를 추가하지만 Object 생성자 함수 방식은 일단 빈 객체를 생성한 이후 프로퍼티 추가
- Object 생성자 함수 호출하면 추상 연산 OrdinatyObjectCreate 호출
- 이땐 추상 연산 OrdinatyObjectCreate에 전달되는 프로토타입은 Object.prototype
- Object 생성자 함수에 의해 생성되는 객체의 프로토타입은 Object.prototype
- Object 생성자 함수에 의해 생성된 obj 객체는 Object.prototype을 프로토타입으로 갖게되며 Object.prototype을 상속받음
c. 사용자 정의 생성자 함수에 의해 생성된 객체의 프로토타입
- new 연산자와 함께 생성자 함수를 호출해 인스턴스 생성하면 추상 연산 OrdinatyObjectCreate 호출
- 이때 추상연산 OrdinatyObjectCreate에 전달되는 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어 ㅣㅇㅆ는 객체
- 생성자 함수에 의해 생성되는 객체의 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체
- 추상 연산 OrdinatyObjectCreate에 의해 생성자 함수와 생성자 함수의 prototype 프로퍼티에 바인딩되어있는 객체와 생성된 객체 사이에 연결이 만들어짐
- 표준 빌트인 객체인 Object 생성자 함수와 더불어 생성된 프로토타입 Object.prototype은 다양한 빌트인 메서드를 갖고 있지만 사용자 정의 생성자 함수 Person과 생성된 프로토 타입 Person.prototype의 프로퍼티는 constructor 뿐
- 프로토타입은 객체기때문에 프로퍼티를 추가, 삭제할 수 있고 추가, 삭제된 프로퍼티는 프로토타입 체인에 즉각 반영됨
- Person 생성자 함수를 통해 생성된 모든 객체는 프로토타입에 추가된 sayHello 메서드를 상속받아 자신의 메서드처럼 사용 가능
모던 자바스크립트 딥다이브 책을 정리한 내용입니다!!!
728x90
'개념 정리 > 모던 자바스크립트 딥다이브' 카테고리의 다른 글
모던 자바스크립트 : strict mode (0) | 2023.11.10 |
---|---|
모던 자바스크립트 : 프로토타입 2 (0) | 2023.11.09 |
모던 자바스크립트 : 함수와 일급 객체 (0) | 2023.11.08 |
모던 자바스크립트 : 생성자 함수에 의한 객체 생성 (1) | 2023.11.07 |
모던 자바스크립트 : 프로퍼티 어트리뷰트 (0) | 2023.11.06 |