일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- npm
- react
- 공무원
- 파이썬
- springboot
- 한성대맛집
- 자바스크립트에러처리
- 성신여대맛집
- 통영에어비앤비
- 뚝섬역맛집
- 영화추천
- 성신여대편백집
- 스페인여행
- 통영예쁜카페
- tomcat7
- 퇴사후공무원
- 통영여행
- ELK
- JavaScript
- 국가직
- 꼴뚜기회
- 한남동맛집
- 방이편백육분삼십성신여대
- 돈암동맛집
- 통영
- ubuntu자바설치
- 성북구맛집
- 서울숲누룽지통닭구이
- gradle
- 방이편백육분삼십
- Today
- Total
코린이의 기록
[ES6] 객체와 객체지향 프로그래밍 본문
클래스는 함수다.
ES6에 생겨난 클래스.
Javascript에서 class는 클래스 생성자로 사용할 함수를 만든다는 의미이다. 주의할점은 클래스 문법이 추가되었다는 것이지, 자바스크립트가 클래스 기반으로 바뀌었다는 것은 아니다.
function FuncCar() {
this.make = "Telsa";
this.model = "Model S";
}
const car1 = new FuncCar();
console.log(car1.make); // => Telsa
console.log(car1.model); // => Model S
class ClassCar() {
this.make = "Telsa";
this.model = "Model S";
}
const car1 = new ClassCar();
console.log(car1.make); // => Telsa
console.log(car1.model); // => Model S
typeOf FuncCar // "function"
typeOf ClassCar // "function"
짚고 넘어갈 OOP기본 용어
(ex 자동차)
- 클래스 : 자동차 (추상적이고 범용적인것)
- 인스턴스 : 특정 자동차 (구체적이고 한정적인것)
- 메서드 : 자동차의 기능 (ex. 시동을 거는기능)
- 서브클래스 : 클래스가 '운송수단'이라면, 서브클래스는 '자동차, 보트, 비행기..' (클래스에 상대적임)
클래스 만들기
class Car {
constructor () {
}
}
인스턴스 만들기
인스턴스를 만들때는 new를 사용한다.
const car1 = new Car();
const car2 = new Car();
(참고)
위에서 생성된 car1, car2 두객체의 클래스를 확인해보기 위해서 instanceof을 사용해본다.
car1 instanceof Car
car2 instanceof Car
클래스와 인스턴스 예시
클래스 ex.
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
this.userGears = ['P', 'N', 'R', 'D'];
this.userGear = this.userGears[0];
}
shift(gear) {
if (this.userGears.indexOf(gear) < 0)
throw new Error(`Invalid gear: ${gear}`);
this.userGear = gear;
}
}
여기서 this는 메서드를 호출하는 시점에 알 수 있다.
인스턴스 ex.
const car1 = new Car("Telsa", "Model S");
const car2 = new Car("Maxda", "3i");
car1.shift('D');
car2.shift('R');
console.log("user1 Gear : " + car1.userGear);
console.log("user2 Gear : " + car2.userGear);
car1은 Telsa제조사고 모델은 Model S이며 변속기능은 'D' 이다.
car2는 Maxda제조사고 모델은 3i이며 변속기능은 'R'이다.
Result
user1 Gear : D
user2 Gear : R
만약에 직접 car1의 shift를 바꾸면 어떻게될까?
car1.userGear = 'X';
console.log("user1 Gear : " + car1.userGear);
Result
user1 Gear : X
프로퍼티에 직접 접근이 가능하여 실수가 발생할 수 있다.
이를 막을 방법으로 아래와 같이 구현해보자
방법 1
class Car { constructor(make, model) { this.make = make; this.model = model; this._userGears = ['P', 'N', 'R', 'D']; this._userGear = this._userGears[0]; } get userGear(){ return this._userGear; } set userGear(value){ if(this._userGears.indexOf(value) < 0) throw new Error(`Invalid gear : ${value}`); this._userGear = value; } shift(gear){ this.userGear = gear; } }
const car1 = new Car("Telsa", "Model S"); const car2 = new Car("Maxda", "3i");
car1.userGear = 'X'; console.log("user1 Gear : " + car1.userGear); |
차이점이 있다면 get, set 메서드를 만들어서 프로퍼티에 직접 접근하지 못하도록 하였다.
Result
Error: Invalid gear : X
at Car.set userGear [as userGear] (c:\Users\Desktop\Untitled-1.js:13:15)
....
....
근데 이것은 근본적인 해결방법이 아니다.
car1._userGear = 'X'; 로 한다면??
결국 car1의 기어변속 값이 바뀌는 것이다.
여기서 "_"를 붙인다는 것은 단지 외부에서 접근해서는 안되는 프로퍼티 이름 이라고 단지 주의를 주는것이다.
프로퍼티를 완벽하게 보호하기 위해서는 아래와 같이 수정할 수 있다.
방법 2
const Car = (function(){ const carProps = new WeakMap();
class Car { constructor(make, model) { this.make = make; this.model = model; this._userGears = ['P', 'N', 'R', 'D']; carProps.set(this, {userGear: this._userGears[0]}); } get userGear(){ return carProps.get(this).userGear; } set userGear(value){ if(this._userGears.indexOf(value) < 0) throw new Error(`Invalid gear : ${value}`); carProps.get(this).userGear = value; } shift(gear){ this.userGear = gear; } } return Car; })(); |
여기서 사용한 방식은 WeakMap이라는인스턴스를 이용하였다. 이 방법은 javascript의 특징인 스코프 개념을 이용하여 보호하는 방법이다. (WekMap에 대한 개념 및 Map 대신 WeakMap을 쓰는 이유는 아래 블로그를 참고하도록.
https://m.blog.naver.com/1ilsang/221305941903)
WeakMap은 클래스(함수) 외부에서 접근할 수 없는 객체이므로 프로퍼티를 보호할 수 있다.
프로토타입
자바스크립트는 프로토타입 기반 언어다.
클래스의 인스턴스가 사용할 수 있는 메서드가 프로토타입 메서드를 말한다. 위 "Car"의 예제에서 보면 shift가 프로토타입 메서드가 된다.
함수를 정의하면 (function Car{}) 함수만 생성되는것이 아니라 "Prototype Object"라는 것도 생성이된다. 이 Prototype Object라는 녀석은 기본적으로 "constroctor(상속자)"와 "__proto__" 프로퍼티를 가지고있다. constroctor 자격이 부여되어야 new를 통해 객체를 만들 수 있다.
var Car = {};
var car1 = new Car();
Result
Uncaught Type Error: Car is not a constructor..
constructor와 __proto__을 이해해보자.
function Car() {};
var car1 = new Car();
console.log(car1);
Car라는 함수를 정의하면 Car 함수 뿐만 아니라 Car Prototype Object라는 것이 생성되는데 이 Prototype Object에는 "constructor"와 "__proto__"를 가지고 있다. __proto__는 Car라는 새로운 객체를 만들어내기 위해 사용된 Prototye Link이다. 즉 자기 자신을 만들어낸 객체의 원형을 의미한다.
proto (숨은링크)는 상위에서 물려받은 객체의 프로토타입에 대한 정보이고 prototype 프로퍼티는 자신을 원형으로 만들어질 새로운 객체들 즉 하위로 물려줄 연결에 대한 속성이다.
ex.
constrouctor : function Car () { ... }
__proto__ : object {constructor: , ...}
function Car() {};
Car.prototype.make = "Telsa";
Car.prototype.model = "Model S";
var car1 = new Car();
var car2 = new Car();
console.log(car1.make);
Car의 Prototype 속성으로 Prototype Object에 접근할 수 있다. new Operator를 통해 새 인스턴스를 만들었다. 새로 생성된 Object는 생성자의 prototype 프로퍼티에 접근할 수 있다. 객체 인스턴스는 생성자의 prototype 프로퍼티를 __proto__에 저장한다.
prototype 에서의 동적 디스패치 개념
위에서 구현한 Car class를 이용하여 Object를 생성하고 console.log로 프러퍼티 및 메서드 접근에 대해 출력해본다.
Ex 1.
const car1 = new Car();
const car2 = new Car();
console.log("car1 : " + car1);
console.log("car1.shift === Car.prototype.shift : " + car1.shift === Car.prototype.shift);
car1.shift('D');
console.log("car1.userGear : " + car1.userGear);
//car1.shift('p'); // error
console.log("car2.userGear : " + car2.userGear);
console.log("car1.shift === car2.shift : " + car1.shift === car2.shift);
console.log("car1.shift : " + car1.shift);
console.log("car2.shift : " + car2.shift);
Result
car1 : [object Object]
false
car1.userGear : D
car2.userGear : P
false
car1.shift : shift(gear) {
this.userGear = gear;
}
car2.shift : shift(gear) {
this.userGear = gear;
}
첫번째 예제에서는 car1객체에 shift 메서드가 없다. car1.shift('D')를 호출하면 car1의 prototype에 shift라는 이름의 매서드를 검색하여 호출한다. 이때 car1에 shift라는 메서드를 추가해주면 어떻게 될까?
Ex 2.
car1.shift = function(gear) {
this.userGear = gear.toUpperCase();
}
console.log("car1.shift === Car.prototype.shift : " + car1.shift === Car.prototype.shift);
console.log("car1.shift === car2.shift : " + car1.shift === car2.shift);
console.log("car1.shift : " + car1.shift);
console.log("car2.shift : " + car2.shift);
car1.shift('p');
console.log("car1.userGear : " + car1.userGear);
Result
false
false
car1.shift : function(gear) {
this.userGear = gear.toUpperCase();
}
car2.shift : shift(gear) {
this.userGear = gear;
}
car1.userGear : P
car1과 car1의 protototype은 같은 메소드 shift를 갖게된다. 이때 car1.shift('p')를 호출하면 car1의 메서드가 호출되고 프로토타입의 메서드는 호출되지 않는다.
Mixin (다중상속)
자바스크립트에서는 오직 한개의 object만 상속할 수 있다. 한개의 object에는 한개의 prototype이 존재하기 때문이다. 또한 class도 마찬가지로 오직 한개의 class만 상속할 수 있다. 하지만 이는 한계가 있다. 대신 Mixin 패턴을 사용하여 이를 해결할 수 있다.
Mixin?
Mixin은 다른 클래스의 부모2 클래스가 아니어도 다른 클래스에서 사용할 수있는 메서드가 포함 된 클래스이다.
다시 말해, Mixin은 특정 동작을 구현하는 메서드를 제공하지만 우리는이 메서드를 단독으로 사용하지 않고 다른 클래스에 동작을 추가하는 데 사용한다.
자바 스크립트에서 Mixin을 만드는 가장 간단한 방법은 유용한 메소드로 객체를 만드는 것이다. 그 객체를 어떤 클래스의 prototype으로 쉽게 병합할 수 있다.
Mixin example
// mixin
let sayHiMixin = {
sayHi() {
console.log(`Hello ${this.name}`);
},
sayBye() {
console.log(`Bye ${this.name}`);
}
};
// usage:
class User {
constructor(name) {
this.name = name;
}
}
// copy the methods
Object.assign(User.prototype, sayHiMixin);
// now User can say hi
new User("Dude").sayHi(); // Hello Dude!
여기서 mixin인 sayHiMixin은 User에 대한 "sayHi"와 "sayBye"와 같은 메소드를 추가하는 데 사용된다. 상속은 없지만 간단한 방법으로 메소드를복사 할 수 있다. 따라서 User는 다른 클래스를 상속 할 수 있으며 아래와 같은의미로 추가 메소드를 "혼합(mix)"하기 위해 mixin을 포함 할 수 있다.
class User extends Person {
// ...
}
Object.assign(User.prototype, sayHiMixin);
Mixins는 내부에서 상속을 사용할 수 있다.
For example
let sayMixin = {
say(phrase) {
console.log(phrase);
}
};
let sayHiMixin = {
__proto__: sayMixin, // (or we could use Object.create to set the prototype here)
sayHi() {
// call parent method
super.say(`Hello ${this.name}`);
},
sayBye() {
super.say(`Bye ${this.name}`);
}
};
class User {
constructor(name) {
this.name = name;
}
}
// copy the methods
Object.assign(User.prototype, sayHiMixin);
// now User can say hi
new User("Dude").sayHi(); // Hello Dude!
sayHiMixin은 sayMixin으로부터 상속 받는다. sayHiMixmin에서 상위 메소드인 super.say를 호출하면 클래스가 아닌 해당 mixin의 프로토타입의 메소드가 실행이 된다.
https://javascript.info/mixins
프로토타입 개념 Reference
Javascript 기초 - Object Property 이해하기 : http://insanehong.kr/post/javascript-prototype/
[javascript] 프로토타입 이해하기 : https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67
Reference : Learning Javascript (이선 브라운 지음)
'javascript,HTML,CSS' 카테고리의 다른 글
[javascript] Jquery click event not working after append method, 동적으로 생성된 태그 'append(html)'에 이벤트 주기 (0) | 2019.07.24 |
---|---|
[javascript] Set onclick on the <tbody tr> except for specific <td> (0) | 2019.07.05 |
[ES6] 스코프 (0) | 2019.06.20 |
[ES6] 표현식 & 연산자 (0) | 2019.06.17 |
[HTML] 한자 넣기 (0) | 2019.06.14 |