22-10-25-JavaScript(12) 객체지향 프로그래밍

YJ·2022년 10월 25일
0
post-thumbnail

객체지향 프로그래밍

객체와 객체의 상호작용

객체란?
자바스크립트의 객체 : 데이터의 묶음
객체 지향의 객체 : 우리가 표현하고자 하는 사물을 추상적으로 표현한 것

// 나를 객체로 표현
const me = {
  // 프로퍼티 (상태)
	age : 11,
  	name : "lee",
  	address : "서울",
  // 메소드 (행동)
  	canWalk : function() {
    	console.log("walking");
    },
  	learning : function(teacher) {
    	console.log("hello");
      	teacher.feelingUp();
    }
}
const teacher = {
	feeling : 99,
  	feelingUp : function() {
    	this.feeling++;
    }
}
me.learning(teacher);

객체는 행동, 상태를 가짐
행동-> 메소드
상태 -> 프로퍼티

객체와 객체가 서로 메소드를 통해 상호작용 => 객체지향 프로그래밍

생성자 (constructor)

new 연산자와 함께 사용하여 객체를 생성

const me = new 생성자함수();

생성자를 통해 생성된 객체들은 같은 프로퍼티, 메소드를 공유할 수 있다.

  • 생성자는 함수이므로 함수가 필요
// 함수 이름은 대문자로 시작
function NewFactory() {

}
let robot1 = new NewFactory();

return 필요없이 new 키워드가 붙으면 자동적으로 객체를 생성하고 반환함

반환된 객체 => 인스턴스라고 부름

function NewFactory(name) {
	this.name = name;
  	this.sayYourName = function() {
    	console.log(`제 이름은 ${this.name}입니다.`);
    }
}
let robot1 = new NewFactory('lee');

생성자 함수 내부의 this 는 생성자가 만들어낸 객체 = 인스턴스 를 참조함

  • 랜덤 메뉴 출력
function FoodFactory(foodList) {
    this.food = foodList;
    const pickFood = foodList[Math.floor(Math.random() * foodList.length)] // 0 ~ length 미만
    this.foodName = function() {
        console.log(`랜덤 메뉴는 ${pickFood}입니다.`);
    }
}
const foodList = ['짜장면','짬뽕','탕수육','냉짬뽕','간짜장'];
let robot3 = new FoodFactory(foodList);
robot3.foodName();

프로토타입

문제 )
객체 생산은 쉽지만 매번 새로운 함수를 생성 => 자원의 낭비

해결 )
프로토타입

- 프로토타입
특정 객체에 대한 참조로 어떠한 공간을 가르킴

생성자 함수가 인스턴스를 생성하면 그 안에는 숨겨진 프로퍼티 [[Prototype]] 이 존재
코드 상으로는 __proto__ 로 존재
__proto__는 자신을 만든 생성자 함수의 prototype을 참조

결국, 생성자 함수의 prototype 과 인스턴스의 __proto__ 연결!!

function Test() {}
const test = new Test();
test.__proto__ === Test.prototype; // true

__proto__ 는 오로지 인스턴스에만 존재하며 숨겨진 프로퍼티
prototype 은 오직 function() 안에만 존재하는 참조값

인스턴스는 __proto__를 통해 생성자 함수의 prototype에 접근하여 필요한 값, 메서드 사용 가능

모든 인스턴스가 하나의 메서드를 공유하도록 하여 자원 낭비를 막음
자신의 prototype이 없으면 계속 상위로 올라가서 찾는다.
프로토타입 체이닝!!!
function NewFactory2(name) {
	this.name = name;
}
NewFactory2.prototype.sayYourName = function() {
	console.log(`제 이름은 ${this.name}입니다.`);
}

const robot4 = new NewFactory2('Lee');
const robot5 = new NewFactory2('Kim');

prototype 공간을 하나 가리킴
모든 인스턴스들은 이 공간을 참조하며 이 안에 존재하는 sayYourName()을 사용 가능
prototype를 통해 메모리 아끼며 모든 메소드 등을 공유 가능

객체의 상속

자바스크립트의 상속은 기본적으로 prototype를 통해 일어난다.

