[JavaScript 17] 클래스의 기본 기능

김헤일리·2022년 12월 4일
0

JavaScript

목록 보기
18/20
  • 클래스는 객체를 효율적이고 안전하게 만들기 위해서 만들어진 문법이다.
  • JavaScript는 "객체 지향 (object oriented)" 이라는 패러다임을 기반으로 만들어진 프로그램 언어이다.
  • 객체 지향 프로그래밍 언어는 클래스 (class)라는 문법으로 객체를 효율적이고 안전하게 만들어 패러다임을 프로그래밍에 적용할 수 있도록 돕는다.
    • 객체는 속성과 매소드를 갖는다.
    • 클래스는 객체의 틀과 같다.
    • 객체 지향 프로그래밍은 객체를 만들고 객체들의 상호작용을 중심으로 개발하는 방법론이다.

1. 객체에 대한 것

1. 추상화

  • 객체는 많은 속성을 가지고 있고, 그 속성들이 모두 데이터가 될 수 있다.

  • "추상화 (abstraction)" 는 프로그램에 필요한 요소만 사용해서 객체를 표현하는 것을 의미한다.

    • 복잡한 자료, 모듈 시스템 등으로부터 핵심적인 개념과 기능을 간추려낸다.

2. 같은 형태의 객체 만들기

  • 예를 들어, 학생 성적 관리 프로그램을 만든다고 했을 때:
    • 학생이라는 객체가 필요하고
    • 객체들 (학생들) 로부터 성적 관리에 필요한 공통사항을 추출하는 것을 추상화라고 한다.
    • 객체가 여려 개(명) 일 경우, 추출한 요소는 배열을 이용해 관리한다.
    • 예시:
<script>
    const students = []; 
	// 1. students 라는 빈 배열을 두고, 
    students.push({ 이름: "구름", 국어: 87, 영어: 98, 수학: 88, 과학: 90 });
    students.push({ 이름: "별이", 국어: 92, 영어: 98, 수학: 96, 과학: 88 });
    students.push({ 이름: "겨울", 국어: 76, 영어: 96, 수학: 94, 과학: 86 });
    students.push({ 이름: "바다", 국어: 98, 영어: 52, 수학: 98, 과학: 92 });
	// 2. 배열 안에 필요한 객체를 .push()를 이용해 추가한다.

    console.log(JSON.stringify(students, null, 2));
	// 3. 배열에 추가한 객체를 JSON 형식으로 콘솔에 표시

    let output = "이름 \t 총점 \t 평균 \n";
	// 4. ouput 이라는 변수에 이름, 총점, 평균에 각각 탭을 두고, 평균 이후에 줄바꿈을 추가한다.

    for (const s of students) {
      // 5. students 내부에 있는 요소 중 하나를 s라고 가정하고 반복문을 돌린다.
      // 5-1. 반복문을 돌릴 시, 각 객체에 있는 속성을 전부 돌고 나서 다음 객체의 속성을 돌게된다.
      const sum = s.국어 + s.과학 + s.수학 + s.영어;
      // 6. sum 이라는 변수에 "."이라는 속성 접근자를 이용해서 각 속성별로 접근한 후 전체를 더한 값을 지정한다.

      const average = sum / 4;
      // 7. average 라는 변수에 sum을 4로 나눈 값을 할당한다.

      output += `${s.이름} \t${sum}점 \t${average}점 \n`;
      // 8. output에 객체의 이름 속성과 해당 속성들의 sum과 average를 추가 할당한다.
    }

    console.log(output);
	// 9. 출력 시 각 학생의 이름과 해당 학생의 전체 과목 총 점과 평균을 구할 수 있다.
</script>

2. 객체를 처리하는 함수

  • 만들어진 프로그램에서 확장성을 고려하면, 간단하게 계산하는 것보다 함수를 만들어 놓는것이 유리하다.
    • 객체를 만드는 부분과 객체를 활용하는 부분으로 나눠서 코드를 작성하면, 객체에 더 많은 기능을 추가하게 되었을 때 유지보수가 쉬워지고 객체 활용 시 더 간단한 코드를 작성할 수 있다.

    • 예시: 객체의 속성과 기능을 만드는 부분에 함수 추가하기
