본문 바로가기
JavaScript

자바스크립트#09 : 클래스

by haheehee 2022. 12. 22.

객체 지향 패러다임

객체 지향 프로그래밍 객체를 만들고, 객체 간 상호작용을 중심으로 개발하는 방법론을 말한다.

C를 제외한 모든 프로그래밍 언어는 OOP(Object Oriented Programming, 객체지향언어)를 기반으로 만들어진 프로그래밍 언어이다.

자바스크립트도 객체 지향 패러다임을 기반으로 한다.

클래스(class)라는 문법으로 객체(object)를 효율적이고 안전하게 만들어 객체 지향 패러다임에 적용

 

추상화(abstraction)

프로그램에 필요한 요소만 사용 해서 객체를 표현하는 것을 말한다.

 


.push()로 배열에 값을 넣을 때, iterator는 해당 값을 해당 인덱스에 넣고, 다음 인덱스를 가리킴.

JSON.stringify()     : 객체를 JSON문자열로 변환할 때 사용하는 메소드

 

와 같은 것을 사용해도좋지만, 객체를 처리하는 함수를 사용해 프로그램을 구성해보겠다.

getSumOf()와 getAverageOf()로 총합과 평균 구하기 (매개변수로 학생 객체를 받아)

<script>
	const students = []
	students.push({name: "콩이", java: 99, javascript: 95, html: 89, css: 97})
	students.push({name: "말랑", java: 89, javascript: 87, html: 100, css: 91})
	students.push({name: "통통이", java: 75, javascript: 80, html: 79, css: 84})
	students.push({name: "그레이", java: 96, javascript: 74, html: 72, css: 80})
	
	function getSumOf(s) {
		return s.java + s.javascript + s.html + s.css
	}
	
	function getAverageOf(s) {
		return getSumOf(s) / 4
	}
	
	let output = "name\tsum\taverage\n"
	for(const s of students) {
		output += `${s.name}\t${getSumOf(s)}점\t${getAverageOf(s)}점\n`
	}
	console.log(output)
</script>

for구문으로 function묶기

<script>
	const students = []
	students.push({name: "콩이", java: 99, javascript: 95, html: 89, css: 97})
	students.push({name: "말랑", java: 89, javascript: 87, html: 100, css: 91})
	students.push({name: "통통이", java: 75, javascript: 80, html: 79, css: 84})
	students.push({name: "그레이", java: 96, javascript: 74, html: 72, css: 80})
	
	for(const student of students) {
		student.getSum = function () {
			return this.java + this.javascript + this.html + this.css
		}
		
		student.getAverage = function () {
			return this.getSum() / 4
		}
	}
	
	let output = "name\tsum\taverage\n"
	for(const s of students) {
		output += `${s.name}\t${s.getSum()}점\t${s.getAverage()}점\n`
	}
	console.log(output)
</script>

객체를 생성하는 함수

<script>
	function createStudent(name, java, javascript, html, css) {
		return {
			name: name,
			java: java,
			javascript: javascript,
			html: html,
			css: css,
			
			getSum() {
				return this.java + this.javascript + this.html + this.css
			},
			getAverage() {
				return this.getSum() / 4
			},
			toString() {
				return `${this.name}\t${this.getSum()}점\t${this.getAverage()}점\n`
			}
		}
	}
	
	const students = []
	students.push(createStudent("콩이", 99, 95, 89, 97))
	students.push(createStudent("말랑", 89, 87, 100, 91))
	students.push(createStudent("통통이", 75, 80, 79, 84))
	students.push(createStudent("그레이", 96, 74, 72, 80))
	
	let output = "name\tsum\taverage\n"
	for(const s of students) {
		output += s.toString()
	}
	console.log(output)
</script>

getSumOf()와 getAverageOf()로 총합과 평균 구하기

위의 세 가지 코드 모두 같은 결과를 도출한다.

 


- 클래스는 대문자로 선언

- 자바스크립트는 생성자 이름을 constructor로 설정

      (자바는 생성자와 클래스의 이름을 동일하게 설정)

<script>
	constructor(매개변수, ...) { 
		this.매개변수=매개변수 
    	... 
	}
</script>

이것은 부생성자!

자바스크립트는 필드가 자동생성되기 때문에, this.매개변수 = 매개변수 에서 매개변수가 필드명이된다.

<script>
	class Student {
		constructor(name, kor, eng, mat, sci) {
			this.name = name
			this.kor = kor
			this.eng = eng
			this.mat = mat
			this.sci = sci
		}
		
		getSum() {
			return this.kor + this.eng + this.mat + this.sci
		}
		
		getAverage() {
			return this.getSum() / 4
		}
		
		toString() {
			return `${this.name}\t${this.getSum()}점\t${this.getAverage()}점\n`
		}
	}
</script>

 

- 인스턴스는 클래스를 기반으로 생성한 객체

