Javascript class 전역변수 - javascript class jeon-yeogbyeonsu

클래스

ES6 버전부터 신규로 추가된 객체 설계 문법. Prototype이 발전된 형태로 보면 된다.

객체 (Object)

  • 사전적 의미 : 어떠한 물건이나 대상
  • 프로그래밍에서의 의미 : 프로그램에서 표현하고자 하는 기능을 묶기 위한 단위

객체를 구성하는 단위

  • 객체를 이루는 것은 데이터기능이다.
  • 데이터는 변수로 표현된다.
    • 객체 안에 포함된 변수를 멤버변수 혹은 프로퍼티라 한다.
  • 기능은 메서드(=함수)로 표현된다.

클래스 (Class)

객체의 설계도 역할을 하는 프로그램 소스

공장에서 하나의 설계도를 사용하여 여러 개의 제품을 생산할 수 있는 것처럼 하나의 클래스를 통해 동일한 구조를 갖는 객체를 여러 개 생성할 수 있다.

1) 클래스의 가장 기본적인 코드 형식

클래스 이름은 명사들의 조합으로 이루어지며 첫 글자는 대문자로 지정하는 것이 관례이다.

class 클래스이름{
    // 생성자 --> 멤버변수 선언 및 기타 초기화
    // getter, setter
    // 메서드
}

2) 클래스를 통한 객체 생성하기

new 예약어를 사용한다.

var|let|const 변수이름 = new 클래스이름();

일반적으로 JS에서의 객체 선언은 const 키워드를 사용함.

위와 같이 정의하면 변수는 클래스 안에 정의된 모든 기능을 부여받은 특수한 형태의 변수가 되는데 이를 객체라고 하고, 객체는 자신에게 기능을 점(.)을 통해 접근할 수 있다.

객체.멤버변수 = 값;
객체.메서드();

3) 클래스의 작성 패턴

  1. 변수만 정의
  2. 메서드만 정의
  3. 변수와 메서드를 함께 정의

객체라는 개념은 배열이 같은 종류의 변수들만 그룹화 하는 한계를 벗어나 서로 다른 종류의 변수를 그룹화 하는데서 출발한다. (이 상태를 C언어의 구조체라고 한다.)

그렇게 그룹화 해 놓은 변수들간의 관계를 구현하기 위해 메서드를 함께 포함하는 형태로 발전된 것이다.

변수만 정의한 클래스

생성자 함수를 정의하고 생성자 함수 안에서 this키워드를 통해 객체 안에 탑제될 변수값들을 생성한다.

생성자 함수는 constructor로 이름이 미리 약속되어져 있다.

클래스는 중복 선언될 수 없기 때문에 Jupyter에서 실습할 경우 아래의 코드 블록이 2번 실행되면 에러가 발생하게 된다. (커널 재시작 필요)

class MemberClass1{
    constructor(){
        this.userName = "이름";
        this.email = "이메일";
    }
}

var m1 = new MemberClass1();
console.log(m1);
console.log(m1.userName);
console.log(m1.email);

var m2 = new MemberClass1();
console.log(m2);
console.log(m2.userName);
console.log(m2.email);

m1.userName = "민혁";
m1.email = "";

m1.userName = "철수";
m1.email = "";

console.log(m1);
console.log(m2);
MemberClass1 { userName: '이름', email: '이메일' }
이름
이메일
MemberClass1 { userName: '이름', email: '이메일' }
이름
이메일
MemberClass1 { userName: '철수', email: '' }
MemberClass1 { userName: '이름', email: '이메일' }

JSON 형식의 객체와 차이

class나 prototype을 통해 new로 생성된 객체는 구조는 동일하지만 각각 독립적인 값을 갖는다.

앞의 예제에서 m1과 m2가 내장하는 멤버변수의 값을 각각 민혁과 철수로 변경했을 때 두 개의 객체는 각각 독립적인 값이 저장된 상태로 존재한다.

