221017.til

Universe·2022년 10월 17일
0

study

목록 보기
35/49

class 확장기능 ( extends, super )

extends

class 를 상속하는 기능.
유사한 class 를 하나 더 만들고 싶으면 쓸 수 있다.
아래의 예시를 보자.

판타지 세계의 모험가 길드 인사담당자 라고 가정을 해보자.
모험가 길드에서는 등록할 인원의 속성별로 구분한다.
‘물속성보유자’ 라는 class 를 만들었다.
해당 class 로 만드는 오브젝트는 ‘물’ 이라는 elements 를 보유하고 있다.

class 물속성보유자 {
	constructor(name){
		this.elements = '물';
		this.name = name;
	}
}

let man1 = new 물속성보유자('김덕구');

// man1
// 물속성보유자 {elements: '물', name: '김덕구'}

모험가 길드에 첫번째로 등록된 인원은 물속성을 가진 김덕구씨이다.
그런데 물 속성을 메인 속성으로 가지고 있으면서
서브속성으로 불속성을 가진 사람들이 나타났다.
그래서 인사담당자는 새로운 class 를 만들었다.

class 물속성보유자 {
	constructor(name){
		this.elements = '물';
		this.name = name;
	}
}

class 물불속성보유자 {
	constructor(name){
		this.elements = '물';
		this.subElemets = '불';
		this.name = name;
	}
}

let woman1 = new 물불속성보유자('이수영');

// woman1
// 물불속성보유자 {elements: '물', subElemets: '불', name: '이수영'}

서브속성을 제외한 나머지 항목은 같은 내용이므로 물속성보유자 class 에서 복사해서 붙혀넣었다.
그런데 이번에는 물속성이 메인이면서 풀속성, 바람속성, 땅속성인 사람들이 나타나고 말았다.
인사담당자는 머리가 어지러워졌다…

예시에서는 constructor 가 아주 간단한 항목들이어서 복사하여 붙혀넣는 과정이 그렇게 큰 일이 아니지만
constructor 가 점점 더 복잡해지고 세밀해진다면 비슷한 새로운 class 를 만드는게 여간 불편한 일이 아닐 수 없다.
그럴 때 쓸 수 있는 키워드가 extends 와 super 이다.
따라서 인사담당자는 이런식으로 코딩을 할 수 있었다.

class 물속성보유자 {
	constructor(name){
		this.elements = '물';
		this.name = name;
	}
}

class 물땅속성보유자 extends 물속성보유자 {
	constructor(name){
		super(name);
		this.subElements = '땅';
	}
}

class 물바람속성보유자 extends 물속성보유자 {
	constructor(name){
		super(name);
		this.subElements = '바람';
	}
}

let man2 = new 물땅속성보유자('박재롱');
let woman2 = new 물바람속성보유자('이시영');

//물땅속성보유자 {elements: '물', name: '박재롱', subElements: '땅'}
//물바람속성보유자 {elements: '물', name: '이시영', subElements: '바람'}

여기서 super() 는 뭘까 ?
super 는 부모 class 의 constructor 와 prototype 을 대신 가져다 쓰는 역할을 해준다.
예를들어 위의 코드에서 super 의 역할은
물속성보유자의 constructor 를 물땅속성보유자로 extends 할 때
복사하여 붙혀넣는 역할을 대신 수행해준다고 할 수 있다.

class 물속성보유자 {
	constructor(name){
		this.elements = '물';
		this.name = name;
	}
}

class 물땅속성보유자 extends 물속성보유자 {
	constructor(name){
		super(name);
// ===
//	constructor(name){
//		this.elements = '물';
//		this.name = name;
		this.subElements = '땅';
	}
}

이런식으로.

주의할 점이라면 super() 의 파라미터는 반드시 복사할 constructor 의 파라미터를 명시해주어야 한다는 것.
또한 extends 할 class에 super 키워드가 없으면

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

