객체(Object)
- JavaScript의 데이터 타입은 크게 두가지로 분류된다.
- 원시 타입 - 단 하나의 값만을 나타내고, 값의 변경이 불가능한 값
- 객체 타입 - 다양한 타입의 값을 하나의 단위로 구성, 복합적인 자료구조이며 객체 타입의 값을 변경 가능
- 객체기반의 프로그래밍 언어이며 구성하는 거의 모든 것은 객체로 구성되어 있다.
- 객체(Object)는 0개 이상의 프로퍼티로 구성된 집합이며, 하나의 프로퍼티는 Key와 Value로 구성
객체 리터럴
- 리터럴(literral)은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해
- 값을 생성하는 표기법
=> 객체를 생성하기 위한 표기법
- 객체를 생성하기 위해 Class를 먼저 선언하고 new 연산자와 함께
- 생성자를 호출할 필요없이 일반적인 숫자, 문자열을 만드는것과 유사하게 객체를 생성
객체 리터럴로 객체생성
- 객체리터럴은 중괄호{ } 내에 0개 이상의 프로퍼티를 정의해서 선언
let objectLiteral = {
key: "Value",
helloWorld: function () {
return "Hello world";
},
};
프로퍼티(Property)
- 객체의 상태를 나타내는 값(Data)
- 프로퍼티는 키(Key)와 값(Value)으로 구성
const human = {
// 프로퍼티 키: 'name', 프로퍼티 값: '이용우'
name: "홍길동",
// 프로퍼티 키: 'human age', 프로퍼티 값: 28
"human age": 28,
};
메서드(Method)
- 프로퍼티를 참조하고 조작할 수 있는 동작(behavior)
- 객체의 프로퍼티 값이 함수로 구성되어 있을 경우 메서드(Method)라 한다.
let objectLiteral = {
key: "Value", // 프로퍼티
helloWorld: function () {
// 메서드
return "Hello world";
},
};
console.log(objectLiteral.helloWorld()); // Hello world
- 객체 리터럴을 활용하여 사친연산을 하는 객체를 만들어보기
let calculator = {
add: function (a, b) {
return a + b;
},
sub: function (a, b) {
return a - b;
},
mul: function (a, b) {
return a * b;
},
div: function (a, b) {
return a / b;
},
};
console.log(calculator.add(3,2)); // 5
console.log(calculator.sub(3,2)); // 1
console.log(calculator.mul(3,2)); // 6
console.log(calculator.div(3,2)); // 1.5
구조 분해 할당(Destructuring assignmant)
- ES6에서 새로 도입된 문법
- 객체(Object)나 배열(List)을 변수로 '분해'할 수 있다.
객체(Object) 구조 분해 할당
- 객체를 분해할 때에는 반드시 변수명과 객체의 프로퍼티 이름이 일치해야 한다.
- 프로퍼티의 이름이 유효한 식별자인 프로퍼티만 분해 후 할당된다.
const obj = { name: "홍길동", age: 28, tech: "Node.js" };
const { name, age, tech, hair } = obj;
console.log(name); // 홍길동
console.log(age); // 28
console.log(tech); // Node.js
console.log(hair); // undefined: obj에는 "hair" 프로퍼티가 없다.
배열(Array) 구조 분해 할당
- 배열로 분해할 때에는 변수 이름을 마음대로 선언할 수 있고, 배열의 순서대로 할당
- 배열 구조 분해 할당으로 선언한 변수를 제외한 나머지 배열의 요소는 할당되지 않는다.
const arr = ["Node.js", "React", "Spring"];
const [backEnd, frontEnd] = arr;
console.log(backEnd); // Node.js
console.log(frontEnd); // React
Q. 배열 구조 분해 할당을 이용해 변수의 값을 서로 바꿀 수 있을까?
A. 일반적인 변수의 값을 변경할 때에는 임시 변수가 필요하지만
배열 구조 분해 할당을 이용해 변수의 값을 직관적으로 변경할 수 있다.
// 임시변수를 이용한 변수의 값 변경
let a = 1;
let b = 99;
let temp;
temp = a;
a = b;
b = temp;
console.log(a); // 99
console.log(b); // 1
// 배열 구조 분해 할당을 이용한 변수의 값 변경
let a = 1;
let b = 99;
[a, b] = [b, a];
console.log(a); // 99
console.log(b); // 1
에러 핸들링(Error handling)
- 에러를 관리하는 방법, 예상치 못한 상황에 대처하는 방식
- 에러는 예상할 수 있는 에러와 예상치 못한 에러로 구분할 수 있는데
- 일반적인 어플리케이션을 설계할 때 예상치 못한 에러 상황이 많다고 가정해야 한다.
- 작성한 코드에서 예상치 못한 에러가 일어날 가능성은 언제나 존재
- 이러한 에러상황을 대비해 언제든지 처리할 수 있어야 한다.
try / catch
- 서버에서 에러가 발생하지 않게 하기 위해서 예외처리를 진행한다.
- 예외 처리는 일반적으로 try ... catch 문을 사용하는데
- users에 들어있는 이름들을 String.toUpperCase()를 이용하여 대문자로
- 변경하려할 때 문자열(String)이 아닌 데이터가 들어온다면 에러가 발생한다.
이와같이 예상치 못한 에러에 대처하기 위해선 try ... catch문으로 코드 전체를 감싸
에러가 발생하더라고 프로그램이 멈추지 않고 에러를 기록할 수 있다.
const users = ["Lee", "Kim", "Park", 2];
try {
for (const user of users) {
console.log(user.toUpperCase());
}
} catch (err) {
console.error(`Error: ${err.message}`);
}
// LEE
// KIM
// PARK
// Error: user.toUpperCase is not a function
throw
프로그래머 입장에서는 에러를 고의로 발생시키기도 해야하는데
예를 들어 은행 어플리케이션의 현금 인출 서비스를 만든다고 할 때,
계좌의 잔고가 요청받은 금액보다 적다면 현금 인출을 막고 예외를
발생시켜야 한다.
이때 사용하는것이 throw 인데 호출 즉시 현재 실행되고 있는 함수는 실행을 멈춘다.
function withdraw(amount, account) {
if (amount > account.balance)
throw new Error("잔고가 부족합니다.");
account.balance -= amount;
console.log(`현재 잔고가 ${account.balance}남았습니다.`);
}// 출력되지 않는다.
const account = { balance: 1000 };
withdraw(2000, account);
// Error: 잔고가 부족합니다.
finally
try에서는 HTTP연결이 되고 있거나 파일과 같은 특정한 '자원'을 가지고 처리할 때가 있는데
해당 '자원'을 계속 가지고 있으면, 무의미한 메모리를 차지하므로 에러 여부와 상관없이
일정 시점에서는 해당 '자원'을 삭제 해야한다.
이러한 상황에서는 try, catch가 아닌 finally가 필요
function errorException(isThrow) {
try {
console.log("자원을 할당하였습니다.");
if (isThrow) throw new Error();
} catch (error) {
console.log("에러가 발생했습니다.");
} finally {
console.log("자원을 제거하였습니다.");
}
}
errorException(false);
// 자원을 할당하였습니다.
// 자원을 제거하였습니다.
errorException(true);
// 자원을 할당하였습니다.
// 에러가 발생했습니다.
// 자원을 제거하였습니다.
클래스(Class)
- 정보를 묶는것.
- 현실과 비슷한 개념(객체)을 나타내기 위한 도구
- 미리 정의해놓으면 필요할 때마다 해당 클래스로 동일한 틀을 가진 객체를 만들 수 있다.
- 이때 동일한 클래스를 이용해 생성한 객체를 인스턴스(Instance)라고 부른다.
클래스 연습
class User {}
const user = new User();
user.name = "홍길동";
user.age = 28;
user.tech = "Node.js";
console.log(user.name); // 홍길동
console.log(user.age); // 28
console.log(user.tech); // Node.js
생성자(Constructor)
- 클래스 내부에서 constructor()로 정의한 메서드를 "생성자"라고 한다.
- 미리 정의한 클래스를 기반으로 인스턴스를 생성할 때 JavaScript 내부에서
- 호출되는 메소드이다.
// User 생성자
class User {
constructor(name, age, tech) {
// User 클래스의 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
}
const user = new User("홍길동", 28, "Node.js"); // user 인스턴스 생성
console.log(user.name); // 홍길동
console.log(user.age); // 28
console.log(user.tech); // Node.js
this와 프로퍼티(Property)
- this라고 표시함으로써 클래스 전체의 값을 바꾸는게 아니라 하나의 매개변수의
- 값만 바꾸는 것.
생성자의 바디에서 this 키워드를 사용한다. 이 this는 클래스를 사용해 만들어 질
객체 자신을 의미하고 this 뒤에 붙는 name, age, tech는 클래스를 이용해서
만들어질 객체의 속성(Property)이다.
// User 생성자
class User {
constructor(name, age, tech) {
// User 클래스의 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
}
const user = new User("홍길동", 28, "Node.js"); // user 인스턴스 생성
console.log(user.name); // 홍길동
console.log(user.age); // 28
console.log(user.tech); // Node.js
생성자를 이용해 name, age, tech 인자값을 입력받아 class 내부변수에 저장한다.
메소드(Method)
- JavaScript에서 사용할 수 있는 모든 값은 프로퍼티 값으로 사용할 수 있는데
- 프로퍼티 값이 함수일 경우에는 일반 함수와 구분하기 위해 메소드(Method)라 한다.
=> 메소드는 객체(Object)에 묶여 있는 함수를 의미
클래스에서도 데이터를 나타내는 속성뿐만 아니라 함수와 같이 특정 코드를 실행하는
문법을 사용할 수 있는데 이때는 Class라는 객체(Object)에 묶여있는 함수를
메소드(Method)라고 부른다.
class User {
constructor(name, age, tech) {
// User 클래스의 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
getName() {
return this.name;
} // getName 메서드
getAge() {
return this.age;
} // getAge 메서드
getTech() {
return this.tech;
} // getTech 메서드
}
const user = new User("홍길동", 28, "Node.js"); // user 인스턴스 생성
console.log(user.getName()); // 홍길동
console.log(user.getAge()); // 28
console.log(user.getTech()); // Node.js
상속
일반적으로 클래스의 인스턴스는 선언한 클래스의 기능을 모두 상속한다.
이를 이용해 부모 클래스와 자식 클래스로 나뉠 수 있는데 부모 클래스의 경우
메소드, 내부 변수와 같은 정보를 자식 클래스에게 할당해줄 수 있다.
상속을 이용한 자식 클래스 만들어보기
우선 User 클래스를 정의하고 Employee 라는 이름의 새로운 클래스가 User를 상속한다
생성자 내부의 super() 는 생성자내에서만, 그리고 this 키워드를 사용하기 전에만 쓸 수 있다.
Employee를 User의 서브 클래스로 정의
User의 자식 클래스인 Employee에서 User.getTech() 메소드를 호출한다.
class User {
// User 부모 클래스
constructor(name, age, tech) {
// 부모 클래스 생성자
this.name = name;
this.age = age;
this.tech = tech;
}
getTech() {
return this.tech;
} // 부모 클래스 getTech 메서드
}
class Employee extends User {
// Employee 자식 클래스
constructor(name, age, tech) {
// 자식 클래스 생성자
super(name, age, tech);
}
}
const employee = new Employee("홍길동", 28, "Node.js");
console.log(employee.name); // 홍길동
console.log(employee.age); // 28
console.log(employee.getTech()); // 부모 클래스의 getTech 메소드 호출: Node.js
super 키워드
함수처럼 호출할 수도 있고 this와 같이 식별자 처럼 참조할 수 있는 키워드
키워드를 호출하면 부모 클래스의 생성자를 호출하고
키워드를 참조하면 부모 클래스의 메소드를 호출한다.
호이스팅(Hoisting)
JavaScript가 코드를 읽어올 때 각 Scope(구역)에 들어있는 var, function과 같은
키워드로 선언된 코드는 예시와 같이 동작한다.
// 예시
console.log(name) // Print: undefined
var name = '홍길동'
예시코드를 실행했을때 선언되지 않은 변수를 미리 참조 했기 때문에 첫번째 줄에서
참조 에러가 발생해야 하는데 실제로는 발생하지 않았다.
Hoisting이라는 동작 때문인데 var, function키워드로 선언하면 예시와 같이 동작한다.
var name;
console.log(name); // Print: undefined
name = "홍길동";
이렇게 선언된 변수나 함수가 함수의 스코프 최상단으로 끌어올려진다는 의미
var, function 뿐만 아니라 let, const, class도 내부적으로는 모두 호스팅되지만
다른 자료에서 let, const가 호이스팅되지 않는다는 이유는 JavaScript 내부에는
TDZ라는 영역(개념)이 존재하며 TDZ 영역에 존재하는 변수는 접근이 불가능한데,
실제 변수 선언이 실행되는 구문이 실행되기 전에는 const, let, class로 선언된 이름은
모두 TDZ에 존재하여 참조 에러를 발생시키는것.
참고자료
Don't Use JavaScript Variables Without Knowing Temporal Dead Zone
JavaScript의 let과 const, 그리고 TDZ
이해가 어렵다면 var키워드를 사용하면 호이스팅이 일어나 좋지않다. 정도까지만 이해하는게 좋다
TDZ의 보호를 받고 있지 않은 키워드인 var를 사용하는것은 변수의 값이 언제 바뀔지도 모르며
스파게티 코드를 만드는 주범이 될 수 있으므로 절대로 지양해야한다.
Quiz
요구사항에 맞는 클래스를 완성해보기
- Unit 클래스를 만든다.
- 클래스 생성자에서 내부변수 name, hp를 정의한다
- healing, damaged 메소드를 정의한다.
- healing 메소드는 hp를 올릴 수 있고 hp가 100이 넘을 경우 더이상 회복되지 않는다.
- damaged 메소드는 hp를 감소 시킬 수 있고 hp가 0이 넘을 경우 더이상 감수되지 않는다.
- hp가 0이 되었을 경우 더이상 healing 메서드와 damaged 메소드가 동작하지 않는다.
class Unit {
constructor(name, hp) {
this.name = name;
this.hp = hp;
}
healing(heal) {
if (this.hp <= 0) return;
this.hp += heal;
if (this.hp >= 100) this.hp = 100;
}
damaged(damage) {
if (this.hp <= 0) return;
this.hp -= damage;
if (this.hp <= 0) this.hp = 0;
}
}
const unit = new Unit("유닛", 100);
unit.damaged(70); // 30
unit.healing(10); // 40
unit.damaged(50); // 0
unit.healing(100); // 0
더 알아보면 좋을것 !
동기와 비동기의 차이
super, prototype이 필요한 경우
클로저 ?
참고할만한 추가자료
'Language > Javascript' 카테고리의 다른 글
06-1 객체의 기본 (0) | 2022.10.05 |
---|---|
ES의 의미와 ES5,ES6 차이점 (0) | 2022.10.02 |
JavaScript 기초 2 (0) | 2022.09.30 |
구조 분해 할당 (0) | 2022.09.30 |
함수 (0) | 2022.09.30 |