비슷한 객체 여러 개 만들기 : 생성자 함수와 class

나는야 토마토·2022년 4월 4일
0

JavaScript💛

목록 보기
5/15
post-thumbnail
post-custom-banner

우리는 웹사이트를 만들 때 회원의 정보, 상품의 정보등 객체를 사용하여 값이 변경되는 경우를 만들어야 할 때가 있다! 이러한 경우 생성자 함수나 class를 이용하면 좋다! es6에 class가 추가되었으므로 이를 사용하면 좋다고 한당!

객체 리터럴

let user = {
	name: 'Mike',
    age: 30,
}

생성자 함수

생성자 함수는 보통 첫 글자는 대문자로 해서 만들어준다. 이름과 나이를 인자로 받아서 this로 넣어주고 있다. 그리고 new 연산자를 사용해서 함수를 호출하고 있다.

function User(name, age){
// User -> 대문자
  this.name = name;
  this.age = age;
}

let user1 = new User('Mike', 30);
let user2 = new User('Jane', 20);
let user3 = new User('Tom', 17);
// new 연산자를 사용해서 호출


이렇게 값은 다르지만 비슷한 여러 개의 객체를 만들 수 있다!
생성자 함수는 붕어빵 틀이나 와플 팬이라고 생각하면 된다. 원하는 재료들을 넣어주고, 찍어내면 되는 것이다! 지금 필요한 재료는 이름과 나이였다.

이제 생성자 함수가 어떻게 동작하는지 자세히 알아보자!

new 함수명을 실행하면
빈 객체{}를 만들고
this를 할당한다. this = {} 함수를 실행하면서 function User(name, age){} this의 프로퍼티들을 추가한다. this.name = name;this.age = age; 그 후에 마지막으로 this를 반환한다. (return this)

function User(name, age){
  // this = {}
  this.name = name;
  this.age = age;
  // return this
}

이제는 메소드를 추가해보자! 모든 User에는 sayName이 있어서 자신의 이름을 말하는 메소드가 있다고 가정해보자

function User(name, age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    // console.log의 this는 user5를 뜻함
  	console.log(this.name);
  }
}
let user5 = new User('Han', 40);
user5.sayName(); // Han

여기서 console.log의 this는 user5를 뜻하기 때문에 user5.sayName은 Han을 뜻한다.


코드를 직접 실행해보면 위의 사진과 같다. 이 때 new 생성자 함수를 이용하면 Item함수의 주석 부분이 실행되어지는 것을 알 수 있다. 꼭 new를 붙여야지만 주석 부분이 실행되어지는 것이다!

만약 여기서 new를 안 붙이게 되면 해당 값이 undefined가 뜨게 된다.

new를 안 붙이면 함수가 실행되어지는 것이다. 사실 이 함수는 아무것도 return 해주고 있지 않기 때문에 undefined로 들어가게 되서 출력이 되는 것이다.
즉, 생성자 함수는 잊지말고 new를 붙여주어야 한다!!!!!

상속, 프로토타입(Prototype)

const user = {
	name: 'Mike'
}
console.log(user.name); // "Mike"
user.hasOwnProperty('name'); // true
user.hasOwnProperty('age'); // false

객체에는 자신이 프로퍼티를 가지고 있는지 확인하는 메소드인 user.hasOwnProperty를 사용할 수 있다.

객체의 프로토타입은 사진과 같이 알 수 있다. 객체에서 프로토를 읽으려고 할 때 없으면 여기에서 찾게 된다. 만약 hasOwnProperty이 객체 안에 있다면 다음과 같이 실행되어진다.

const user = {
	name: 'Mike',
    hasOwnProperty: function(){
    	console.log('haha')
    }
}
user.hasOwnProperty()
// haha

프로토타입이 어떻게 동작하는지 알기 위해서 상속이라는 개념을 살펴보자!

color, wheels, drive는 동일하게 사용된다. 객체들이 늘어나면 공통된 부분이 중복해서 사용되므로 _proto_로 사용할 수 있다. bmw의 wheels를 작성하면 객체의 내부에서 wheels를 찾게 된다. 찾게 되면 거기서 탐색을 멈추고 만약 없다면 _proto_에서 찾게 된다.


상속은 계속해서 이어질 수 있다. x5를 만들고, x5는 bmw를 상속하도록 만들었다