이런 무시무시한 에러를 출력하게 된다.
직역하자면, 파생된 클래스의 this에 접근하려면 super 를 호출해야 한다는 뜻이다.
extends로 class에 정의된 함수도 가져올 수 있는데,
extends 한 class 도 상속받기 전 class의 prototype 으로 존재하는 함수를 사용할 수 있다.

class 물속성보유자 {
	constructor(name){
		this.elements = '물';
		this.name = name;
	}
	워터볼(){
		console.log('워터볼!')
	}
}

class 물땅속성보유자 extends 물속성보유자 {
	constructor(name){
		super(name);
		this.subElements = '땅';
	}
}

let man2 = new 물땅속성보유자('박재롱')
man2.워터볼();

// 워터볼!

물땅속성보유자 박재롱씨는
분명 상속받은 물땅속성보유자 class 에 워터볼 마법을 정의해준적 없음에도
물속성보유자라면 사용할 수 있는 워터볼을 사용할 수 있는 것 처럼 보인다.
새로운 함수의 정의에도 super() 를 이용할 수 있다.

class 물속성보유자 {
	constructor(name){
		this.elements = '물';
		this.name = name;
	}
	워터볼(){
		console.log('워터볼!')
	}
}

class 물땅속성보유자 extends 물속성보유자 {
	constructor(name){
		super(name);
		this.subElements = '땅';
	}
	체인마법(){
		super.워터볼();
		console.log('어스볼!');
	}
}

let man2 = new 물땅속성보유자('박재롱')
man2.체인마법();

//워터볼!
//어스볼!

물땅속성보유자 박재롱씨는
물속성보유자라면 사용할 수 있는 워터볼에 땅속성보유자만 사용할 수 있는 어스볼 마법을 융합하여
체인마법이라는 마법을 사용할 수 있게 됐다 !

Getter & Setter

최근의 개발추세는 원본 데이터를 직접 꺼내 수정하는게 아니고
원본 데이터를 꺼내고 수정하는 함수를 만들어 간접적으로 사용한다고 한다.
그 이유가 무엇일까 ?

let obj = {
	name : 'soo',
	money : {
		money1 : {
			money2 : {
				money3 : 3000}
		}
	}
}

이렇게 오브젝트 내의 데이터가 여러겹으로 중첩된 오브젝트가 있다고 가정해보자.
그러면 money3 의 데이터를 알아보기위해서는

console.log(obj.money.money1.money2.money3);

이런식으로 코드를 짜야한다.
그런데 obj 함수 안에 money3 를 찾아서 출력하는 함수를 만들었다면 어떨까 ?

let obj = {
	name : 'soo',
	money : {
		money1 : {
			money2 : {
				money3 : 3000}
		}
	},
	getMoney(){
		return this.money.money1.money2.money3;
	}
}

obj.getMoney();
// 3000

간단하게 데이터를 찾을 수 있겠다.

다른 예시를 살펴보자.

let obj2 = {
	name : 'kim',
	age : 20
}

obj2.age = '30';
console.log(obj2.age)
// '30'

어떤 문제가 있을까 ?

자바스크립트 개발자가 가장 하기 쉬운 실수인데
분명 age 는 숫자 데이터를 입력해야 하는데
‘’ 따옴표를 붙히는 바람에 문자 데이터로 저장이 되어버렸다 !

여기서 나이에 + 1을 해보면…

console.log(obj2.age + 1)
// 301

입력한 ‘30’을 문자열 데이터로 인식하여 301 이 출력되고 만다.
이러한 실수는 오류메시지도 출력하지 않을 뿐더러 찾기도 매우 까다롭다.

그런데 저러한 기능을 함수로 구현하면 어떨까 ?
나이를 다시 설정하는 함수와
나이에 +1을 더해 꺼내주는 기능을 구현해보자.

let obj2 = {
	name : 'kim',
	age : 20,
	reAge(age){
		this.age = parseInt(age)
	},
	nextAge(){
		return this.age + 1
	}
}