<script>
    const students = [];
    students.push({ 이름: "구름", 국어: 87, 영어: 98, 수학: 88, 과학: 90 });
    students.push({ 이름: "별이", 국어: 92, 영어: 98, 수학: 96, 과학: 88 });
    students.push({ 이름: "겨울", 국어: 76, 영어: 96, 수학: 94, 과학: 86 });
    students.push({ 이름: "바다", 국어: 98, 영어: 52, 수학: 98, 과학: 92 });

    console.log(JSON.stringify(students, null, 2));

    const getSumOf = (students) => {
      return students.국어 + students.영어 + students.수학 + students.과학;
    };
	// 1. students 배열에 있는 객체들의 모든 속성들을 접근하도록 "."를 사용해서 속성들의 합을 구하는 함수를 선언한다.
	// 1-1. getSumOf 함수가 배열 students에 접근할 수 있도록 매개변수를 students로 한다.

    const getAverageOf = (students) => {
      return getSumOf(students) / 4;
    };
	// 2. getSumOf(students) 함수의 값을 4로 나눌 수 있는 함수 getAverageOf를 선언한다.
	// 2-1. 이때 getAverageOf 함수도 students에 접근할 수 있도록 매개변수를 students로 준다.

    let output = "이름 \t 총점 \t 평균 \n";

    for (const s of students) {
      output += `${s.이름} \t${getSumOf(s)}점 \t${getAverageOf(s)}점 \n`;
    }
	// 3. 반복문을 돌려 students 배열에 1개의 객체인 s에 접근한다.
	// 3-1. 총합과 평균을 구하는 getSumOf와 getAverageOf 함수 사용 시, students가 아니라 s를 준다.
	// 3-2. 출력 시 특정 객체의 속성값들의 합과 그 평균이 출력된다. (각 학생의 총점과 평균이 구해진다.)

    console.log(output);
</script>
  • 코드 자체는 길어졌지만, 유지보수 측면에서 유리하고, 객체 활용 시에도 간단하게 코드를 작성할 수 있다.

3. 객체의 기능을 메소드로 추가하기

  • 객체의 수가 늘어나면 함수 이름 충돌이 발생할 수 있다.
  • 또한 매개변수에 어떤 종류의 객체를 넣을지 몰라 함수를 사용하는 데 혼동이 있을 수 있다.
  • 함수 이름을 최대한 상세하게 적으면 해결될 수 있는 문제지만, 이름이 길어질 수록 가독성이 떨어질 수 있다.
  • 그래서 대안으로 활용하는 방식이 함수를 메소드로서 객체 내부에 넣어서 활용하는 방식이다.
    • 예시: 학생 성적 관리 코드에 메소드 추가하기
