객체지향 프로그래밍
객체지향 프로그래밍이란?
- 프로그램을 작성할 때 객체들을 만들어 서로 소통하도록하는 방법
객체(Object)란??
객체 지향의 객체는 표현하고자 하는 사물을 추상적으로 표현
const person = {
name: "JJamVa",
nextYear: function (currentAge) {
currentAge.ageUp();
},
};
const person1 = {
age: 12,
ageUp: function () {
this.age++;
},
};
person.nextYear(person1);
console.log(person1); // {age:13, ageUp:f}
생성자(constructor)
- 객체를 만들 때, new연산자와 함께 사용하는 함수
생성자 만들기
- 생성자를 생성할 때, 함수처럼 사용하지만 함수의 이름을 맨앞의 글자를 대문자로 작성
function Person() {}
let person1 = new Person(); // instance라고 부름
생성자 확인 방법(instanceof
)
- 생성자 함수에는 return이 없지만 new연산자를 통해 객체(
instance
)를 반환한다. - 생성자 함수에서 생성된 객체인지 확인하기 위해
instanceof
를 통해 확인한다. - 반환값은
true
,false
function Person(name) {
(this.name = name),
(this.speak = function () {
console.log(`내이름은 ${this.name} 입니다.`);
});
}
let person1 = new Person("JJamVa");
let person2 = Person("JJamVa");
console.log(person1 instanceof Person); //true
console.log(person2 instanceof Person); //false
new연산자 유/무의 차이점
console.log(person1); // Person {name: 'JJamVa', speak: ƒ}
console.log(person2); // undefined
위의 코드 기반으로 person1과 person2의 출력을 확인해보았다.
person1은 instance의 정보를 출력하고 있지만 person2는 undefined가 출력되었다.
person2는 new연산자가 없기 때문에 this는 Person의 객체를 지목하는 것이 아닌 전역 변수인 window객체를 가르키고 있다.
추가적으로 Person이라는 생성자 함수에서는 return이 없기 때문에 undefined가 출력된 것이다.
프로토타입(prototype)
- 특정 객체에 대한 참조
- 동일한 생성자 함수로 생성된 인스턴스가 하나의 메소드를 만들어 자원을 효율적으로 관리
생성자함수.prototype.속성이름 = function(){}
function Person(name) {
(this.name = name),
(this.speak = function () {
console.log(`${this.name} 출력`);
});
}
let person1 = new Person("Hello");
let person2 = new Person("World");
console.log(person1); // {name: 'Hello', speak: ƒ}
console.log(person2); // {name: 'World', speak: ƒ}
console.log(person1.speak()); // Hello 출력
console.log(person2.speak()); // World 출력
function Person(name) {
this.name = name;
}
Person.prototype.speak = function () {
console.log(`${this.name} 출력`);
};
let person1 = new Person("Hello");
let person2 = new Person("World");
console.log(person1); // {name: 'Hello'}
console.log(person2); // {name: 'World'}
console.log(person1.speak()); // Hello 출력
console.log(person2.speak()); // World 출력
위의 두가지 코드를 보면 결론적으로 원하는 instance에 대한 speak메소드 호출은 성공적이다.
하지만 Person에 대한 instance를 출력해보면 speak메소드가 객체 내에 내장되어 있는지 없는지 차이가 있다.
prototype
으로 인해 객체내에 있는 공통된 메소드를 제거함으로 메모리 절약을 했다.
또한, speak를 메소드로 선언함으로써 코드 재사용성을 높일 수 있다.
prototype
와 __proto__
의 차이점
prototype
은 function안에 존재하는 참조값
__proto__
는 객체 안에 있는 Property
즉, new 연산자를 통해 생성된 instance는 __proto__
를 통해 생성자 함수의 prototype
에 접근하여 필요한 값이나 메소드를 사용
function Proto() {}
const obj = new Proto();
console.log(obj.prototype); // undefined
console.log(obj.__proto__); // {constructor:f}
console.log(obj.__proto__ === Proto.prototype); // true
객체의 상속
- 새롭게 생성할 생성자 함수가 기존의 생성자 함수에서 공통된 속성을 가지고 있으며 추가적인 속성을 추가
- 객체에 대하여 계층적인 표현 가능(부모 - 자식)
function Parent(name) {
//부모 생성자 함수
this.name = name;
}
Parent.prototype.rename = function (name) {
this.name = name;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child() {
//자식 생성자 함수
Parent.call(this);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.speak = function () {
console.log("Hello");
};
Parent.call(this);
call()함수를 통해 자식 생성자 함수의 this를 부모 생성자 함수의 this를 바라보게 설정.
자식을 통해 생성된 instance의 this가 부모 생성자 함수안에 Property 접근
Child.prototype = Object.create(Parent.prototype);
Object.create()를 통해 자식 생성자 함수에 연결 즉, 부모의 객체의 프로토타입을 자식 객체의 프로토타입에게 참조하도록 설정
class
- 생성자 함수와 프로토타입을 기반으로 구현을 하며, 좀 더 간결하고 객체를 생성하고 다루기 쉽게 한다.
class의 사용법
- class키워드 + 이름 + 중괄호으로 구성
function Person(Pname) {
this.name = Pname;
}
Person.prototype.speak = function () {
console.log(`내 이름은 ${this.name} 입니다.`);
};
class Person {
constructor(Pname) {
this.name = Pname;
}
speak() {
console.log(`내 이름은 ${this.name} 입니다.`);
}
}
생성자 함수에서 내부적인 동작은 동일하지만 코드 가독성이 좋아지며
편리하게 개선된 문법을 **슈가신텍스 (Syntactic sugar)**라 한다.
class 상속
- class 상속은
extends
키워드를 사용 - 상속을 받는 클래스(자식 클래스)는 서브 클래스(subclass) 혹은 파생 클래스(derived class)
- 부모 클래스의 Property를 상속받기 위해 super 함수를 사용.
super는 부모 생성자를 참조
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayParentName() {
console.log(`부모의 이름은 ${this.name} 나이는 ${this.age} 입니다.`);
}
}
class Child extends Parent {
//Parent class를 상속
constructor(name, age, childname) {
super(name, age);
this.myName = childname;
}
sayChildName() {
// 상속을 받으면 부모 클래스의 메소드를 사용 가능. this로 접근
this.sayParentName();
console.log(`자식의 이름은 ${this.myName} 입니다.`);
}
}
super 사용시 주의할 점
- 만약 서브 클래스에 생성자 함수를 사용하고 싶다면 반드시 생성자 함수안에서 super 함수를 사용
- 서브 클래스에 생성자 함수가 없다면 super 함수가 자 동으로 호출됨으로 부모 클래스의 프로퍼티를 상속 받는다.
- 생성자 함수에서 this값을 사용할 경우 super 함수는 반드시 this보다 먼저 실행
- 서브 클래스가 아닌 클래스에서 사용하면 에러 발생
비공개(private) Property
- 비공개(private) Property는 객체 안의 중요한 정보를 보호
- 중요한 데이터를 조심스럽게 다뤄야 할 경우, 외부에서 함부로 수정할 수 없게 만들고 싶을 때 Private Property로 데이터를 변경
class Person {
#weight; //weight를 Private Property로 설정
constructor(name, wt) {
this.name = name;
this.#weight = wt;
}
getWeight() {
return this.#weight;
}
setWeight(wt) {
this.#weight = wt;
}
}
let human = new Person("Hello", 50);
human.weight = 70;
console.log(human); // Person {name: 'Hello', weight: 70, #weight: 50}
console.log(human.getWeight()); // 70
human.#weight = 80;
console.log(human); // Person {name: 'Hello', #weight: 80}
console.log(human.getWeight()); // 80
human.setWeight(90);
console.log(human); // Person {name: 'Hello', #weight: 90}
console.log(human.getWeight()); // 90
human.weight = 70;
weight라는 속성이 존재하지 않기 때문에 human이라는 instance에 속성이 추가가 된다.
human.#weight = 80;
human라는 Private Property에 직접 접근하여 값을 변환한다.
#weight
는 Private Property이지만, 완전한 Private 접근 제어가 되지 않았기 때문에 외부에서 직접 접근하거나 변경이 가능.
따라서 human.#weight = 80과 코드로 private 필드의 값을 변경.
이에 따라 class내에 변수를 Private Property를 설정할 때, #_변수명
설정을 권장
setWeight(wt) {
this.#weight = wt;
}
human.setWeight(90);
객체의 상태를 캡슐화하고 더욱 안전하게 데이터를 관리 하기 위해 setWeight 메소드를 사용된 것이다.
get
, set
키워드를 이용한 Private Property 접근
get
,set
을 이용하여 객체의 (Private) Property를 조금 더 편리하게 접근이 가능.
class Person {
#weight;
constructor(name, wt) {
this.name = name;
this.#weight = wt;
}
get weight() {
return this.#weight;
}
set weight(wt) {
this.#weight = wt;
}
}
let human = new Person("JJamVa", 70);
console.log(human.weight); // 70
human.weight = 90;
console.log(human.weight); // 90
setWeight 메소드와 getWeight 메소드의 동작결과는 똑같다.
하지만 Private Property에 대한 데이터 변경, 데이터 값에 대해서 더 편리해졌다.