obj2.reAge(30)
console.log(obj2.age)
// 30
obj2.reAge('30')
console.log(obj2.age)
// 30
obj2.reAge('rr')
console.log(obj2.age)
// NaN
obj2.nextAge()
// 31

age에 문자를 넣으면 NaN 이 나오거나 ‘’ 가 붙은 숫자를 숫자형 데이터로 변환시켜 줄 수도 있고,
저런식의 코드는 원본데이터를 수정하지 않고 함수 내부의 값만 수정하면 되므로 유지보수도 쉬워보인다.
저런식의 코딩 추세가 발달하게 되자 나온 문법이 get 과 set 이다.
get과 set을 이용하면 이런식으로 구현된다.

let obj2 = {
	name : 'kim',
	age : 20,
	set reAge(age){
		this.age = parseInt(age)
	},
	get nextAge(){
		return this.age + 1
	}
}

obj2.reAge = 30;
obj2.nextAge;

원래는 함수처럼 다루어야 할 대상들을 마치 파라미터 처럼 쓸 수 있게 해준다.
set 함수는 데이터를 수정 및 입력하는 기능을,
get 함수는 데이터를 출력해주는 기능을 명시한다.

주의사항이 있다면
set 함수는 반드시 한 개의 파라미터를 가져야 하고,
get 함수는 반드시 파라미터가 존재하지 않으면서 함수의 return을 명시해주어야 한다.

어찌보면 당연한 이야기인데,
특정 값을 수정하기 위해서는 수정할 대상과 수정할 내용, 수정할 사항이 명시되어야 하며,
특정 값을 단지 출력하기 위해서라면 입력해주어야 하는 값은 없지만,
함수 내부에서 특정 값을 return 받아야 하는 것이다.
class 에서도 get과 set 을 사용할 수 있다.

class a {
        constructor() {
          this.name = "soo";
          this.age = 20;
        }
        set reAge(age) {
          this.age = parseInt(age);
        }
        get nextAge() {
          return this.age + 1;
        }
      }

      let man = new a();
      console.log(man);
      man.reAge = 30;
      console.log(man.age);
      console.log(man.nextAge);

      // a {name: 'soo', age: 20}
      // 30
      // 31

이런식으로.

구조분해할당(destructuring)

배열이나 오브젝트의 내용을 변수로 각각 정의해주고 싶을 때 쓸 수 있는 아주 간단한 문법.

기본 형식은 이렇다.

let arr = [1,2,3]
let [a,b,c] = arr;
// a = 1, b = 2, c = 3;

let [a,b,c=10] = [1,2];
// a = 1, b = 2, c = 10;

let [a,b,c] = [];
// a = undefined, b = undefined, c = undefined;

let obj = {
	name: 'kim',
	age: 30
}

var { name, age } = { name: 'kim', age: 30}
var { name, age } = obj;
// name = 'kim', age = 30;

default 파라미터도 적용해줄 수 있고, 할당을 하지 않으면 undefined 를 반환한다.
단, 오브젝트는 인덱싱이 되지 않으므로 key와 value를 매칭해주어야 한다.
이런식으로 오브젝트를 변수를 이용해서 만드는 방법이나,

let name = 'kim';
let age = 30;

let obj = { name : name, age : age }
===
let obj = { name, age }

함수의 파라미터에도 사용할 수 있다.

let obj = {
	name: 'kim',
	age: 30
}

function func( {name, age} ){
	console.log(name);
	console.log(age);
}

func(obj)
//kim
//30

let arr = [1,2,3,4,5]

function func2([...arg]){
	arg.forEach((e)=>{
		console.log(e)
	})
}

func2(arr);
// 1 2 3 4 5

import / export

자바스크립트 파일 분리. 모듈화 라고도 한다.

원래는

<script src='script.js'></script>

이런식으로 불러왔었다.

ES6부터는

<script type='module'>
	import a form '/script.js'
</script>

// script.js
let a = 10;
export default a;

이런식으로 불러올 수 있다.

