(1) DOM의 기본 개념
속성과 메서드를 가지고 있음.속성은 해당 객체의 특성을 나타내는 값을 가져오거나 설정하는데 사용, 메서드는 해당 객체가 수행하는 작업을 나타내는 함수.(2) DOM에 접근 및 제어해보기
클래스(Class)는 설계도. 특징(변수 또는 속성과 메서드)이 무엇인지 알 수 있음.
인스턴스(Instance)는 설계도를 보고 만들어진 실체물건.
(1) Class란?
(2) Constructor란?
(2)-1 클래스 연습해보기
<Car 라는 새로운 클래스를 만들되, 처음 객체를 만들 때는 modelName, modelYear, type, price 값이 필수로 입력돼야 함!, makeNoise() 메서드를 만들어 클락션을 출력해주세요>
// 새로운 Car class 정의
class Car {
constructor(modelName, modelYear, type, price) {
this.modelName = modelName;
this.modelYear = modelYear;
this.type = type;
this.price = price;
}
// 클락션을 울리는 메서드
makeNoise() {
console.log(`${this.modelName}: 빵!`);
}
}
// 자동차 만들기
const car1 = new Car("Sorento", "2023", "e", 5000);
const car2 = new Car("SM5", "1999", "g", 3000);
const car3 = new Car("QM6", "2010", "g", 4500);
car1.makeNoise();
car2.makeNoise();
car3.makeNoise();
(3) Getter와 Setter
// Getters와 Setters
// 객체지향 프로그래밍 언어 -> G, S
// 클래스 --> 객체(인스턴스)
// 프로퍼티(constructor)
// new Class(a, b, c)
class Rectangle {
constructor(height, width) {
// underscore : private(은밀하고, 감춰야 할 때)
this._height = height;
this._width = width;
}
// width를 위한 getter
get width() {
return this._width;
}
// width를 위한 setter
set width(value) {
// 검증 1 : value가 음수이면 오류!
if (value <= 0) {
//
console.log("[오류] 가로길이는 0보다 커야 합니다!");
return;
} else if (typeof value !== "number") {
console.log("[오류] 가로길이로 입력된 값이 숫자타입이 아닙니다!");
return;
}
this._width = value;
}
// height를 위한 getter
get height() {
return this._height;
}
// height를 위한 setter
set height(value) {
// 검증 1 : value가 음수이면 오류!
if (value <= 0) {
//
console.log("[오류] 세로길이는 0보다 커야 합니다!");
return;
} else if (typeof value !== "number") {
console.log("[오류] 세로길이로 입력된 값이 숫자타입이 아닙니다!");
return;
}
this._height = value;
}
// getArea : 가로 * 세로 => 넓이
getArea() {
const a = this._width * this._height;
console.log(`넓이는 => ${a}입니다.`);
}
}
// instance 생성
const rect1 = new Rectangle(10, 7);
rect1.getArea();
// const rect2 = new Rectangle(10, 30);
// const rect3 = new Rectangle(15, 20);
=> this로 접근하는 property에 underscore(_)를 사용하지 않으면 무한루프에 빠지게 됨.
(3)-1 Getter와 Setter 연습해보기
// 클래스 연습해보기!
// [요구사항]
// 1. Car라는 새로운 클래스를 만들되, 처음 객체를 만들 때는
// 다음 네 개의 값이 필수로 입력돼야 합니다!
// (1) modelName
// (2) modelYear
// (3) type : 가솔린, 전기차, 디젤
// (4) price
// 2. makeNoise() 메서드를 만들어 클락션을 출력해주세요.
// 2-1. 해당 자동차가 몇년도 모델인지 출력하는 메서드 작성!
// 3. 이후 자동차를 3개 정도 만들어주세요(객체 생성)
// [추가 요구사항]
// 1. modelName, modelYear, price, type을 가져오는 메서드
// 2. modelName, modelYear, price, type을 세팅하는 메서드
// 3. 만든 인스턴스를 통해서 마지막에 set 해서 get 하는 로직까지
class Car {
constructor(modelName, modelYear, type, price) {
this._modelName = modelName;
this._modelYear = modelYear;
this._type = type;
this._price = price;
}
get modelName() {
return this._modelName;
}
// 입력값에 대한 검증까지 가능하다
set modelName(value) {
// 유효성 검사
if (value.length <= 0) {
console.log("[오류] 모델명이 입력되지 않았습니다. 확인해주세요!");
return;
} else if (typeof value !== "string") {
console.log("[오류] 입력된 모델명이 문자형이 아닙니다!");
return;
}
// 검증이 완료된 경우에만 setting!
this._modelName = value;
}
get modelYear() {
return this._modelYear;
}
set modelYear(value) {
// 유효성 검사
if (value.length !== 4) {
// 연도에 대한 유효성 검증 로직 ---> googling 엄청~~~~많이 나옵니다!!
console.log("[오류] 입력된 년도가 4자리가 아닙니다.확인해주세요!");
return;
} else if (typeof value !== "string") {
console.log("[오류] 입력된 모델명이 문자형이 아닙니다!");
return;
}
// 검증이 완료된 경우에만 setting!
this._modelYear = value;
}
get type() {
return this._type;
}
set type(value) {
if (value.length <= 0) {
console.log("[오류] 타입이 입력되지 않았습니다. 확인해주세요!");
return;
} else if (value !== "g" && value !== "d" && value !== "e") {
// g(가솔린), d(디젤), e(전기차)가 아닌 경우 오류
console.log("[오류] 입력된 타입이 잘못되었습니다. 확인해주세요!");
return;
}
// 검증 완료!
this._type = value;
}
get price() {
return this._price;
}
set price(value) {
if (typeof value !== "number") {
console.log("[오류] 가격으로 입력된 값이 숫자가 아닙니다. 확인해주세요!");
return;
} else if (value < "1000000") {
console.log("[오류] 가격은 100만원보다 작을 수 없습니다. 확인해주세요!");
return;
}
// 검증이 완료된 경우
this._price = value;
}
// 클락션을 울리는 메서드
makeNoise() {
console.log(this._modelName + ": 빵!");
}
// 해당 자동차가 몇년도 모델인지 출력하는 메서드 작성!
printModelYear() {
console.log(
this._modelName + "은 " + this._modelYear + "년도의 모델입니다."
);
}
}
// 자동차 만들기
const car1 = new Car("Sorento", "2023", "e", 5000);
const car2 = new Car("SM5", "1999", "g", 3000);
const car3 = new Car("Palisade", "2010", "d", 4500);
// car1.makeNoise();
// car1.printModelYear();
// car2.makeNoise();
// car2.printModelYear();
// car3.makeNoise();
// car3.printModelYear();
// getter 예시1
console.log(car1.modelName);
// setter 예시1
car1.modelName = 1;
console.log(car1.modelName);
(4) 상속(Inheritance)
// 동물 전체에 대한 클래스에요
class Animal {
// 이름을 필수로 받아야 해요
constructor(name) {
this.name = name;
}
// 동물의 행동을 정의하는 메소드
speak() {
console.log(`${this.name} makes a noise.`);
}
}
// 동물 클래스를 상속받는 Dog 클래스를 만들어요
class Dog extends Animal {
// 상속받을 때, speak()를 입맛에 맞게 재정의해요.
speak() {
console.log(`${this.name} barks.`);
}
}
// Dog를 만들 때는 Animal의 상속을 받은 class이기 때문에 이름을 필수로
// 받아야 해요!
let d = new Dog('Mitzie');
// speak는 'makes a noise'가 아니라, 'barks'가 출력되네요.
d.speak(); // "Mitzie barks."
=> 위 코드에서 Dog Class는 Animal Class를 상속받음. Dog Class에서 speak 메서드를 오버라이딩하여 Animal Class의 speak 메서드를 덮어씀.
(4)-1 상속(Inheritance) 연습해보기
// 새로운 Car class 정의
class Car {
constructor(modelName, modelYear, type, price) {
this.modelName = modelName;
this.modelYear = modelYear;
this.type = type;
this.price = price;
}
// 모델명 출력하는 메서드
makeNoise() {
console.log(`${this.modelName}: 빵!`);
}
// getters
getModelName() {
return this.modelName;
}
getModelYear() {
return this.modelYear;
}
getPrice() {
return this.price;
}
// 새로운 가격 반영
setPrice(newPrice) {
this.price = newPrice;
}
}
// 자동차 만들기
const car1 = new Car("Sorento", "2023", "e", 5000);
const car2 = new Car("SM5", "1999", "g", 3000);
const car3 = new Car("QM6", "2010", "g", 4500);
car1.makeNoise();
car2.makeNoise();
car3.makeNoise();
console.log(car1.getModelName());
console.log(car1.getModelYear());
console.log(car1.getPrice());
// 가격변동
car1.setPrice(6000);
console.log(car1.getPrice());
// 아래 부분이 상속에 관련된 코드에요!
class ElectronicCar extends Car {
constructor(modelName, modelYear, price) {
// Car에 있는 constructor를 통해 자동차를 만들었어요!
super(modelName, modelYear, "e", price);
}
}
const eleCar1 = new ElectronicCar("뉴아이오닉5", "2023", 7000);
console.log(eleCar1.getModelName());
=> 전기차이기 때문에 constructor 상에 type은 필요없음!
(5) Static Method
class Calculator {
static add(a, b) {
return a + b;
}
static subtract(a, b) {
return a - b;
}
}
console.log(Calculator.add(1, 2)); // 3
console.log(Calculator.subtract(3, 2)); // 1
=> 위 코드에서 Calculator Class는 add와 Subtract 메소드를 정의함. 두 메소드 모두 Class 레벨에서 호출할 수 있으며, 인스턴스에서 호출할 수 없음.
(1) 클로저의 개념(정의)
const x = 1;
function outerFunc() {
const x = 10;
function innerFunc() {
console.log(x); // 10
}
innerFunc();
}
outerFunc();
=> [설명]
console.log(x)에서 참조하고 있는 x 값은scope chain에 의해 바로 바깥쪽 scope를 찾는다.outer를 찾는 것!LexicalEnvironment를 갖고 있음.console.log(x)는 10이 출력됨.const x = 1;
// innerFunc()에서는 outerFunc()의 x에 접근할 수 없죠.
// Lexical Scope를 따르는 프로그래밍 언어이기 때문
function outerFunc() {
const x = 10;
innerFunc(); // 1
}
function innerFunc() {
console.log(x); // 1
}
outerFunc();
=> outerFunc와 innerFunc는 서로 다른 scope를 가지고 있음. 따라서 변수 공유 불가!
=> InnerFunc함수를 OuterFunc함수의 내부에서 호출한다 하더라도 outerFunc함수의 변수에 접근할 수 없음!
함수 정의가 평가되는 시점에 함수가 정의 된 환경(위치)에 의해 결정됨. ('어디서 호출했는지'가 아님)(여전히) 참조할 수 있다. ← 이 개념에서 중첩 함수가 바로 클로저.const x = 1;
// 1
function outer() {
const x = 10;
const inner = function () {
console.log(x);
};
return inner;
}
const innerFunc = outer();
innerFunc();
=>
outer 함수를 호출하면 중첩 함수 inner를 반환(return).(2) 클로저의 활용
은닉한다(특정 함수에게만 상태 변경을 허용)<예제>
// 카운트 상태 변경 함수 #1
// 함수가 호출될 때마다 호출된 횟수를 누적하여 출력하는 카운터를 구현해요!
// 카운트 상태 변수
let num = 0;
// 카운트 상태 변경 함수
const increase = function () {
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
};
console.log(increase());
// num = 100; // 치명적인 단점이 있어요.
console.log(increase());
console.log(increase());
=> [보완해야 할 사항]
// 카운트 상태 변경 함수 #2
const increase = function () {
// 카운트 상태 변수
let num = 0;
// 카운트 상태를 1만큼 증가시킨다.
return ++num;
};
// 이전 상태값을 유지 못함
console.log(increase()); //1
console.log(increase()); //1
console.log(increase()); //1
=> [리뷰]
// 카운트 상태 변경 함수 #3
const increase = (function () {
// 카운트 상태 변수
let num = 0;
// 클로저
return function () {
return ++num;
};
})();
// 이전 상태값을 유지
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3
=> [코드설명]
결론!! 클로저는 상태(state)가 의도치 않게 변경되지 않도록 안전하게 은닉(information hiding) 하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용함.