<script>
    const students = [];
    students.push({ 이름: "구름", 국어: 87, 영어: 98, 수학: 88, 과학: 90 });
    students.push({ 이름: "별이", 국어: 92, 영어: 98, 수학: 96, 과학: 88 });
    students.push({ 이름: "겨울", 국어: 76, 영어: 96, 수학: 94, 과학: 86 });
    students.push({ 이름: "바다", 국어: 98, 영어: 52, 수학: 98, 과학: 92 });

    console.log(JSON.stringify(students, null, 2));

    for (const student of students) {
      // 1. students라는 객체가 모여있는 배열에서 특정 객체에 접근할 수 있도록 반복문을 돌린다.
      student.getSum = function () {
      // 2. 각 객체에 접근하여 해당 객체의 속성으로 getSum이라는 함수를 부여한다. 
        return this.국어 + this.영어 + this.수학 + this.과학;
      	// 3. 해당 객체의 다른 속성을 지명하기 위해 this 키워드를 사용해서 해당 객체의 다른 속성을 더하는 내용을 리턴값으로 둔다.
      };

      student.getAverage = function () {
      // 4. 그리고 반복문 내부에 다른 함수를 추가해서, 특정 객체에 getAverage라는 함수 속성으로 부여한다,
        return this.getSum() / 4;
        // 5. gerAverage 속성을 갖고있는 해당 객체를 this로 칭하고, 해당 객체의 다른 속성인 getSum에 접근하여 4로 나누고, 해당 값을 리턴한다.
      };
    }

    let output = "이름 \t 총점 \t 평균 \n";
    for (const s of students) {
    // 6. students라는 객체가 모여진 배열에서 특정 객체 1개에 접근할 수 있는 반복문을 다시 돌린다.
      output += `${s.이름} \t${s.getSum()}점 \t${s.getAverage()}점 \n`;
      // 7. 특정 객체 접근 시, 해당 객체의 속성을 접근할 때 원하는 결과값을 출력할 수 있는 속성만 호출하여 결과를 출력한다.
    }

    console.log(output);
 </script>
  • 이렇게 매소드를 각 객체의 속성으로 부여할 경우, 다른 객체를 가진 배열도 같은 이름의 함수를 갖고 있어도 헷갈리거나 충돌할 위험이 없다.
    • 예시 ) 만약 students라는 배열이 1반 학생의 배열이고, 2반 학생의 배열에 똑같이 총합과 평균을 구하는 함수가 있다면, 함수 이름을 지정하는 것에 겹치지 않기 위해 신경써야 한다. 하지만 메소드를 추가할 경우 그런 문제가 사라지는 것.


  • 객체 생성 시에도 함수를 사용해서 객체를 찍어낼 경우, 더 쉽게 객체를 생성할 수 있다.
    • 예시:
<script>
    function createStudent(이름, 국어, 영어, 수학, 과학) {
  // 1. 객체를 생성하기 위해 함수를 선언한다.
      return {
        이름: 이름,
        국어: 국어,
        영어: 영어,
        수학: 수학,
        과학: 과학,
      // 2. 함수가 리턴하는 값에 객체의 key를 부여하고, 함수의 매개변수를 key의 페어가 되는 값으로 설정한다.

        getSum() {
          return this.국어 + this.영어 + this.수학 + this.과학;
        },
        // 3. 함수에 또 다른 key-value로 메소드를 선언한다.
        // 3-1. 해당 메소드의 값은 함수 createStudent 내부에 있는 또 다른 key-value를 this 키워드로 명시해서 원하는 값을 리턴할 수 있도록 설정한다. 

        getAverage() {
          return this.getSum() / 4;
        },
        // 4. 평균을 구하는 함수도 선언한 후, 해당 함수 (key)가 리턴하는 값 (value)을 해당 함수 내 다른 속성임을 명시하기 위해 this 키워드를 사용한다.
        // 4-1. this 키워드를 이용해서 명시한 후, 해당 속성(getSum())의 값을 4로 나누는 것이 getAverage라는 key의 value가 되는 것

        toString() {
        // 5. 마지막으로 원하는 결과값을 문자열로서 리턴할 수 있는 마지막 함수(동시에 객체의 key)를 선언한다.
          return `${
            this.이름
          } \t ${this.getSum()} \t ${this.getAverage()}점 \n`;
          // 6. 해당 함수가 리턴하는 값 또한 같은 객체 내의 다른 key의 속성이기 때문에 this 키워드를 이용한다.
          // 6-1. 함수 toString()은 같은 객체 내 "이름"의 값과 "getSum()"의 값, "getAverage()"의 값을 문자열로 리턴한다.
        },
      };
    }
// 7. 함수를 이용한 객체 생성 완료.

    const students = [];
	// 8. 빈 배열을 students라는 변수에 넣고, 
    students.push(createStudent("구름", 87, 98, 88, 90));
	// 9. 빈 배열인 students에 함수를 이용해서 객체를 push한다.
	// 9-1. 함수 createStudent()의 매개 변수로 넣은 값들이 key와 pair되는 value.
    students.push(createStudent("별이", 92, 98, 96, 88));
    students.push(createStudent("겨울", 76, 96, 96, 86));
    students.push(createStudent("바다", 98, 52, 98, 92));

    console.log(JSON.stringify(students, null, 2));

    let output = "이름 \t 총점 \t 평균 \n";

    for (const s of students) {
      output += s.toString();
    }
	// 10. 반복문을 돌려서 students라는 배열 내부에 있는 각각의 객체 "s"에 접근하고,
	// 10-1. output에 각 객체가 갖고 있는 값 중 하나인 toString()을 "."을 이용해서 접근하고 호출한다. 

    console.log(output);
