javascript - 객체 지향

JM·2022년 3월 30일
post-thumbnail

그냥 다 처음 살아보는 인생이라서 서툰건데, 그래서 안쓰러운건데, 그래서 실수 좀 해도 되는데

오늘은 5시 40분에 기상하고 6시에 공부를 시작했다! 그래도 일어나는 시간이 5로 변하고 있어 행복하다.
항상 조금씩 초조해하지 말고 앞으로 나아가자!


클래스 기반의 객체지향 언어 - java
프로토타입 기반의 객체지향 언어 - javascript

객체와 클래스

  • 객체간의 상호작용을 중심으로 하는 프로그램
  • 프로퍼티와 메소드로 이루어진 각 객체들의 상호작용을 중심으로 코드를 작성하는 것
  • 절차지향 프로그래밍: 변수와 함수를 가지고 작업의 순서에 맞게 코드를 작성하는 것

객체

  • 객체의 '상태'를 나타내는 '변수'
  • 객체의 '행동'을 나타내는 '함수'
    유저: 유저의 아이디, 유저의 생일 / 좋아요, 상품 구매
// 예를 들어 작성하기
// Object Literal
const user = {
  	// 프로퍼티 
    email: 'jongmin@naver.com',
    birthdate: '1992-03-21',
  	// 메소드
    // this 는 user 를 나타낸다.
    buy(item){
        console.log(`${this.email} buys ${item.name}`);
    },
}

const item = {
    name: '스웨터',
    price: 30000,
}

console.log(user.email);
console.log(user.birthdate);
user.buy(item)

유저를 한 명 더 추가해본다.
코드가 많이 길어지는 것을 확인 할 수 있다.

const user01 = {
    email: 'jongmin@naver.com',
    birthdate: '1992-03-21',
    buy(item){
        console.log(`${this.email} buys ${item.name}`);
    },
}

const user02 = {
    email: 'jjmin@naver.com',
    birthdate: '1995-02-21',
    buy(item){
        console.log(`${this.email} buys ${item.name}`);
    },
}

console.log(user01.email);
console.log(user01.birthdate);
user01.buy(item)

Factory Function

// Factory Function
function createUser(email, birthdate){
    const user = {
        email: email,		// email
        birthdate: birthdate,	//  birthdat
        buy(item){
            console.log(`${this.email} buys ${item.name}`);
        },
    }
    return user;
}

const user1 = createUser('chris098@google.com', '1996-07-15');
const user2 = createUser('jerry@google.com', '1991-08-29');

console.log(user1);
console.log(user2);
user1.buy(item);
user2.buy(item);

Constructor FUnction(생성자 함수)

  • this 는 해당 객체를 나타낸다.
  • new 를 사용해서 생성해야한다.
  • 함수 이름 앞에 대문자를 사용해줘야 한다.(관습)
function User(email, birthdate){
    this.email = email;
    this.birthdate =birthdate;
    this.buy = function (item) {
        console.log(`${this.email} buy ${this.name}`)
    }
}

const item = {
    name: '스웨터',
    price: 10000,
}

const user1 = new User('chris098@google.com', '1990-08-19');

console.log(user1.email);
console.log(user1.birthdate);
user1.buy(item);

class

  • es2016 에서 생성 되었다.
class ClassUser {
  	// 프로퍼티
    constructor(email, birthdate){
        this.email = email;
        this.birthdate =birthdate;
    }
	// 메소드
    buy(item){
        console.log(`${this.email} buy ${item.name}`)
    }
}

const user2 = new ClassUser('chris098@google.com', '1990-08-19');

console.log(user2.email);
console.log(user2.birthdate);
user2.buy(item);

객체지향 특징

추상화

  • 어떤 구체적인 존재를 원하는 방향으로 간략화해서 나타내는 것

캡슐화

  • 객체의 특정 프로퍼티에 직접 접근하지 못하도록 막는 것
  • JavaScript에서도 다른 방식으로 우회해서 완벽한 캡슐화를 할 수 있다. 클로저(Closure)라고 하는 개념을 응용해서 적용하면 된다.
class ClassUser {
    constructor(email, birthdate){
        this.email = email;
        this.birthdate =birthdate;
    }

    buy(item){
        console.log(`${this.email} buy ${item.name}`)
    }

    // setter 메소드
    set email(address){
        if (address.includes('@')){
            this._email = address;
        } else {
            throw new Error('invalid email address');
        }
    }

    // getter 메소드
    get email() {
        return this._email;
    }
}

아래 코드를 보면 함수 외부에서 user1.email 로 _email 변수를 읽어오고 있다.
createUser 는 user1 객체를 를 생성하고 종료 되었지만 리턴된 객체에서 내부 변수(_email)를 읽어 올 수 있다.
이를 가능하게 하는 것은 자바스크립트의 클로저(Closure) 라고 하는 것 때문에 가능하다.
getter/setter 메소드를 갖고 있는 객체가 리턴된 이후더라도 여전히 내부 메소드들로 부모 메소드에 접근을 할 수 있게한다.(함수가 정의된 당시에 참조할 수 있었던 변수들을 계속 참조할 수 있는 상태의 함수를 클로저라고 한다.)