- 생성자는 클래스를 기반으로 인스턴스를 생성할 때 처음 호출되는 메소드

 


상속(inheritance)

상속은 클래스의 선언 코드를 중복해서 작성하지 않도록 하는 것!

<script>
	class 클래스_이름 extends 부모클래스_이름 {
	}
</script>

 

Rectangle(부모), Square(자식) 예제

<script>
	class Rectangle {
		constructor (width, height) {
			this.width = width
			this.height = height
		}
		
		getPerimeter() {
			return 2 * (this.width + this.height)
		}
		
		getArea() {
			return this.width * this.height
		}
	}
	
	class Square extends Rectangle {
		constructor(length) {
			super(length, length)
		}
	}
	
	const square = new Square(10, 20)
	console.log(`정사각형의 둘레 : ${square.getPerimeter()}`)
	console.log(`정사각형의 넓이 : ${square.getArea()}`)
</script>

Square에는 매개변수가 1, Rectangle클래스는 매개변수 2개를 필요로한다.

자바스크립트의 경우, 자식 클래스인 Square의 객체를 생성하여 값을 넣어줄 때, 매개변수를 2가지로 넣으면,

첫번째 매개변수만 인식하여 length 즉, width 와 height을 10과 10(10과 20이 아닌)로 생각하여 정사각형 둘레가 40이 나옴

Rectangle(부모), Square(자식) 예제

 

값이 양수일때 작동하는 정사각형 둘레&넓이 구하기

<script>
	class Square {
		constructor(length) {
			this.length = length
		}
		
		getPerimeter() {
			return 4 * this.length
		}
		
		getArea() {
			return this.length * this.length
		}
	}
	
	while(true) {
		let input = prompt("정사각형의 한 변을 입력하시오.")
		if(input <= 0) {
			alert("정사각형의 한 변은 0보다 큰 양수여야 합니다.")
		} else {
			const square = new Square(input)
			alert(`정사각형의 둘레 : ${square.getPerimeter()} \n정사각형의 넓이 : ${square.getArea()}`)
		}
		const in_confirm = confirm("다시 반복하시겠습니까?")
		if(!in_confirm) {
			break;
		}
	}

값이 양수일때 작동하는 정사각형 둘레&넓이 구하기

 


private속성과 메소드

클래스 사용자가 클래스 속성성(또는 메소드)을 의도하지 않은 방향으로 사용하는 것을 막아 클래스의 안정성을 확보하기 위해 나온 문법

자바스크립트에서는 속성 이름과 메소드이름() 앞에 #을 붙여 private처리를 해준다.

<script>
	#속성_이름
    #메소드_이름()
</script>

this사용할 때에는, this.#length

 