그림으로 보면 x5에서 navigation이 없기 때문에 _proto_타입인 bmw에서 탐색을 하고, 있으니깐 멈추게 된다. drive()는 _proto_타입인 car까지 올라가서 사용할 수 있다. 이러한 것을 Prototype Chain이라고 한다.

for(p in x5){
	console.log(p);
   // color
  // name
  // navigation
  // wheels
  // drive
}
Object.keys(x5);
// ["color", "name"]
Object.values(x5);
// ["white", "x5"]

for문을 통해 사용해보면 해당 값이 다 나온 다는 것을 알 수 있다. 사실 name과 color를 제외하고 프로토타입에서 정의한 프로토이다. 하지만 Object.keys나 Object.value를 하면 상속된 값들이 나오지는 않는다.

생성자 함수를 이용하면 비슷한 객체들을 간단하게 만들 수 있다. 여기서 매개변수로 전달해준 color값 말고, wheels와 drive는 동일하기 때문에 분리할 수 있다. 여기서 _proto_prototype은 모습이 다르다. 생성자함수가 생성한 객체에 _proto_을 설정한 의미이다.

class : es6에 추가된 스펙

const User = function(name, age){
	this.name = name;
    this.age = age;
    this.showName = function(){
      console.log(this.name);
    }
}

const mike = new User("Mike", 30);

class User2{
	constructor(name, age){
    	this.name = name;
        this.age = age;
    }
    showName(){
    	console.log(this.name);
    }
}
const tom = new User2("Tom", 19);
// User2 {name: "Tom", age: 19}

어떤 게 다른지 살펴보면 new를 통해서 호출했을 때 내부에서 정의된 내용으로 객체를 생성하는 것은 동일하다. 일단 class라는 객체를 사용하고, 내부의 constructor가 있다. constructor는 객체를 만들어주는 생성자 메소드이다. new를 통해 호출하면 자동으로 실행되어진다. 객체를 초기화하기 위한 값이 저장이되고, 인수를 넘겨받을 수 있다. showName()처럼 class에 정의한 메소드는 User2의 프로토타입에 저장되어진다. 그래서 mike와 tom을 확인해보면 mike는 객체 내부의 showName이 있고, Tom은 protoType의 내부에 있다.
그러면 생성자 함수에서 class와 동일하게 동작하기 위해서는 아래와 같이 작성하면 된다.

User.prototype.showName = function(){
	console.log(this.name);
}

그러면 단순히 문법의 편의성을 위해 class가 탄생한 것일까?
new를 빼고 실행해보면 class는 typeerror가 뜨고, 생성자 함수는 undefined가 뜬다. 이렇게 error로 인해 개발자가 코드를 잘못입력 했을 시에 알려준다.

class: 상속

생성자의 함수 경우에는 prototype으로 상속을 구현해주었었다. class에서 상속은 extends키워드를 사용한다.

class: 메소드 오버라이딩

만약 Bmw내부에 Car에서 정의한 메소드와 동일한 이름의 메소드가 존재하면 어떨까? 동일한 이름으로 주어진다면 덮어씌워지게 된다. 부모의 메소드를 사용하면서 확장하고 싶다면 super라는 키워드를 사용하면 된다.

class : 오버라이딩

counstrutor 오버라이딩을 해보기 위해 해당 constructor를 생성해주고 this.navigation = 1로 만들어주면 error가 나게 된다. 에러 메시지를 보면 부모 생성자를 반드시 호출해야한다고 말한다. class의 constructor는 빈 객체를 만들어주고 this로 이 객체를 가르키게 된다. 반면 extends를 써서 만든 자식 class는 빈 객체를 만들어지고 this에 할당하는 이 작업을 건너뛴다. 그래서 항상 super();키워드로 부모클래스의 constructor를 실행해주어야 한다.

하지만 확인해보면 color의 값이 undefined가 뜨게 된다. 생성할 때 blue라는 값을 넣어주었지만 제대로 동작하기 위해서는 자식 클래스의 constructor에 동일한 인수를 받는 작업을 해주어야한다.


만약 constructor가 없다면 아래의 코드와 같이 동작하게 된다.

constructor(...args){
	super(...args);
}

자식생성자는 무조건 부모생성자를 호출해야하는 것이다. 자식 생성자의 constructor가 있으면 위의 처럼 호출되지 않기에 super()를 생성해주고,인수를 할당해주어야한다.


출처)

!youtube[ddJcDZHBRm0]
!youtube[OpvtD7ELMQo]

profile
토마토마토
post-custom-banner

0개의 댓글