JSON으로 생성되는 객체는 싱글톤으로서 존재하므로 단순 복사만으로는 동일한 형식을 갖는 두 개의 데이터를 생성할 수 없다.
서로 독립적인 데이터를 보유하려면 동일한 JSON 코드를 한 번 더 작성해야 한다.

var m3 = {
    userName: "이름",
    email:"이메일"
};

var m4 = m3; // 객체의 복사가 아닌 참조가 발생한다.
console.log(m3);
console.log(m4);

//값의 변경
m3.userName = "민수";
m3.email= "";

// 객체간의 복사는 서로 영향을 준다.
console.log(m3);
console.log(m4);
{ userName: '이름', email: '이메일' }
{ userName: '이름', email: '이메일' }
{ userName: '민수', email: '' }
{ userName: '민수', email: '' }

5) 메서드만 정의한 클래스

용도나 목적이 같은 메서드들을 별도의 클래스로 묶어둔다.

class Calc {
    plus(x,y){
    return x+y;
    }
    minus(x,y){
        return x-y;
    }    
}

var c = new Calc();
console.log(c.plus(1,2));
console.log(c.minus(10,5));
3
5

6) 메서드와 멤버변수를 함께 갖는 클래스

멤버변수의 스코프는 클래스 내의 모든 메서드에서 식별 가능하다.

결국 멤버변수는 모든 메서드가 공유하는 전역 변수의 개념이 된다.

class HelloWorld{
    constructor(){
        // 멤버변수 정의 : 선언만 하고 추후 값을 할당할 경우 null을 할당해 둔다.
        this.message = null;
    }

    sayHello() {console.log(this.message);}
    setEng() { this.message = "Hello Javascript";}
    setKor() {this.message =  "안녕하세요.자바스크립트";}
}

const hello = new HelloWorld();

//메서드의 호출

hello.setEng();
hello.sayHello();
hello.setKor();
hello.sayHello();
Hello Javascript
안녕하세요.자바스크립트

7) getter, setter

객체를 통한 멤버변수로의 직접 접근이 소스코드 보안에 좋지 않기 때문에 멤버변수에 대한 직접 접근을 제한하려는 목적으로(혹은 getter, setter 이름과 구분을 위한 목적으로) 멤버변수의 이름을 언더바(_)로 시작하게 지정한다.

그 후에 멤버변수에 대한 간접적인 할당, 반환을 수행하는 메서드를 별도로 만드는 형태를 getter, setter라고 한다.

class UserClass{
    constructor(){
        this._userName = null;
        this._email = null;
    }
    set userName(value){
        if(!value){
            console.log("userName을 입력하세요.");
            return;
        }
        this._userName = value;
    }

    get userName(){
        return this._userName;
    }

    get email(){
        return this._email;
    }

    set email(value){
        if(!value){
            console.log("email을 입력하세요.");
            return ;
        }
        this._email = value;
    }

    login() {
        if(!this.userName || !this.email){
            console.log("userName이나 email을 확인하세요.");
            return;
        }
        //같은 클래스의 getter를 통해 멤버변수 값을 반환받아 출력에 활용한다.
        console.log("로그인되었습니다. >> userName=%s, email=%s", this.userName, this.email);
    }
}

const user = new UserClass();
user.login();

user.userName = "";
user.email = "";

user.userName = "helloworld";
user.email = "";
user.login();

//상속은 된다. 부모가 누군지 알 수 없어서 다형성 없다.
userName이나 email을 확인하세요.
userName을 입력하세요.
email을 입력하세요.
로그인되었습니다. >> userName=helloworld, email=

#02. 클래스 상속

어떤 클래스의 기능을 다른 클래스에 상속시킨 후 추가적인 기능을 명시하여 원래의 기능을 확장하는 방법
class를 정의할 때 클래스 이름 뒤에 extends 키워드를 명시하고 상속받고자 하는 부모 클래스의 이름을 지정한다.

