선언 및 생성
• 객체의 모형을 선언하고 그 모형을 이용해 객체를 만드는 방법은 우리가 이미 살펴본 생성자 함수를 이용하는 방법이 있고 클래스를 이용하는 방법이 있다.
생성자 함수를 이용하는 방법은 자바스크립트 초기부터 제공하던 전통적인 방법이며 클래스를 이용하는 방법은 ECMA2015부터 추가된 방법이며 ECMA2015에서 대부분의 소프트웨어 언어에서 객체지향을 위해 제공하는 클래스 방식을 지원하기 시작한 것이다.
• 동일한 멤버로 구성되는 객체를 여러 개 만들기 위해서는
우선 객체의 모형을 선언해야 하는데 이러한 모형이 클래스 이다.
• 클래스는 class 라는 예약어로 선언되며 이 클래스내에 객체의 멤버인 프로퍼티와 함수를 선언하고
클래스를 바탕으로 객체를 생성하기 위해서는 new 연산자를 이용한다.
//클래스(class 예약어로 선언되는 객체 생성 모형) 내에는 3개의 구성요소가 추가될 수 있다.
//변수, 함수, 생성자
class User {
name = '홍길동'
sayHello() {
console.log(`Hello, ${this.name}`)
}
}
let obj = new User()
obj.sayHello()//Hello, 홍길동
생성자
• 클래스(Class)는 생성자(Constructor), 프로퍼티(Property), 메서드(Method)로 구성된다.
그리고 이중 생성자는 객체 생성시에 호출되어 클래스의 프로퍼티, 메서드를 메모리에 할당하는 역할이며
객체 생성을 위해 new 연산자를 이용할 때 호출되는 것은 '생성자 (Constructor) ' 이며
클래스가 객체를 생성하기 위한 모형임으로 생성자를 가지지 않는 클래스는 존재할 수 없다.
그런데 만약 개발자가 코드에서 클래스를 선언하면서 명시적으로 생성자를 추가하지 않았다면
자동으로 기본 생성자(Default Constructor) 가 추가된다.
(기본 생성자 (Default Constructor) = 매개변수를 가지지 않는 생성자)
• 명시적으로 추가한다면 매개변수를 가지는 생성자를 추가할 수있으며
클래스내에 constructor() 로 생성자를 정의할 수 있는데 하나의 클래스내에 하나의 생성자만 추가할 수 있다.
즉 매개변수를 다르게 해서 여러 개의 생성자를 추가할 수 없다.
//모든 클래스내에 생성자는 필수이며 생성자가 없는 클래스는 존재할 수 없다
//new User() 객체 생성 구문 -> 생성자 호출 구문
//위처럼 개발자가 명시적으로 클래스내에 생성자를 추가하지 않았다면
//아래처럼 선언한 것과 동일하게 자동으로 default constructor 가 추가된다.
class User2 {
name = '홍길동'
//constructor 예약어로 선언되는 함수
//매개변수를 가지지 않는 생성자를 default constructor
//생성자 함수는 어디선가 new 로 객체 생성 시점에 객체 생성 역할을 하는 것이 중요함으로
//아래처럼 {} 로직을 가지지 않는 생성자도 그 자체로 의미가 있다.
//원한다면 {} 에 로직을 추가해서 객체 생성과 동시에 개발자의 로직이 실행되게 할수도 있다.
constructor(){}
sayHello() {
console.log(`Hello, ${this.name}`)
}
}
let obj2 = new User2()
obj2.sayHello()//Hello, 홍길동
//자동으로 추가되는 생성자는 매개변수 없는 생성자.
//객체 생성하면서 데이터를 전달받아서 생성되어야 한다면 개발자가 명시적으로 생성자 선언
class User3 {
name = '홍길동'
constructor(name){
//name - 생성자가 호출되는 시점에서만 유지되는 local variable
//this.name - 객체의 멤버
this.name = name
}
sayHello() {
console.log(`Hello, ${this.name}`)
}
}
let obj3 = new User3('김길동')
obj3.sayHello()//Hello, 김길동
//하나의 클래스내에 생성자 여러개 선언은 불가능하다.
//==> 클래스내에 명시적으로 생성자 선언은 하나만 할 수있다.
//class User4 {
//constructor(){}
//constructor(name){}
//}
//let obj4 = new User4('김길동')//error - A class may only have one constructor
객체 멤버
• 객체가 가져야 하는 프로퍼티와 메서드가 클래스내에 선언되어 있어야 하고 실제 객체가 생성되면
선언된프로퍼티와 메서드가 객체의 메모리에 할당된다.
클래스에 멤버를 선언할 때는 프로퍼티의 경우
생성자내에서 “this.프로퍼티명” 형태로 선언하고
메서드의경우 생성자 밖에 클래스 영역에 선언하는 것이 일반적이며
클래스 내에 멤버를 추가할 때는 일반적으로 변수를 선언하기 위한
let, var, const 예약어와 함수를 선언하기 위한 function 예약어는 사용할 수 없다.
• 프로퍼티를 생성자에서 선언하지 않고 클래스 영역에서 선언할 수 있고
함수를 this.sayHello1 으로 생성자 내에서 선언할 수도 있다.
일반 메서드 내에서 클래스 멤버를 this.address 처럼 선언할 수도 있으며
객체 생성후에 클래스 외부에서 멤버를 추가할 수있다.
//이 클래스로 생성되는 객체의 멤버를 준비하는 위치.
class User {
//class 영역에 멤버 선언.
name
sayHello(){
//멤버 함수내에서 this 로 객체 멤버 선언.
this.address = 'seoul'
console.log(`Hello2 , ${this.name}, ${this.age}, ${this.address}`)
}
constructor(name, age){
this.name = name
//생성자 내에서 this 로 멤버 선언.
this.age = age
this.sayHello2 = function(){
console.log(`Hello2 , ${this.name}, ${this.age}`)
}
}
}
let obj = new User('홍길동', 20)
//클래스 외부에서 객체의 멤버 추가
obj.phone = '0101111'
obj.sayHello()//Hello2 , 홍길동, 20, seoul
obj.sayHello2()//Hello2 , 홍길동, 20
console.log(obj.address, obj.phone)//seoul 0101111
private 멤버
• 클래스 내부에서만 사용되는 멤버는 대부분의 소프트웨어 언어에서 private 예약어를 추가하는 방식으로 선언한다.
private 같은 예약어에 의해 멤버의 사용 범위가 제한된다는 의미에서 용어로 “접근제한자”라고 한다.
하지만 자바스크립트에서는 일반 소프트웨어 언어에서 제공하는 접근제한자를 제공하지 않는다.
대신 클래스 내부에 선언된 멤버가 외부에서 사용되지 않게 하려면 이름을 #으로 시작하는 이름을 지정한다.
또한 #으로 시작하는 이름의 멤버는 클래스 영역에 멤버가 선언될 때만 사용할 수 있으며
생성자내에서 멤버를 선언할때는 사용할 수 없다.
//private
//js 에는 private 이라는 예약어 없다. oop 에서 private 이라는 개념은
//클래스 내에 선언된 멤버가 그 클래스내에서만 사용되게 강제하는 기법을 의미한다.
class User {
//이 클래스내에 선언된 변수, 함수 중에서 일부는
//클래스내에서 필요하니까 선언하기는 하지만
//외부에서 이용하지 못하게 하고 싶을 때 ==> 외부와 결합도를 낮추어서 유지보수성 증대
//private 개념으로 사용하고자 하는 멤버의 이름을 #으로 시작
#name;
age;
constructor(name, age){
this.#name = name
this.age = age
}
#myFun(){
console.log('myFun call')
}
sayHello(){
console.log(`Hello, ${this.#name}, ${this.age}`)
this.#myFun()
}
}
let user = new User('홍길동', 20)
// user.#name = '김길동'//error
user.age = 30
user.sayHello()
// user.#myFun()//error
static 멤버
• 클래스에 선언된 멤버는 객체 멤버와 static 멤버로 구분된다.
• static 멤버는 static 예약어로 선언된 프로퍼티, 메서드이며
객체 멤버는 static 예약어로 선언되지 않은 프로퍼티, 메서드이다.
클래스는 객체를 만들기 위한 모형이며 클래스를 이용해 객체를 생성한 후 객체를 이용해 멤버를 이용한다.
그런데 static 멤버는 클래스내에 선언되기는 하지만 객체로 이용되기 위한 멤버가 아니며
그럼으로 static 멤버는 객체 생성 없이 클래스명으로 이용이 가능하다.
• 객체 멤버는 클래스로 생성되는 객체를 위한 멤버임으로
객체 생성시마다 그 객체의 메모리에 할당되는 멤버이다.
하지만 static 멤버는 객체를 위한 멤버가 아니므로
객체가 몇 개가 생성된다고 하더라도 하나의 단일 메모리에 할당된다.
//static
//클래스는 객체의 모형이다. 클래스의 대부분의 멤버는 객체 메모리에 할당되어야 하는 객체 멤버이다
//필요에 의해서, 선별적으로 객체별 메모리 할당이 필요없는 멤버에 한해서 (static 멤버)
class MyClass {
data1 = 10
static data2 = 20
myFun1(){
console.log(`myFun1 call ${this.data1} ${this.data2}`)
}
static myFun2(){
//static member 함수내에서 object member - undefined
console.log(`myFun2 call${this.data1} ${this.data2}`)
}
}
//static 멤버 접근 - 객체 생성 없이 클래스명으로 접근
MyClass.myFun2()//myFun2 call undefined 20
console.log(MyClass.data2)//20
//객체 멤버를 클래스명으로 접근
console.log(MyClass.data1)//undefined
// MyClass.myFun1()//error
//객체 멤버 - 객체명으로
let obj = new MyClass()
console.log(obj.data1)//10
obj.myFun1()//myFun1 call 10 undefined
//static 멤버 - 객체명으로
console.log(obj.data2)//undefined
// obj.myFun2()//error
//==>static 멤버는 클래스명으로
//==>object 멤버는 객체생성후 객체명으로
상속
• 객체지향에서 상속은 중요한 기법이며 공통적인 코드를 한곳에 작성하고 이를 상속받아 재사용하는 기법이다
생성자 함수를 이용해 객체지향 프로그래밍을 한다면 프로토타입을 이용해 상속을 구현하는데
대부분의 소프트웨어 언어에서는 클래스로 객체지향 프로그램을 하며
extends 예약어로 상속관계를 명시해서 작성한다.
자바스크립트에서 객체의 모형을 클래스로 만들었다면 다른 소프트웨어 언어와 마찮가지로
extends 예약어로 다른 클래스를 상속받아 작성할 수 있다.
▼ private 멤버, static 멤버 상속
• 상위에 선언된 private 멤버, 즉 #으로 시작하는 이름의 멤버는 하위 클래스에 상속되지 않으며
private 이라는 개념이 멤버를 자신의 클래스내에서만 사용하고자 하는 것임으로 상속관계로 명시했다고 하더라도 하위 클래스에서 상위 클래스의 #으로 시작하는 이름의 멤버는 사용할 수 없다.
그리고 상위의 멤버가 static 으로 선언되어 있다면
이 멤버는 하위에 상속되어 하위 클래스에 선언한 것처럼 사용할 수 있다.
▼ super로 상위 생성자 호출
• 자바스크립트 포함 모든 소프트웨어 언어에서 객체지향에 공통적으로 사용되는 예약어가 this, super 이다.
• this 는 객체 자신을 지칭하는 예약어이며 super 는 상위 클래스를 지칭하는 예약어이며
super의 사용은 하위 생성자에서 명시적으로 상위 생성자를 호출해야 하는 경우에 사용되거나 아니면 하위클래스에서 상위에 선언된 멤버를 지칭할 때 사용됩니다.
객체를 생성하기 위해서 생성자를 호출하면 상위 클래스의 생성자까지 호출이 되어야 한다.
• super() 로 상위의 생성자를 호출하는 구문은 생성자 내에서 가장 첫 줄에 한번만 작성할 수 있으며
상위 생성자를 super() 로 호출할 때 ( ) 에 상위 생성자에 전달해야 하는 매개변수 값을 명시할 수 있다.
▼ super 로 상위 멤버 이용
• 상위에 선언된 멤버는 하위에서 상속되어 이용된다.
즉 상위에 선언된 멤버를 자신 클래스에 선언된 멤버처럼 사용하는데 경우에 따라 상위에 선언된 멤버를 하위에서 동일이름으로 다시 정의하는 경우가 있다.
이를 용어로 오버라이딩(Overriding) 이라고 한다.
• 상위의 멤버를 오버라이딩으로 하위에서 재정의 해서 사용하는 경우
필요에 의해 상위에 선언된 동일이름의 멤버를 이용해야 하는 경우가 있다.
이때 super 라는 예약어로 명시적으로 상위의 멤버를 지칭하게 된다.
"use strict"
//상속
//extends 예약어로 상속관계 명시
//상속의 기본은 부모에 선언된 멤버를 내꺼처럼 이용
class Shape {
name = '도형'
x = 0
y = 0
draw(){
console.log(`${this.name}을 ${this.x}, ${this.y}에 그립니다.`)
}
}
class Rect extends Shape {
width = 0
height = 0
}
let rect = new Rect()
rect.name = '사각형'
rect.x = 10
rect.y = 10
rect.width = 100
rect.height = 100
rect.draw()
//private, static 상속관계
class Super {
data1 = 10
#data2 = 20
static data3 = 30
}
class Sub extends Super {
static data4 = 40
subFun(){
//부모에 선언된 것을 마치 자신것 처럼
console.log(this.data1)
// console.log(this.#data2)//error.. private 은 자신의 클래스내에서만 사용하겠다는 선언
//상속 안됨 하위 클래스에서 사용할 수 없다
}
}
let obj = new Sub()
obj.subFun()
console.log(Super.data3)
console.log(Sub.data3)//30 ==>ok, 상위 static 멤버를 하위 클래스명으로 이용 가능
//생성자 연결관계
//생성자를 개발자가 명시적으로 선언하지 않은 경우다
//default생성자가 자동으로 추가함
//ok - 별문제 없이 객체 생성 된다
// class Super1 {
// }
// class Sub1 extends Super1 {
// }
// let obj1 = new Sub1()
//개발자가 명시적으로 생성자를 추가한다
class Super1 {
constructor(){}
}
class Sub1 extends Super1 {
//아래처럼 생성자를 명시적으로 선언하면 에러.
// constructor(){}
//아래처럼 개발자가 명시적으로 생성자를 추가했고, 상위 클래스가 명시되어 있다면
//생성자 내에서 꼭 상위 생성자 호출해야함
// constructor(){
// super()//상위 생성자 호출구문. 생성자 내에서만 작성 가능
// //상속관계에 있는 클래스에서 명시적으로 생성자를 추가하지 않으면 자동으로
// //추가되는 생성자 코드
// }
constructor(){
//super() 는 생성자 내에서 첫줄에 한번만
//1 - 상위 생성자 호출, 2 - 자신 멤버 메모리 할당.
super()
this.data = 10
}
}
let obj1 = new Sub1()
//test3..... 상위 생성자 호출..
class Super2 {
constructor(name, x, y){
this.name = name
this.x = x
this.y = y
}
}
class Sub2 extends Super2 {
constructor(name, x, y, width, height){
super(name, x, y)//상위 생성자 호출하면서 매개변수 전달
this.width = width
this.height = height
}
}
let obj2 = new Sub2('rect2', 20, 20, 200, 200)
//override
//상위에 선언된 멤버를 동일 이름으로 하위 클래스에서 재정의
//상위 멤버 상속은 X
//변수 오러라이드 - 하위에서 데이터 초기화 다시.
//함수 오러라이드 - 알고리즘 교체
//이름을 다르게 해서 멤버를 선언하면 동일 행동, 데이터를 가지는 멤버가 두개임
class Shape2 {
data = 10
constructor(name, x, y){
this.name = name
this.x = x
this.y = y
}
calcArea(){
console.log(`${this.name}의 면적을 계산합니다.`)
}
}
class Rect2 extends Shape2 {
//변수 오버라이드
data = 20
constructor(name, x, y, width, height){
super(name, x, y)
this.width = width
this.height = height
}
//함수 오버라이드
calcArea(){
//상위 멤버 상속이 안된다. 명시적으로 상위 함수를 호출하겠다면
super.calcArea()
console.log(`넓이는 ${this.width * this.height}`)
}
}
let rect3 = new Rect2('사각형', 10, 10, 20, 20)
console.log(rect3.data)//20
rect3.calcArea()//사각형의 면적을 계산합니다.
//넓이는 400
'BootCamp Review' 카테고리의 다른 글
2024.10.02-04 (Day 19,20) - JavaScript 비동기/ Web APIs (0) | 2024.10.04 |
---|---|
2024.10.01 (Day 18) - JavaScript OOP / 클로저(Closure) (0) | 2024.10.01 |
2024.09.26-30 (Day 15,16,17) - JavaScript OOP / 다양한 OOP 기법 (0) | 2024.09.30 |
2024.09.25-26 (Day 14,15) - JavaScript OOP / 프로토 타입 (0) | 2024.09.26 |
2024.09.24 (Day 13) - JavaScript OOP / 생성자 함수 (0) | 2024.09.24 |