그런데 src로 가져오는 방법과 다른 부분은 가져올 파일의 특정 부분만 따로 떼어서 가져올 수 있다는 점이다.
export default 로 가져온 import 변수는 작명이 가능하다. a 를 b로 바꿔도 작동한다.
해당 키워드는 한 파일에 한번만 사용할 수 있다.
여러개의 변수를 export 하고싶다면

// script.js
let a = 10;
let b = 20;
export let c = 30;
export {a,b};

<script type='module'>
	import {a,b,c} from '/script.js';
	console.log(a);
	console.log(b);
	console.log(c);
</script>

// 10 20 30

이런식으로 중괄호를 씌워주어야 쓸 수 있다.
중괄호를 씌워서 import 해오는 경우에는 작명이 불가능한데,
작명을 하려면


<script type='module'>
	import {a as aa} from '/script.js';
	console.log(aa);
	console.log(a);
</script>

//10
//Uncaught ReferenceError: a is not defined

as 키워드를 사용하면 작명을 할 수 있다.

<script type='module'>
	import {* as A} from '/script.js';
		console.log(A)
</script>

//
Module {Symbol(Symbol.toStringTag): 'Module'}
a: 10
b: 20
c: 30
Symbol(Symbol.toStringTag): "Module"
get a: ƒ ()
set a: ƒ ()
get b: ƒ ()
set b: ƒ ()
get c: ƒ ()
set c: ƒ ()

* 키워드를 사용하여 모듈 형식으로 export 한 모든 내용을 불러올 수도 있다.

Stack & Queue

예를들어 1을 출력하고 1초쉬고 2를 출력하고 그 다음 3을 출력하는 코드를 짜보자.

console.log(1);
setTimeout(() => {console.log(2)}, 1000);
console.log(3);

setTimeout 은 설정한 시간 후에 콜백함수를 실행해주는 함수이다.
얼핏보면 잘 될 것 같다.
그런데 실행 결과는

//
1
3
undefined
2

제대로 되지 않는다.

다른 언어, 예를들어 파이썬 같은 경우에

print(1)
time.sleep(1)
print(2)

이런식으로 코딩하면 정상적으로 1초를 쉬고 2를 출력한다.
왜 그럴까 ?
왜 자바스크립트만 잘 되지 않을까 ?

자바스크립트는
Stack 에서 한번에 하나의 코드만 실행할 수 있다.
single threaded, 단일 쓰레드를 사용하기 때문인데,
그래서 setTimeout 같은 함수는 즉각 실행할 수 없기때문에 대기장소에서 잠시 보류한다.
특정 장소에서 대기가 끝난 함수는 먼저 Queue 라는 공간으로 보내는데,
Queue 에서는 Stack 의 작업이 모두 끝나면 순서대로 함수들을 Stack 으로 보낸다.

그래서 자바스크립트로는 연산이 오래걸리는 작업을 시키지 않는다.
왜냐하면 Stack 에서 오랫동안 작업을 하는 동안 ajax요청이나 이벤트가 발생하면
그동안 요청을 처리하지 못하므로, 웹 프리징에 걸려버리고 만다.

자바스크립트는 이처럼 한번에 코드 한줄씩 차례로 실행할 수 있다.
이를 동기식 처리라고 한다.
바로바로 실행되지 않는 작업을 처리해야 할 때.
예를들어,
특정 시간 이후에 처리되는 함수에 대한 작업 (setTimeout)
특정 이벤트가 발생했을 때 처리되는 작업 (addEventListener)
서버에서 특정 데이터를 요청하여 전달받을 때 까지 기다려야 하는 작업 (ajax 요청)
이러한 Web API 와 연관된 함수들을 사용하면 지금 당장 해결해야할 작업부터 먼저 처리를 한다.
자바스크립트의 비동기식 처리는 이런식으로 처리된다.
웹브라우저의 특수성때문에 그렇다.

profile
Always, we are friend 🧡

0개의 댓글