</script>
  • 이렇게 함수를 이용해서 객체를 생성하고 리턴하게 만들면:
    • 오탈자의 위험이 줄어든다.
    • 코드를 입력하는 양이 크게 줄어든다.
    • 속성과 메소드를 한 함수 내부에서 관리할 수 있으므로 객체를 더 손쉽게 유지보수 할 수 있다.
  • 하지만 단점도 존재하는데, 객체별로 getSum(), getAverage(), toString() 메소드를 생성하기 때문에 함수라는 기본 자료형보다 무거운 자료형이 여러 번 생성된다.

2. 클래스 선언하기

  • 객체들을 정의하고 그런 객체를 활용해서 프로그램을 만드는 것을 객페 지향 프로그래밍이라고 한다.
  • 프로그래밍 언어 개발자들은 크게 "클래스 (class)"프로토타입 (prototype)"이라는 2가지 문법을 객체를 효율적으로 만들 수 있도록 고안했다.

1. 클래스 (class)와 인스턴스 (instance)

  • 클래스는 객체를 만드는 함수와 비슷한 것을 의미한다.

    • 생성할 시 기본 형태: class 클래스 이름 ()
    • 클래스의 이름은 첫 글자로 대문자로 지정하는 것이 보편적인 규칙이다. (버그가 발생하진 않음)

  • 인스턴스는 클래스를 기반으로 만든 객체를 의미하지만, 보통 그냥 객체라고 부른다.

    • 생성할 시 기본 형태: new 클래스 이름 ()
  • 예시: 클래스 선언하고 인스턴스 생성하기

<script>
    class Student {}
	// 1. 클래스 선언

    const student = new Student();
	// 2. student라는 변수에 인스턴스를 할당한다.

    const students = [
      new Student(),
      new Student(),
      new Student(),
      new Student(),
    ];
	// 3. students라는 변수에 여러개의 인스턴트를 할당하기 위해 배열로 감쌌다.
</script>

2. 생성자 함수 이해하기

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

  • 자바스크립트 함수는 재사용 가능한 코드를 묶음으로 사용하는 것 외에 객체를 생성 하기 위한 방법으로도 사용된다.

    • 생성자와 new 키워드는 객체의 구조를 재활용하기 위해서 나온 개념이다.
  • 객체를 생성하기 위해서는 직접 객체를 반환해도 되지만, new 키워드를 사용하여 함수를 호출하게되면 return 문이 없어도 새로운 객체를 반환 시킨다.

    • 그리고 함수 에서 this를 사용하여 반환되는 객체의 초기 상태와 행위를 정의할 수 있다.
    • 이렇게 객체를 생성하는 역할을 하는 함수를 생성자 함수라고 한다.
  • 생성자 함수는 new 키워드를 사용하지 않으면 일반적인 함수와 동일하게 동작하여 새로운 객체를 반환하지 않기 때문에 함수명을 대문자로 시작한다.

    • 생성자의 기본 형태:
      class 클래스 이름 {
         constructor () {
              //생성자 코드가 들어간다//
         }
      }
    • 생성자에서는 속성을 추가하는 등 객체의 초기화 처리를 한다.
    • 예시:
<script>
    class Student {
      constructor(이름, 국어, 영어, 수학, 과학) { 
      // 생성자 코드를 이용해서 유사 객체 생성
        this.이름 = 이름;
        this.국어 = 국어;
        this.영어 = 영어;
        this.수학 = 수학;
        this.과학 = 과학;
      }
    }

    const students = [];
    students.push(new Student("구름", 87, 98, 88, 90)); 
	// 호출 할 땐 New 키워드를 사용한다.
    students.push(new Student("별이", 92, 98, 96, 88));
    students.push(new Student("겨울", 76, 96, 94, 86));
    students.push(new Student("바다", 98, 52, 98, 92));
