자바스크립트는
객체 지향 프로그래밍 언어
ECMAScript 스펙에 OOP라고 작성되어 있음
OOP에서 Object(객체)는
행위와 속성으로 객체의 특성을 표현
행위(Behavior)
속성(Attribute)
객체를 코드로 구체화하면
클래스에
class Point {
constructor(point){ this.point = point; }
getPoint(){ return this.point; }
};
const obj = new Point(100);
log(obj.getPoint());
log(obj.point);
// 100
// 100
➡ 따라서 비교하는 것은 의미가 없음
Syntax
클래스 작성 방법
class Point {
getPoint(){
return 100;
}
};
const obj = new Point();
log(obj.getPoint());
// 100
Syntax
클래스 작성 방법
const Point = class {
getPoint(){
return 100;
}
};
const obj = new Point();
log(obj.getPoint());
// 100
const Point = class {
getPoint(){
return 100;
}
};
/*
1. Point를 펼치면 프로퍼티, prototype, __proto__가 있음
2. prototype을 펼치면 constructor가 있으며, getPoint()가 있음
3. constructor는 Point 클래스 전체를 참조
4. 클래스에 메소드를 작성하면 prototype에 연결됨
- Point.prototype.getPoint = function(){} 형태로 작성한 것과 같음
5. __proto__에서 빌트인 Function 오브젝트의 prototype에 연결된 메소드를 참조
*/
const obj = new Point();
/*
1. Point 클래스로 인스턴스를 생성
2. obj를 펼치면 __proto__가 있으며 constructor와 getPoint()가 있음
3. Point.prototype에 연결된 메소드로 인스턴스를 생성하고 __proto__에서 참조할 수 있도록 만듦
*/
console.log(obj.getPoint());
/*
1. obj 인스턴스의 getPoint() 메소드를 호출
- obj.__proto__에 연결된 getPoint()가 호출됨
*/
강좌의 const, let 사용 기준
오브젝트의 프로퍼티가 변경되더라도
강좌의 함수, 메소드 사용 기준
함수
log(Array.isArray([]));
const point = {
getPoint(){
return 100;
}
};
log(point.getPoint());
// true
// 100
1) 클래스에 작성한 함수
class Point {
getPoint(){
return 100;
}
};
const obj = new Point();
log(obj.getPoint());
// 100
2) prototype에 연결된 function
const Point = function(){};
Point.prototype.getPoint = function(){
return 100;
};
const obj = new Point();
log(obj.getPoint());
// 100
3) 빌트인 오브젝트의 prototype에 연결된 함수
const list = [];
list.push("책");
log(list);
// [책]
클래스는 strict 모드에서 실행되므로 이에 맞추어 코드를 작성해야 함
클래스에 메소드 작성 방법
class Point {
setPoint(point){
this.point = point;
}
getPoint(){
return this.point;
};
};
log(typeof Point);
// function
const name = "Point";
class Point {
static ["get" + name](add){
return add ? 100 : 50;
}
};
log(Point["get" + name](true));
// 100
const Point = class {
setPoint(point){
this.point = point;
}
};
log(Point.prototype.setPoint);
// setPoint(point){ this.point = point; }
const Point = class { };
const obj = new Point();
Point.prototype.getPoint = function(){
return 100;
};
log(obj.getPoint());
// 100
클래스는 열거할 수 없음
prototype에 메소드 추가
const Book = class {
setTitle(title){
this.title = title;
}
};
/*
1. Book을 펼치면, 프로퍼티와 prototype이 있음
2. prototype을 펼치면, setTitle()이 있음
*/
const obj = new Book();
obj.setTitle("자바스크립트");
/*
1. obj를 펼치면, title 프로퍼티가 있으며 이것은 setTitle()에서 설정한 것
2. title처럼 인스턴스에 바로 연결된 프로퍼티를 인스턴스 프로퍼티라고 부름
3. obj.__proto__를 펼치면, setTitle()이 있음
4. 인스턴스 프로퍼티는 __proto__ 위에 표시되며 메소드는 __proto__ 안에 표시됨
*/
Book.prototype.getTitle = function(){
return this.title;
};
/*
1. Book.prototype에 getTitle()이 추가됨
2. obj.__proto__에 getTitle()이 표시됨
3. prototype에 메소드를 추가로 연결하면 생성된 모든 인스턴스에서 메소드를 사용할 수 있음
4. 이것을 prototype sharing(공유)라고 부름
*/
console.log(obj.getTitle());
/*
1. obj 인스턴스의 getTitle() 메소드가 호출되며 "자바스크립트"를 반환함
*/
constructor는 생성자로 인스턴스를 생성하고 초기화함
ES5까지는 constructor를 작성할 수 없었으나 ES6부터는 작성할 수 있음
class Point {
constructor(point){
this.point = point;
}
};
const obj = new Point(100);
/*
*** 인스턴스 생성 방법
1. const obj = new Point(100);
- new 연산자가 Point 클래스 오브젝트의 constructor를 호출
2. constructor(point){...}
- point 파라미터 값은 100이 됨
3. 엔진은 빈 오브젝트{}를 생성 => 이것이 인스턴스
4. 인스턴스에 프로퍼티 이름과 값을 설정하여 인스턴스 구조를 만듦. __proto__, __proto__.constructor 등
5. constructor 블록의 코드를 실행
6. this.point = point;
- this가 생성한 인스턴스를 참조. 인스턴스{}를 먼저 생성하므로 this로 참조할 수 있음
7. ponit는 인스턴스 프로퍼티가 됨. point 파라미터 값이 100이므로 point 프로퍼티 값은 100이 됨
8. 생성한 인스턴스를 반환
*/
class Point {
setPoint(point){
this.point = point;
}
};
const obj = new Point();
obj.setPoint(100);
/*
1. 엔진이 class 키워드를 만나 Point 클래스 오브젝트를 생성할 때 constructor에서 클래스 전체를 참조하도록 환경을 만듦
2. constructor를 작성하지 않으면 prototype.constructor를 사용하므로 인스턴스를 생성할 수 있지만 인스턴스에 초기값을 설정할 수 없음
3. 클래스에 constructor를 작성하면 prototype.constructor를 오버라이드하게 됨
*/
constructor에 return을 작성하지 않으면
constructor에서 Number, String을 반환하면
class Point {
constructor(point){
this.point = point;
return point;
}
};
const obj = new Point(100);
log(obj.point);
log(obj instanceof Point);
// 100
// true
class Point {
constructor(point){
return {point: point};
}
};
const obj = new Point(100);
log(obj);
log(obj instanceof Point);
// {point: 100}
// false
getter는 메소드를 호출하여 값을 구함
클래스에 getter 작성 방법
class Point {
constructor(point){
this.point = point;
}
get getPoint(){
return this.point;
}
};
const obj = new Point(100);
log(obj.getPoint);
// 100
class Point {
set setPoint(point){
this.point = point;
}
};
const obj = new Point();
obj.setPoint = 100;
log(obj.point);
// 100
Syntax
static 메소드 작성 방법
class Point {
static getPoint(){
return 100;
}
};
log(Point.getPoint());
// 100
class Point {
static getPoint(){
return 100;
}
};
const obj = new Point();
log(obj.getPoint);
// undefined
try {
const obj = Point;
} catch {
log("호이스팅 불가");
};
class Point {
static getPoint() {
return 100;
}
};
log(Point.getPoint());
// 호이스팅 불가
// 100
new.target 프로퍼티는
new 연산자로 constructor를 호출하면
class Point {
constructor(){
log(new.target.name);
}
};
new Point();
// Point
function book() {
log(new.target);
};
book();
// undefined
상속(Inheritance)은 OOP 기능 중 하나
상속해주는 클래스, 상속받을 클래스를
상속받는 클래스를
Syntax
extends 키워드로 상속을 구현
class Book {
constructor(title){
this.title = title;
};
getTitle(){
return this.title;
}
};
class Point extends Book {
setPoint(point){
this.point = point;
}
};
const obj = new Point("책");
log(obj.getTitle());
// 책
class Book {
constructor(title){
this.title = title;
};
getTitle(){
return this.title;
}
};
/*
1, 엔진이 Book.prototype.getTitle() 형태로 만듦
*/
class Point extends Book {
setPiont(point){
this.point = point;
}
};
/*
1. Book {setPoint(point){...}}
- setPoint()는 Point 클래스의 메소드이며
- Point.prototype에 연결됨
2. 엔진이 extends 키워드를 만나면
- Point 클래스에서 Book 클래스를 상속받아
- 서브와 슈퍼 구조를 만듦
3. Point.prototype.__proto__를 펼치면
- getTitle()이 있으며
- Book.prototype에 연결된 메소드
4. prototype.__proto__에 상속해주는 클래스의
- prototype에 연결된 메소드를
- 구조적, 계층적으로 만듦
=> 이것이 상속
*/
/*
Point.__proto__를 펼치면 상속받은 Book 클래스 전체가 표시됨
*/
const obj = new Point("책");
/*
1. obj를 펼치면 {title: "책"}이 있으며
- 이것은 인스턴스 프로퍼티
2. 이런 방법으로 인스턴스마다
- 고유의 프로퍼티 값을 가질 수 있음
3. 고유의 값을 가지는 것이 인스턴스의 가장 큰 목적
4. 상속이 클래스의 가장 큰 목적이 아님
- 상속은 인스턴스 프로퍼티를 지원하기 위한 수단
5. obj.__proto__를 펼치면 setPoint()가 있으며
- 이것은 서브 클래스의 메소드
6. obj.__proto__.__proto__를 펼치면 getTitle()이 있으며
- 이것은 슈퍼 클래스의 메소드
7. 이처럼 __proto__를 사용하여
- 슈퍼 클래스의 prototype에 연결된 메소드를
- 구조적, 계층적으로 연결
=> 이것이 상속 구조
8. 인스턴스의 메소드를 호출하면
- __proto__ 구조를 따라 아래로 내려 가면서 메소드를 식별
- 식별하는 위치에 메소드가 있으면 실행
*/
class Point extends Book {
getTitle(){
return "서브 클래스";
}
};
/*
1. 오버라이드 설명을 위해 Point 클래스에도 getTitle() 작성함
2. getTitle()이 양쪽 클래스에 있음
*/
const obj = new Point("책");
/*
1. obj.__proto__를 펼치면 getTitle()이 있으며
= 이것은 서브 클래스의 메소드
2. obj.__proto__.__proto__를 펼치면 getTitle()이 있으며
- 이것은 슈퍼 클래스의 메소드
*/
console.log(obj.getTitle());
/*
1. obj.getTitle()을 호출하면
- 먼저 서브 클래스에서 찾기
- 즉, obj.__proto__에서 찾기
2. 없으면 슈퍼 클래스에서 찾기
- 즉, obj.__proto__.__proto__에서 찾기
3. obj.__proto__에 즉, 서브 클래스에
- getTitle()이 있으므로 이것을 호출
4. 이것이 메소드 오버라이딩
*/
class Book {
getTitle(){
log("슈퍼")
}
};
class Point extends Book {
getTitle(){
super.getTitle();
log("서브");
}
};
new Point().getTitle();
// 슈퍼
// 서브
class Book {
setTitle(title){
this.title = title;
}
};
class Point extends Book {
};
const obj = new Point();
obj.setTitle("책");
log(obj.title);
// 책
class Book {
constructor(title){
this.title = title;
};
};
class Point extends Book {
};
const obj = new Point("책");
log(obj.title);
// 책
class Book {
setTitle(title){
this.title = title;
}
};
class Point extends Book {
// constructor(point){
// this.point = point;
// };
};
const obj = new Point(100);
class Book {
constructor(title){
this.title = title;
};
};
class Point extends Book {
constructor(title, point){
super(title);
this.point = point;
};
};
const obj = new Point("책", 100);
log(`${obj.title}, ${obj.point}`);
// 책 100
class Point extneds Array {
constructor(){
super();
}
getTotal(){
let total = 0;
for (const value of this){
total += value;
};
return total;
}
};
const obj = new Point();
obj.push(10, 20, 30);
log(obj.getTotal());
// 60
Object는 클래스가 아니므로
__proto__
구조가 되는 것을 활용하여 상속을 수현할 수 있음Object 상속
__proto__
구조를 만듦const Book = {
getTitle(){
log("Book");
}
};
const Point = {
getTitle(){
super.getTitle();
}
};
Object.setPrototypeOf(Pont, Book);
Point.getTitle();
// Book
class Home extends Image {
constructor() {
super();
};
setAttr(){
this.src = "../../image/rainbow.png";
this.alt = "집과 나무가 있고 " + "무지개가 있는 모습";
this.title = "무지개";
}
};
const obj = new Home();
obj.setAttr();
document.querySelector("#show").appendChiled(obj);
super();
this.src, this.alt, this.title
class Music extends Audio {
constructor() {
super();
};
setAttr(src, controls, muted, loop){
this.src = src;
this.controls = controls;
this.muted = muted;
this.loop = loop;
}
};
const obj = new Music();
const src = "../../image/Beet5.ogg";
obj.setAttr(src, true, true, true);
document.querySelector("#show").appendChiled(obj);
super();
this.src, this.controls
인스턴스.메소드() 형태로 호출하면
static 메소드에서 this는
class Point {
static setPoint(point){
this.value = point;
};
};
Point.setPoint(100);
log(Point.value);
log(new Point().value);
// 100
// undefined
class Point {
static value = 100;
};
log(Point.value);
Point.bonus = 300;
log(Point.bonus);
log(new Point().value);
// 100
// 300
// undefined
class Point {
constructor(){
log(this.constructor.get());
}
static get(){
return 100;
}
};
new Point();
// 100
class Point {
*getPoint() {
yield 10;
yield 20;
}
};
const gen = new Point();
const obj = gen.getPoint();
log(obj.next());
log(obj.next());
log(obj.next());
// {value: 10, done: false}
// {value: 20, done: false}
// {value: undefined, done: true}