✔ 객체지향 프로그래밍은 객체의 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작을 하나의 논리적인 단위로 묶어 생각
✔ 객체 = 상태 데이터와 동작을 하나의 논리적읜 단위로 묶은 복합적인 자료구조
✔ 객체의 상태 데이터 = 프로퍼티
✔ 객체의 동작 = 메서드
✔ 상속 = 객체지향 프로그래밍의 핵심 개념으로, 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
✔ 코드의 재사용
✔ 자바는 프로토타입을 기반으로 상속을 구현
// 생성자 함수
function Circle(radius){
this.radius = radius;
}
/* Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를 공유해서
사용할 수 있도록 프로토타입에 추가한다.
프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.*/
Circle.prototype.getArea = function(){
return Math.PI * this.radius ** 2;
};
// 인스턴스 생성
const circle1 = new Circle(1);
const circle2 = new Circle(2);
/* Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
프로토타입 Circle.prototype으로부터 getArea 메서드를 상속받는다.
즉 Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유한다.*/
console.log(circle1.getArea === circle2.getArea); //true
console.log(circle1.getArea()); //3,141592..
console.log(circle2.getArea()); //12.56637..
✔ 프로토타입 객체(프로토타입) - 객체 간 상속을 구현하기 위해 사용
✔ 모든 객체는 [[Prototype]] 내부 슬롯을 가짐
✔ 객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고 [[Prototype]]에 저장됨
✔ 모든 프로토타입은 생성자 함수와 연결되어 있다.
1. __proto__ 접근자 프로퍼티
✔ 모든 객체는 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입, 즉 [[Prototype]] 내부 슬롯에 간접적으로 접근 할 수 있다.
- __proto__는 접근자 프로퍼티
✔ 접근자 함수(getter/setter)를 통해 [[Prototype]] 내부 슬롯의 값, 프로토타입을 취득하거나 할당함
✔ 프로토타입에 접근 -> getter / 새로운 프로토타입 할당 -> setter
- __proto__ 접근자 프로퍼티는 상속을 통해 사용
✔ __proto__ 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Objet.prototype의 프로퍼티이다.
✔ 모든 객체는 상속을 통해 Object.prototype.__proto__ 접근자 프로퍼티를 사용
- __proto__ 접근자 프로퍼티를 통해 프로퍼티에 접근하는 이유
✔ 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해
✔ 프로토타입 체인은 단방향 linked list로 구현
-> __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하고 교체하도록 구현
- __proto__ 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않음
✔ 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문
✔ 프로토타입의 참조를 취득하고 싶은 경우 Object.getPrototypeOf 메서드 사용
✔ 프로토타입을 교체하고 싶은 경우 Object.setPrototypeOf 메서드 사용
구분 | 소유 | 값 | 사용 주체 | 사용 목적 |
---|---|---|---|---|
__proto__ 접근자 프로퍼티 | 모든 객체 | 프로토타입의 참조 | 모든 객체 | 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용 |
prototype 프로퍼티 | constructor | 프로토타입의 참조 | 생성자 함수 | 생성자 함수가 자신이 생성할 객체인스턴스의 프로토타입을 할당하기 위해 사용 |
✔ 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재
✔ 리터럴 표기법으로 생성한 객체를 생성자 함수로 생각해도 무리가 없다.
리터럴 표기법 | 생성자 함수 | 프로토타입 |
---|---|---|
객체 리터럴 | Object | Object.prototype |
함수 리터럴 | Function | Function.prototype |
배열 리터럴 | Array | Array.prototype |
정규 표현식 리터럴 | RegExp | RegExp.prototype |
✔ 프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성됨
✔ 생성자 함수는 사용자 정의 생성자 함수와 빌트인 생성자 함수로 구분
✔ 객체 생성 방법 = 객체 리터럴, Object 생성자 함수, 생성자 함수, Object.create 메서드, 클래스(ES6) -> 객체 생성 방식의 차이는 있으나 모두 추상 연산 OrdinaryObjectCreate에 의해 생성
✔ OrdinaryObjectCreate는 필수적으로 자신이 생성할 객체의 프로토타입을 인수로 전달 받음
✔ 프로토타입은 OrdinaryObjectCreate에 전달되는 인수에 의해 결정
-> 인수는 객체가 생성되는 시점에 객체 생성 방식에 의해 결정
✔ 프로토타입 체인 - 자바스크립트는 객체의 프로퍼티(메서드 포함)에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색하는 것
✔ 프로토타입 체인은 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 메커니즘
function Person(name){
this.name = name;
}
// 프로토타입 메서드
Person.prototype.sayHello = function(){
console.log(`Hi! My name is ${this.name}`);
};
const me = new Person('Lee');
// hasOwnProperty는 Object.prototype의 메서드
console.log(me.hasOwnProperty('name')); // true
- 먼저 hasOwnProperty 메서드를 호출한 me 객체에서 hasOwnProperty 메서드를 검색 -> me객체에는 hasOwnProperty가 없으므로 프로토타입 체인을 따라 Person.prototype으로 이동하여 hasOwnProperty 메서드 검색
- Person.prototype에도 hasOwnProperty가 없으므로 프로토타입 체인을 따라 Object.prototype으로 이동하여 hasOwnProperty 메서드 검색
- Object.prototype에는 hasOwnProperty가 있으므로 자바스크립트 엔진은 Object.prototype.hasOwnProperty 메서드를 호출
이때, Object.prototype.hasOwnProperty 메서드의 this에는 me객체가 바인딩됨
✔ Object.prototype을 프로토타입 체인의 종점이라 부름
✔ 프로토타입 체인 = 상속과 프로퍼티 검색을 위한 메커니즘
✔ 스코프 체인 = 식별자 검색을 위한 메커니즘
✔ 프로토타입 체인과 스코프 체인은 서로 협력하여 식별자와 프로퍼티를 검색하는데 사용
✔ 오버라이딩 - 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의하여 사용
✔ 오버로딩 - 함수의 이름은 동일하지만 매개변수의 타입 또는 개수가 다른 메서드를 구현하고 매개변수에 의해 메서드를 구별하여 호출하는 방식
✔ 프로퍼티 섀도잉 - 상속 관계에 의해 프로퍼티가 가려지는 현상
✔ 생성자 함수 또는 인스턴스에 의해 교체할 수 있음
✔ 프로토타입은 직접 교체하지 않는 것이 좋음 -> 직접 상속 or 클래스 이용
const Person = (function(){
function Person(name){
this.name = name;
}
//생성자 함수의 prototype 프로퍼티를 통해 프로토타입을 교체
Person.prototype = {
// constructor 프로퍼티와 생성자 함수간의 연결 설정
constructor: Person,
sayHello(){
console.log(`Hi! My name is ${this.name}`);
}
};
return Person;
}());
const me = new Person('Lee');
console.log(me.constructor === Person); //true
console.log(me.constructor === Object); //false
function Person(name){
this.name = name;
}
const me = new Person('Lee');
// 프로퍼티 타입으로 교체할 객체
const parent = {
// constructor 프로퍼티와 생성자 함수 간의 연결을 설정
constructor:Person,
sayHello(){
console.log(`Hi! My name is ${this.name}`);
}
};
// 생성자 함수의 prototype 프로퍼티와 프로토타입 간의 연결을 설정
Person.prototype = parent;
// me 객체의 프로토타입을 parent 객체로 교체
Object.setPrototypeOf(me,parent);
me.sayHello(); // Hi! My name is Lee
// constructor 프로퍼티가 생성자 함수를 가리킴
console.log(me.constructor === Person); //true
console.log(me.constructor === Object); //false
// 생성자 함수의 prototype 프로퍼티가 교체된 프로토타입을 가리킴
console.log(person.prototype === Object.getPrototypeOf(me)); //true
✔ instanceof 연산자는 이항연산자로서 좌변에 객체를 가리키는 식별자, 우변에 생성자 함수를 가리키는 식별자를 피연산자로 받음
✔ 우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true, 그렇지 않으면 false로 평가
✔ 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인 상에 존재하는지 확인
객체 instanceof 생성자 함수
//생성자 함수
function Person(name){
this.name = name;
}
const me = new Person('Lee');
//Person.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true
console.log(me instanceof Person); //true
//Object.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true
console.log(me instanceof Object); //true
Object.create(prototype,[,propertiesObject])
/*
@param {Object} prototype = 생성할 객체의 프로토타입으로 지정할 객체
@param {Object} propertiesObject = 생성할 객체의 프로퍼티를 갖는 객체
@returns {Object} = 지정된 프로토타입 및 프로퍼티를 갖는 새로운 객체
*/
첫번째 매개변수 = 생성할 객체의 프로토타입으로 지정할 객체를 전달
두번째 매개변수 = 생성할 객체의 프로퍼티 키와 프로퍼티 디스크릅터 객체로 이루어진 객체를 전달(생략가능)
✔ 장점
- new 연산자가 없어도 객체 생성 가능
- 프로토타입을 지정하면서 객체를 생성할 수 있음
- 객체 리터럴에 의해 생성된 객체도 상속 받을 수 있음
✔ 정적 프로퍼티/메서드는 생성자 함수로 인스턴스를 생성하지 않아도 참조/호출 할 수 있는 프로퍼티/메서드를 말함
key in object
// key = 프로퍼티 키를 나타내는 문자열, object = 객체로 평가되는 표현식
for (변수선언문 in 객체){...}
✔ 객체의 모든 프로퍼티를 순회하며 열거
✔ 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 true인 프로퍼티를 순회하며 열거