클로저란 자바스크립트에서 어떤 함수와 그 함수가 참조할 수 있는 값들로 이루어진 환경을 하나로 묶는 것을 의미한다.

function createUser(email, birthdate) {
    let _email = email;
  
    const user = {
      birthdate,
  
      get email() {
        return _email;
      },
  
      set email(address) {
        if (address.includes('@')) {
          _email = address;
        } else {
          throw new Error('invalid email address');
        }
      },
    };
  
    return user;
  }
  
  const user1 = createUser('chris123@google.com', '19920321');
  console.log(user1.email);

상속

  • 클래스의 공통된 부분은 상속을 통해 한 번만 작성을 할 수 있다.
  • User 를 부모클래스, PremiumUser 를 자식클래스 라고 한다.
  • 코드의 재사용성이 좋아진다.

아래와 같이 작성하면 에러가 발생한다.

ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

여기서 derived class 는 자식 클래스를 말한다.
자식클래스에서 super constructor 를 호출해야된다고 한다.

즉, 자식 클래스 생성자 함수 안에서 부모 클래스 생성자를 super 로 반드시 호출해야한다.

class User {
    constructor(email, birthday){
        this.email = email;
        this.birthday = birthday;
    }

    buy(item){
        console.log(`${this.email} buy ${item.name}`);
    }
}

class PremiumUser extends User {
    constructor(email, birthday, level){
        this.level = level;
    }

    streamMusicForFree(){
        console.log(`Free music streaming for ${this.email}`);
    }
}

const item = {
    name: '스웨터',
    price: 10000,
}

const pUser1 = new PremiumUser('test@gmail.com', '1995-02-06');
console.log(pUser1.email);
console.log(pUser1.birthday);
console.log(pUser1.level);
class User {
    constructor(email, birthday){
        this.email = email;
        this.birthday = birthday;
    }

    buy(item){
        console.log(`${this.email} buy ${item.name}`);
    }
}

class PremiumUser extends User {
    constructor(email, birthday, level){
        super(email, birthday);
      	this.level = level;
    }

    streamMusicForFree(){
        console.log(`Free music streaming for ${this.email}`);
    }
}

const item = {
    name: '스웨터',
    price: 10000,
}

const pUser1 = new PremiumUser('test@gmail.com', '1995-02-06');
console.log(pUser1.email);
console.log(pUser1.birthday);
console.log(pUser1.level);

다형성

  • 부모 메소드와 자식 메소드 의 이름은 같지만 내용은 다르게 작성할 수 있다.(오버라이딩)
class User {
    constructor(email, birthday){
        this.email = email;
        this.birthday = birthday;
    }

    buy(item){
        console.log(`${this.email} buy ${item.name}`);
    }
}

class PremiumUser extends User {
    constructor(email, birthday, level){
        super(email, birthday);
        this.level = level;
    }

    streamMusicForFree(){
        console.log(`Free music streaming for ${this.email}`);
    }
	
  	// 오버라이딩(overrindg)
    buy(item){
        console.log(`${this.email} buy ${item.name} with a 5 % discount`);
    }
}

const item = {
    name: '스웨터',
    price: 10000,
}

const User1 = new User('userTest@gmail.com', '1992-02-06', 1);
const pUser1 = new PremiumUser('test@gmail.com', '1995-02-06', 3);

const users = [User1, pUser1];

users.forEach((user) => {
    user.buy(item);
})

// User1.buy(item);
// pUser1.buy(item);

부모 클래스의 메소드가 필요하면?
super를 사용해서 호출하면 된다.

class User {
    constructor(email, birthday){
        this.email = email;
        this.birthday = birthday;
    }

    buy(item){
        console.log(`${this.email} buy ${item.name}`);
    }
}

class PremiumUser extends User {
    constructor(email, birthday, level, point){
        super(email, birthday);
        this.level = level;
        this.point = point;
    }

    streamMusicForFree(){
        console.log(`Free music streaming for ${this.email}`);
    }

    buy(item){
        // console.log(`${this.email} buy ${item.name}`);
        // 부모 클래스의 메소드를 그대로 사용하고 싶을 때도 super 를 호출할 수 있다.
        super.buy(item);
        this.point += item.price * 0.05;
    }
}

const item = {
    name: '스웨터',
    price: 10000,
}

const User1 = new User('userTest@gmail.com', '1992-02-06', 1);
const pUser1 = new PremiumUser('test@gmail.com', '1995-02-06', 3);

const users = [User1, pUser1];

users.forEach((user) => {
    user.buy(item);
})


users.forEach((user) => {
	console.log(user instanceof PremiumUser);
}

// 부모 객체를 instanceof 하면 자식 객체에서도 true 가 나온다.
users.forEach((user) => {
	console.log(user instanceof User);
}

static 프로퍼티와 static 메소드

  • 클래스에 직접적으로 딸려있는 프로퍼티와 메소드
  • 객체가 아닌 클래스 자체로 접근!
class Math {
	static PI = 3.14;
	
	static getCircleArea(radius){
    	return Math.PI * radius * radius;
    }
}
profile
초조해하지 말자! 나는 충분히 할 수 있다! 인생은 길다!

0개의 댓글