const obj = {
	name : 'lee'
}
const arr = [1,2,3]
console.log(arr.hasOwnProperty('name'));

false가 나오는 이유
Array 함수의 __proto__가 Object 함수의 prototype를 참조하고 있기 때문에 Array에서 존재하지 않지만 Object 객체의 프로퍼티와 메서드를 사용할 수 있다.

자기 자신에게 존재하지 않는 프로퍼티나 메소드를 프로토타입을 통해 추적하는 과정을 프로토타입 체이닝

const str = new String('12345');
str.__proto__

str.__proto__ === String.prototype
생성자 함수 프로토타입

str.__proto__.__proto__ === Object.prototype

자바스크립트의 타입들은 기본적으로 Object 타입을 상속 받는다.

  • 상속받기
function Parent() {
	this.name = 'lee'
}
Parent.prototype.rename = function(name) {
	this.name = name;
}
Parent.prototype.sayName = function() {
	console.log(`제 이름은 ${this.name}입니다.`);
}

function Child() {
	Parent.call(this); // Parent()의 프로퍼티 사용
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.canWalk = function() {
	console.log('i can walk');
}
                
const child = new Child();
child.name; // 'lee'
child.rename('kim'); // child.rename에 kim 할당
child.sayName(); // 제 이름은 kim입니다.

Object는 최상위 객체로 아무것도 참조하지 않는다.

Parent.call()에서 call 함수는 Child 함수의 this 가 Parent 생성자 함수의 this를 바라보게 만든다.
즉, Child 통해 생성된 인스턴스 this가 Parent 함수 안의 프로퍼티에 접근 가능

Object.create 함수는 주어진 인자를 Child.prototype 에 연결하는 역할
=> Parent 객체의 프로토타입을 Child 객체의 프로토타입이 참조함

==> Child 객체는 Parent 객체의 모든 것을 상속받게 된다.

1. Parent.call(this);
2. Child.prototype = Object.create(Parent.prototype);

클래스 class

ES6 부터 class 키워드 사용 가능!!
어떠한 사물들의 공통적인 속성을 추상화한 객체

클래스 -> 붕어빵 틀 / 자동차 공장
인스턴스 -> 여러 종류의 붕어빵 / 현0 자동차, 기0 자동차

상위 -> 하위 클래스로 갈수록 상위 클래스를 상속받으면서 더 구체적인 요건이 추가되거나 변경

사용법

class 이름 {}
class Robot {
  // 클래스의 생성자 함수
  // 하나의 클래스는 하나의 생성자만 정의 가능
  // 생성자 함수는 new 키워드를 통해 자동 실행
	constructor(name) {
    	this.name = name;
    }
  
  // 메소드 정의 (function 키워드 사용 안함)
  // 메소드는 클래스가 생성한 인스턴스를 통해 사용 가능
  	sayYourName() {
    	console.log(`제 이름은 ${this.name} 입니다.`);
    }
}
// 인스턴스 만들기
const robot = new Robot('나난난');
console.log(robot.name); // '나난난'
console.log(robot.sayYourName()); // 제 이름은 나난난 입니다.

자바스크립트의 객체 생성 방법을 다른 언어 클래스 문법처럼 변경된 것이 클래스
더 보기 좋고 편리하게 작성되어 슈가신텍스라고도 불림

  • 클래스 생성 실습
function Me(name, address, phoneNum) {
    // 생성자 함수의 this는 인스턴스를 가리킴
    this.name = name;
    this.address = address;
    this.phoneNum = phoneNum; // this.phoneNum은 우리가 전달받는 phoneNum
}
// 메소드 따로 등록
Me.prototype.canWalk = function () {
    console.log('내가 걷는다.');
}
Me.prototype.teaching = function (student) {
    student.levelUp();
}
const me = new Me('hee','seoul','010');
me.canWalk(); // 내가 걷는다
const me2 = new Me('lee','busan','011');
me2.canWalk(); // 내가 걷는다


function Student(level) {
    this.level = level;
}
Student.prototype.levelUp = function() {
    this.level++;
}

const student = new Student(1);
me.teaching(student);

위의 코드를 클래스 함수로 변경하기

class Me {
	constructor(name, address, phoneNum) {
    	this.name = name;
    	this.address = address;
    	this.phoneNum = phoneNum;
    }
  canWalk() {
  	console.log('내가 걷는다.');
  }
  teaching(student) {
  	student.levelUp();
  }
}
const me = new Me('hee','seoul','010');
me.canWalk(); // 내가 걷는다
const me2 = new Me('lee','busan','011');
me2.canWalk(); // 내가 걷는다

class Student {
	constructor(level) {
    	this.level = level;
    }
  levelUp() {
  	this.level++;
  }
}
const student = new Student(1);

클래스 상속받기

extends 키워드 사용하여 상속 받음
부모에 존재하는 프로퍼티를 상속받기 위해 super 함수 사용
이때 super은 부모 생성자를 참조

상속을 받는 클래스 (자식) 파생 클래스라고 부름

주의

  • 파생 클래스에서 생성자 함수를 사용하고 싶으면 super 함수 사용
  • 파생 클래스에 생성자 함수가 없다면 자동 super 호출되어 부모의 프로퍼티 상속 받음
  • 생성자 함수에서 this를 사용하는 경우 super 함수는 반드시 this 보다 먼저 실행
class Robot {
	constructor(name) {
    	this.name = name;
    }
  sayYourName() {
  		console.log(`삐리비리. 제 이름은 ${this.name}입니다.`);
  }
}
class BabyRobot extends Robot {
	constructor(name) {
      	// super 함수는 this 보다 먼저 실행!!
      	super(name);
    	this.ownName = '애기';
    }
  	sayBabyName() {
      // extends 상속을 통해 부모 클래스의 메소드 사용 가능
      // 때문에 this로 접근 가능!!
      	this.sayYourName();
    	console.log('Suceeding you, Father!');
    }
}
const 응애1= new BabyRobot('응애1호');
응애1.name; // 응애1호
응애1.ownName; // 애기

응애1.sayYourName(); // 삐리비리. 제 이름은 응애1호입니다.
응애1.sayBabyName(); 
// 삐리비리. 제 이름은 응애1호입니다.
// Suceeding you, Father!

비공개 프로퍼티

객체 안의 중요 정보 변경을 막는 역할
# 키워드를 이용하여 프로퍼티를 비공개로 전환
비공개 프로퍼티 값에 접근하고 수정하려면 => 해당하는 기능을 하는 메서드 사용

class Robot {
	#password
    
    constructor(name, pw) {
    	this.name = name;
      	this.#password = pw;
    }

	sayYourName() {}
	getPassword() {
    	return this.#password
    }
	setPassword(pw) {
    	this.#password = pw;
    }
}
const robot = new Robot('one', '1111');
console.log(robot.name); // one
console.log(robot.getPassword()); // 1111
robot.setPassword('1313');
console.log(robot.getPassword()); // 1313

get set 키워드를 사용하여 좀 더 단순화하기

class Robot {
	#password
    
    constructor(name, pw) {
    	this.name = name;
      	this.#password = pw;
    }
	get Password() {
    	return this.#password;
    }
	set Password(pw) {
    	this.#password = pw;
    }
}
const bot = new Robot('one', 1234);
console.log(bot.password); // 1234
// set은 값에 할당하듯이 사용한다.
console.log(bot.password = 1313); // 1313

get, set 키워드를 사용하여 객체의 프로퍼티에 접근하듯 값을 다룬다.
주의할 점 )
마치 객체의 프로퍼티를 수정하는 듯한 착각을 불러일으킨다.
협업자들에게 오해를 일으킬 수 있으므로 주석, 가이드 문서등을 통해 정보를 제공해주어야 한다.

  • 클래스 이전의 방식 (비공개 프로퍼티) - IIFE
    IIFE 함수 - 즉시 실행 함수 표현 (정의되자마자 즉시 실행)
const NewPerson = (function(){
	let age = 30;
  	function innerPerson() {}
  	innerPerson.prototype.getAge = function() {
    	return age;
    }
  	return innerPerson;
}())
	const personWithSecret = new NewPerson();
profile
함께 배워나가고 싶습니다!

0개의 댓글