객체지향 프로그래밍
객체지향이란?
객체의 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작을 하나의 노리적인 단위로 묶은것을 말함
객체란?
속성을 통해 여러개의 값을 하나의 단위로 구성한 복합적인 자료구조
const circle = {
redius: 5, //반지름
//원의 지름: 2r
getDiameter(){
return 2 * this.readius;
},
//원의 둘레
getPerimeter(){
return 2 * Math.PI * this.radius;
},
//원의 넓이
getArea(){
return Math.PI * this.radius ** 2;
}
};
console.log(circle);
console.log(circle.getDiameter());
console.log(circle.getPerimeter());
console.log(circle.getArea());
코드에서 circle 객체에서 redius 는 객체의 상태를 나타내고, getDiameter(), getPerimeter(), getArea() 를 구하는 것은 동작을 나타낸다.
상속과 프로토 타입
function Circle(radius){
this.radius = radius;
this.getArea = function(){
return Math.PI * this.radius ** 2;
}
}
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); //?
console.log(circle1.getArea());
console.log(circle2.getArea());
위 코드는 생성자 함수를 이용해서 생성한 경우
위 코드의 문제점은 아래 그림과 같이
getArea() 메서드가 중복으로 생성된다. 즉, 생성자 함수를 통해서 객체를 생성할 때 마다 중복메서드가 계속해서 생성된다는 단점이 있다.
function Circle(radius){
this.radius = radius;
}
Circle.prototype.getArea = function(){
return Math.PI * this.radius ** 2;
}
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); //?
console.log(circle1.getArea());
console.log(circle2.getArea());
자바스크립트는 프로토타입(prototype)을 기반으로 상속을 구현한다. 위 코드는 이를 이용하여 작성한 코드다.
Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입, 즉 상위 객체 역할을 하는 Circle prototyp의 모든 프로퍼티와 메서드를 상속받음
이렇게 할 경우 아래의 사진과 같이 getArea 메서드를 프로퍼티를 통해 상속받아서 사용하기 때문에 동일한 메서드가 중복 생성되는 것을 방지 할 수 있다.
프로토타입 객체
- 모든 객체는 하나의 프로토타입을 갖고, 모든 프로토타입은 생성자 함수와 연결되어있다.
__proto__ 접근자 프로퍼티
__proto__ 를 이용하여 접근할 수 있다.
const obj = {};
const parent = { x : 1 };
obj.__proto__;
obj.__proto__ = parent;
console.log(obj.x);
위의 코드처럼 __proto__ 접근자 프로퍼티를 통해 새로운 프로토타입을 할당하면 __proto__ 접근자 프로퍼티의 setter 함수인 [[Set]]이 호출 됨
사용이유?
const parent = {};
const child = {};
child.__proto__ = parent;
parent.__proto__ = child; //?
- 위 코드에서와 같이 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해서 사용한다.
- 서로가 자신의 프로토타입이 되는 비정상적인 프로토타입 체인이 만들어지기 때문에 에러를 발생시킨다.
- 따라서 프로토타입 체인은 단방향 링크드 리스트로 구현돼야 한다.
__proto__ 접근자 프로퍼티를 코드내에서 직접사용하는것은 권장하지 않는다.
- 프로토타입의 참조를 취득하고 싶은 경우에는 Object.getPrototypeOf 메서드를 사용
- 프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf 메서드를 사용할 것
const obj = {};
const parent = { x : 1 };
// obj 객체의 프로토타입을 취득
Object.getPrototypeOf(obj); // oboj.__proto__;
// obj 객체의 프로토타입을 교체
Object.setPrototypeOf(obj,parent); // obj.__proto__ = parent
console.log(obj.x);
함수 객체의 prototype 프로퍼티
- 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.
- non-constructor 인 화살표 함수, 메서드 축약표현으로 정의한 메서드는 prototype 프로퍼티를 소유하지 않으며, 생성하지도 않음
const Person = name => {
this.name = name;
};
console.log(Person.hasOwnProperty('prototype')); //?
console.log(Person.prototype); //?
const obj = {
foo() {}
};
console.log(obj.foo.hasOwnProperty('prototype')); //?
console.log(obj.foo.prototype); //?
모든 객체가 가지고 있는(Object.prototype으로부터 상속받은) __proto__ 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리킴
구분 | 소유 | 값 | 사용주체 | 사용목적 |
__proto__ 접근자 프로퍼티 | 모든 객체 | 프로토타입의 참조 | 모든 객체 | 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용 |
prototype 프로퍼티 | constructor | 프로토타입의 참조 | 생성자 함수 | 생성자 함수가 자신이 생성할 객체(인스턴스)의 프로 토타입을 할당하기 위해 사용 |
모든 프로토타입은 constructor 프로퍼티를 갖는다. 이 constructor 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킨다. 이 연결은 생성자 함수가 생성될 때 이뤄진다.
function Person(name){
this.name = name;
}
const me = new Person('Lee');
console.log(Person.prototype === me.__proto__); //?
console.log(me.constructor === Person); //?
리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입
- 리터럴 표기법에 의해 생성된 객체도 프로토타입이 존재, 하지만 리터럴 표기법에 의해 생성된 객체의 경우 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정할 수 없음
const obj = {};
console.log(obj.constructor === Object);
- 리터럴로 생성된 객체가 위의 코드를 보면 Object 생성자 함수와 constructor 프로퍼티로 연결 돼 있다. 이를 확인해 보기위해 ECMAScript 정의한 Objectt 생성자 함수를 살펴보면
- 2 에서 생성자 함수에 인수를 전달하지 않거나 undefined 또는 null을 인수로 전달하면서 호출하면 내부적으로 추상 연산(OrdinaryObjectCreate)를 호출하여 Object.prototype을 프로토타입으로 갖는 빈객체를 생성
//1. new.target이 undefined나 Object가 아닌 경우
//인스턴스 -> Foo.prototype -> Object.prototype 순으로 프로토타입 체인이 생성됨
class Foo extends Object{}
new Foo();
//2. Object 생성자 함수에 의한 객체 생성
//인수가 전달되지 않았을 때 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성
let obj = new Object();
console.log(obj);
//3. 인수가 전달된 경우에는 인수를 객체로 변환
//Number 객체 생성
obj = new Object(123);
console.log(obj);
- Object 생성자 함수 호출과 객체 리터럴의 평가는 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성하는 점에서 동일
- 하지만, new.target의 확인이나 프로퍼티를 추가하는 처리 등 세부내용이 다름
- 따라서 객체리터럴에 의해 생성된 객체는 Object 생성자 함수가 생성한 객체가 아님
- 함수 객체의 경우에 함수 선언문과 함수 표현식을 평가하여 함수 객체를 생성한 경우 Function 생성자 함수가 아니지만, 상속을 위해서 프로토타입이 필요하기 때문에 가상적인 생성자 함수를 갖음
- 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재함
리터럴 표기법 | 생성자 함수 | 프로토타입 |
객체 리터럴 | Object | Object.prototype |
함수 리터럴 | Function | Function.prototype |
배열 리터럴 | Array | Array.prototype |
정규 표현식 리터럴 | RegExp | RegExp.prototype |
프로토타입의 생성 시점
- 프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성
- 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재하기 때문
사용자 정의 생성자 함수와 프로토타입 생성 시점
- 생성자 함수로서 호출할 수 있는 함수, 즉 constructor 는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불서 생성 됨
- 생성된 프로토타입은 오직 constructor 프로퍼티만을 갖는 객체, 프로토타입도 객체고 모든 객체는 프로토타입을 가지므로 프로토타입도 자신의 프로토타입을 갖는다.
- 생성된 프로토타입의 프로토타입은 Object.prototype
function Person(name){
this.name = neme;
}
const person = name => {
this.name = name;
};
console.log(Person.prototype); // {constructor: f}
console.log(person.prototype); // undefined
빌트인 생성자 함수와 프로토타입 생성 시점
- Object, String, Number, Function, Array, RegExp, Date, Promise 등과 같은 빌트인 생성자 함수도 일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성 됨
- 모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 생성
- 생성된 프로토타입은 빌트인 생성자 함수의 prototype 프로퍼티에 바인딩 됨
- 생성자 함수 또는 리터럴 표기법으로 객체를 생성하면 프로토타입은 생성된 객체의 [[Prototype]] 내부 슬롯에 할당됨
전역 객체
- 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 생성되는 특수한 객체
- 어떤 객체보다도 먼저 생성되는 특수한 객체이며, 어떤 객체에도 속하지 않는 최상위 객체
- 브라우저 환경에서는 winodw(또는 self, this, frames)가 전역 객체를 가리키고, Node.js 환경에서는 global이 전역 객체를 가리킴.
- 표준 빌트인 객체(Object, String, Number, Function, Array 등)와 환경에 따른 호스트 객체(클라이언트 Web API 또는 Node.js의 호스트 API), 그리고 var 키워드로 선언한 전역 변수와 전역함수를 프로퍼티로 갖음
- 객체가 최상위 객체라는 것은 프로토타입 상속 관계상에서 최상위 객체라는 의미가 아님
- 자신은 어떤 객체의 프로퍼티도 아니고 객체의 계층적 구조상 표준 빌트인 객체와 호스트 객체를 프로퍼티로 소유함을 의미
'Study > Javascript' 카테고리의 다른 글
실행 컨텍스트 (0) | 2022.06.21 |
---|---|
빌트인 객체 (0) | 2022.06.03 |
프로퍼티 어트리뷰트 (0) | 2022.05.10 |
스코프(Scope) (0) | 2022.04.29 |
함수 (0) | 2022.04.27 |