private 속성(# 쓰는 것 ) 예제

<script>
	class Square {
		#length
		
		constructor(length) {
			if(length <= 0) {
				throw "길이는 0보다 커야 합니다."
			}
			this.#length = length
		}
		
		getPerimeter() {
			return 4 * this.#length 
		}
		getArea() {
			return this.#length * this.#length
		}
	}	
	
	const square = new Square(10)
	square.#length = -10
	console.log(`정사각형의 둘레 : ${square.getPerimeter()}`)
	console.log(`정사각형의 넓이 : ${square.getArea()}`)	
</script>

private 속성(# 쓰는 것 ) 예제

코드라인 21 (square.#length = -10)에서 오류!!

private속성은 선언된 클래스 안에서만 사용할 수 있다.

 


getter와 setter

setter : 외부의 값을 받아 필드의 값을 변경하는 것이 목적이다. 유효한 값만 필드로 저자할 수 있다.

getter : 외부로 필드값을 전달하는 것이 목적이다. 필드값을 가공하여 외부로 전달 가능.

 

getter와 setter 예제

<script>
	class Square {
		#length
		
		constructor(length) { this.setLength(length) }
		
		setLength(value) {
			if(value <= 0) {
				throw "길이는 0보다 커야 합니다."
			}
			this.#length = value
		}
		
		getLength(value) { return this.#length }
		getPerimeter() { return 4 * this.#length }
		getArea() {	return this.#length * this.#length }
	}	
	
	const square = new Square(10)
	console.log(`한 변의 길이는 ${square.getLength()}입니다.`)
	
	// 예외발생시키기
	square.setLength(-10)
</script>

getter와 setter 예제

 

set, get 예약어로 만든 메소드는 필드형태로 호출할 수 있다.!!

constructor 생성자함수 안에서 this.length = length(매개변수); 의 this.length는 set length(...)의 length라는 사실!

해당 예제

<script>
	class Square {
		#length
		
		constructor(length) { 
			// this.length에 값을 지정하면, set length(length) 메소드 부분이 호출
			this.length = length
		}
		
		get length() {
			return this.#length
		}
		
		get perimeter() {
			return this.#length * 4
		}
		
		get area() {
			return this.#length * this.#length
		}
		
		set length(length) {
			if(length <= 0) {
				throw "길이는 0보다 커야 합니다."
			}
			this.#length = length
		}
	}	
	
	// 속성을 사용하는 형태로 사용한다면, 자동으로 getter와 setter가 호출
	const square = new Square(10)
	console.log(`한 변의 길이는 ${square.length}입니다.`)
	console.log(`둘레는 ${square.perimeter}입니다.`)
	console.log(`넓이는 ${square.area}입니다.`)
	
	// 예외발생시키기
	const squareE = new Square(-10)
</script>

set, get 예약어  메소드 필드형태로 호출

주석을 유의해서 보기!

 


static 속성과 메소드

객체 생성 없이 사용

<script>
	class Square {
		#length
		static #counter = 0
		static get counter() {
			return Square.#counter
		}
		
		constructor(length) {
			this.length = length	// 여기서 this.length는 set length(..)
			Square.#counter += 1	// 여기서 #counter는 static #counter
		}
		
		static perimeterOf(length) { return length * 4 }	
		static areaOf(length) { return length * length }
		
		get length() { return this.#length }
		get perimeter() { return this.#length * 4 }
		get area() { return this.#length * this.#length }
		
		set length(length) {
			if(length <= 0) {
				throw "길이는 0보다 큰 양수여야 합니다."
			}
			this.#length = length
		}
	}
	// static 속성 사용
	const squareA = new Square(10)
	const squareB = new Square(20)
	const squareC = new Square(30)
	console.log(`지금까지 생성된 인스턴스는 ${Square.counter}개 입니다.`)		// 여기서 Square.counter는 static get counter() {...}
	// static 메소드 사용
	console.log(`한 변의 길이가 20인 정사각형의 둘레는 ${Square.perimeterOf(20)}`)
	console.log(`한 변의 길이가 30인 정사각형의 넓이는 ${Square.areaOf(30)}`)
</script>

static 예제

set, get 예약어로 만든 메소드는 필드형태로 호출할 수 있다.

static은 객체 생성없이 사용할 수 있다. 따라서 바로 클래스명.static속성명 으로 get 메소드 호출 가능!

 


오버라이드 (오버라이딩)

자바는 annotation(@)을 사용했다. 자바스크립트는 안씀!

부모 클래스의 모든 필드, 메소드를 가져와 사용

메소드 재정의 방법 : 부모 메소드와 동일한 시그니처(동일한 선언, 실행문만 변경 가능)

super : 상위 생성자 호출.. (super.메소드 : 상위 메소드 호출)

 

참고 (오버로딩 : 적재시키다. 하나의 함수에 여러개의 매개변수를 넣어서 다양하게 사용하는 것)

 

오버라이드 예제1(메소드체이닝)

<script>
	class LifeCycle {
		call() {
			this.a()
			this.b()
			this.c()
		}
		
		a() { console.log("a()메소드를 호출")}
		b() { console.log("b()메소드를 호출")}
		c() { console.log("c()메소드를 호출")}
	}
	
	class Child extends LifeCycle {	// 오버라이드
		a() { console.log("자식의 a()메소드")}
	}
	
	// 인스턴스 생성
	new LifeCycle().call()
	console.log("")
	new Child().call()
</script>

오버라이드 예제1(메소드체이닝)

상속과 관련

부모 메소드와 자식 메소드가 동일한명이라면 나는 자식이므로 자식쪽 메소드가 호출된다.

 

오버라이드 예제2(super)

<script>
	class LifeCycle {
		call() {
			this.a()
			this.b()
			this.c()
		}
		
		a() { console.log("a()메소드를 호출")}
		b() { console.log("b()메소드를 호출")}
		c() { console.log("c()메소드를 호출")}
	}
	
	class Child extends LifeCycle {	// 오버라이드
		a() { 
			super.a()
			console.log("자식의 a()메소드")
		}
	}
	
	// 인스턴스 생성
	new Child().call()
</script>

오버라이드 예제2(super)

부모 메소드를 호출하고 싶다면, 자식 메소드 안에 super예약어 사용하여 부모 메소드 호출 가능

 

** alert()나 consol.log() 등에 변수 넣을 때 사실은 .toString()이 생략되는 것이다.

 


- 상속 : 어떤 클래스가 가지고 있는 속성과 메소드를 기반으로 새로운 클래스를 창조하는 것

- private 속성, 메소드 : 클래스 내부에서만 접근 가능

- getter : get__() 형태. 값을 확인하는 기능의 메소드

- setter : set__() 형태. 값을 지정하는 기능의 메소드

- 오버라이드는 부모가 가지고 있는 메소드와 같은 이름으로 메소드를 재정의 하는 것.

 

 

 

 

 

 

 

 

 

출처 : 혼자 공부하는 자바스크립트 (윤인성)

 

댓글