1) 기능의 확장으로서의 상속

/** 기본 기능을 갖는 클래스 */
class SayHello {
    eng() {
        console.log("Hello Javascript");
    }
}

/** 기본 기능을 확장하는 클래스 */
// 부모의 모든 기능을 상속 받으며, 추가적으로 자신이 구현하는 기능도 사용할 수 있다.
class SayHelloWorld extends SayHello {
    kor() {
        console.log("안녕하세요 자바스크립트");        
    }    
}

const say = new SayHelloWorld();
say.eng(); // 부모에게 상속받는 기능 사용하기
say.kor(); // 자신이 확장한 기능 사용하기
Hello Javascript
안녕하세요 자바스크립트

2) 여러 클래스간의 공통 기능을 모아 놓는 의미로서의 상속

여러 개의 클래스가 포함하는 기능 중 일부가 동일한 경우 각 클래스로부터 공통되는 부분을 독립적인 클래스로 추출하고 그 클래스를 상속하여 공유하는 처리 기법

공통기능을 정의하는 부모 클래스

class Protoss{
    /** 모든 객체가 갖는 명사적 특성들을 멤버변수로 정의 */
    constructor(name, hp, dps){
        this._name = name; // 이름
        this._hp = hp; // 체력(health point)
        this._dps = dps; //초당공격력(damage per Second)
        console.log("[%s] 체력 : %d, 공격력 : %d", name, hp, dps);        
    }

    /** 객체가 수행해야 하는 동작들을 함수 형태로 정의 */
    move(position){
        console.log("%s(이)가 %s까지 이동합니다.",  this._name, position);
    }

    attack(target){
        console.log("%s(이)가 %s(을)를 공격합니다. 데미지: %d", this._name, target, this._dps);
    }
}

부모를 상속 받는 자식 클래스(들) 정의

class Zealot extends Protoss {
    sword(target){
        this.attack(target);
        console.log(" >>>>>>>> 검으로 찌르기");        
    }
}

class Dragoon extends Protoss {
    fire(target) {
        this.attack(target);
        console.log(" >>>>>>> 원거리 공격");
    }
}

자식 클래스에 대한 객체 생성

부모가 생성자 파라미터를 통해 초기화를 수행하고 있다면 그 생성자는 자식 클래스에게도 상속된다.

그러므로 자식 클래스를 통한 객체 생성시에도 부모가 요구하는 생성자 파라미터를 전달해야 한다.

var z1 = new Zealot("질럿1", 300, 20);
z1.move("본진");
z1.sword("본진");

var z2 = new Zealot("질럿2", 300, 25);
z2.move("멀티");
z2.sword("멀티");

var d1 = new Dragoon("드라군1", 250, 40);
d1.move("본진");
d1.fire("본진");

var d2 = new Dragoon("드라군2", 200, 35);
d2.move("멀티");
d2.fire("멀티");
[질럿1] 체력 : 300, 공격력 : 20
질럿1(이)가 본진까지 이동합니다.
질럿1(이)가 본진(을)를 공격합니다. 데미지: 20
 >>>>>>>> 검으로 찌르기
[질럿2] 체력 : 300, 공격력 : 25
질럿2(이)가 멀티까지 이동합니다.
질럿2(이)가 멀티(을)를 공격합니다. 데미지: 25
 >>>>>>>> 검으로 찌르기
[드라군1] 체력 : 250, 공격력 : 40
드라군1(이)가 본진까지 이동합니다.
드라군1(이)가 본진(을)를 공격합니다. 데미지: 40
 >>>>>>> 원거리 공격
[드라군2] 체력 : 200, 공격력 : 35
드라군2(이)가 멀티까지 이동합니다.
드라군2(이)가 멀티(을)를 공격합니다. 데미지: 35
 >>>>>>> 원거리 공격