</script>
  • 또 다른 예시:
function Teacher(name, age, subject) { 
//Teacher 생성자 함수를 정의. 매개변수(parameter)를 name,age,subject를 정의하고 teach에 method 를 정의하였다.
  this.name = name
  this.age = age
  this.subject = subject
  this.teach = function (student) {
    console.log(`${student}에게 ${this.subject}를 가르칩니다`)
  }
  // teach method 실행 시 콘솔에 글이 출력된다.
}
const jay = new Teacher("jay", 30, "javascript") 
// new 키워드와 함께 생성자 함수를 호출 하여 생성자 함수 블록이 실행되어 return 없이도 새로운 객체가 반환되었음.
// 새로운 객체에서 this 가 가르키는 것이 jay 변수이다

console.log(jay)
// 출력 시 name: "jay", age:30, subject: "javascript"가 출력되고, teach는 함(function)으로 남아있는다.

jay.teach("fran")
// jay.teach(fran)으로 객체에 teach 메소드를 호출하면 "fran 에게 javascript를 가르칩니다" 라는 문구가 출력된다. 

console.log(jay.constructor)
// 모든 객체에는 constructor 속성을 가진다. 
// jay객체의 속성은 Teacher 의 속성을 가지고 있는것이 확인되었다. 
// jay.constructor 를 하니 function teacher 가 나온것을 확인.

console.log(jay instanceof Teacher)
// instanceof 연산자를 이용하여 jay 객체가 Teacher 생성자 함수의 인스턴스 여부를 확인했더니 true가 출력된다.

const jay2 = Teacher("jay2", 32, "Java")
// new 키워드를 사용하지 않을 경우, 바로 Teacher라는 생성자 함수를 호출할 경우, 초기화가 진행되지 않아서 첫번째 인자로 받을 새 객체가 확인되지 않는다.
console.log(jay2)
// 출력 시 새로운 객체가 확인되지 않아 undefined가 출력된다.
console.log(age)
// 두번째 인자에 age는 할당이 되어 32가 출력된다.
// jay2의 자료형은 undefined다.
  • 결론: new 키워드를 붙여야만 객체를 생성할 수 있다.

3. 메소드

  • 만약 프로토타입으로 메서드를 만들어주지 않는 경우에는 인스턴스가 생성될 때마다 계속해서 메서드를 생성해줘야 한다.
  • 클래스를 통해 더 간결하게 프로토타입 메서드를 생성하는 것이 가능하다.
<script>
    class Student {
      constructor(이름, 국어, 영어, 수학, 과학) {
        this.이름 = 이름;
        this.국어 = 국어;
        this.영어 = 영어;
        this.수학 = 수학;
        this.과학 = 과학;
      }
      getSum() {
        return this.국어 + this.영어 + this.수학 + this.과학;
      }
      getAverage() {
        return this.getSum() / 4;
      }
      toString() {
        return `${
          this.이름
        } \t ${this.getSum()}점 \t ${this.getAverage()}점 \n`;
      }
    }

    const students = [];
    students.push(new Student("구름", 87, 98, 88, 90));
    students.push(new Student("별이", 92, 98, 96, 88));
    students.push(new Student("겨울", 76, 96, 94, 86));
    students.push(new Student("바다", 98, 52, 98, 92));

    let output = "이름 \t 총점 \t 평균 \n";

    for (const s of students) {
      output += s.toString();
    }

    console.log(output);
</script>
  • 메소드 사이에 쉼표를 넣지 않으면 내부적으로 메소드가 중복되지 않고 하나만 생성되어 활용된다.


객체는 언제나 헷갈리는 개념인 것 같다...
자바스크립트가 돌아가는 구조에 대해서 잘 익히지 않으면 안되겠다.

출처:

profile
공부하느라 녹는 중... 밖에 안 나가서 버섯 피는 중... 🍄

0개의 댓글