1. DOM
- DOM의 기본 개념
- JS는 왜 만들어진 건가?
- 웹 페이지를 동적으로 만들기 위해
- HTML 문서를 조작해서 생명력을 넣기 위해 만들어진 언어
- 웹 페이지가 동작하는 과정
- 클라이언트가 브라우저에 주소를 입력한다.
- HTML 문서를 서버로부터 수신
- 서버가 클라이언트에게 response를 준다. (즉, response가 HTML 문서(document))
- 브라우저가 HTML 파일을 해석(parsing 파싱)
- 브라우저에는 기본적으로 렌더링 엔진이 있다. 서버가 클라이언트에게 준 HTML문서를 렌더링 한다.
- 랜더링 엔진이 HTML 문서에 코드를 해석한다.
- JS가 이해할 수 있는 방식으로 해석하고 내용을 토대로 DOM Tree를 구성
- DOM Tree랑 CSSOM Tree를 묶어서 Render Tree를 구성
- 렌더 트리는 HTML, CSS 및 JS문서를 파싱하여 브라우저에서 실제로 레더링되는 최종 문서 모델을 나타내는 객체의 계층 구조
- 결국, 브라우저 화면에 그리기 위한 최종 버전을 만들어낸다. 그리고 브라우저에 그림을 그리기 위한 레이아웃 계산 => 페인팅 과정이 시작
- DOM?
- Document(HTML 파일)를 JS가 알아먹을 수 있는 Object 형태롤 Modeling 한 것
- 브라우저에 기본적으로 내장되어 있는 API 중 하나
- DOM이 브라우저에 내장되어있기 때문에 우리는 HTML의 내용을 JS로 접근, 제어할 수 있다.
- 모든 DOM의 node들은 '속성'과 '메서드'를 갖고 있다.
- JS는 왜 만들어진 건가?
2. Class
- 클래스와 인스턴스의 일반적 개념
- 클래스는 설계도와 비슷하다. 설계도 내에 어떤 종류인지 특징이 무엇인지 알 수 있는데 클래스도 이러한 것들을 알 수 있게 해준다.
- 인스턴스는 설계도를 보고 만들어진 실제 결과물을 말한다. 클래스의 구성에 따라 다른 인스턴스가 된다.
- Class 란?
- class는 객체를 생성하기 위한 일종의 템플릿
- class 생성하기 위해 class 키워드를 사용
class Person {
// constructor는 이름을 변경할 수 없어요.
constructor(name, age) {
// 이름(name)과 나이(age)가 없으면 사람이 아니죠?
// new라는 키워드를 이용해서 인스턴스를 만들 때, 기본적으로
// 넣어야 하는 값들을 의미해요! :)
// 여기서 말하는 this는 만들어질 인스턴스를 의미한다고 생각해주세요!
this.name = name;
this.age = age;
}
// 다양한 메소드를 아래와 같이 정의할 수 있어요.
// 여기서 this.name으로 내부 값을 접근해야 함을 잊지 마세요! :)
sayHello() {
console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
}
}
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
// 만든 객체를 토대로 메서드 호출해보기
person1.sayHello(); // 출력: "Hello, my name is Alice and I am 30 years old."
person2.sayHello(); // 출력: "Hello, my name is Bob and I am 25 years old."
==> class: person / 속성: name, age / 메소드: sayHello
- Constructor 란?
- Class의 생성자 함수
- 생성자 함수는 객체를 생성할 때 호출되며, 객체를 초기화하는 역할을 한다.
// 새로운 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();
- Getter와 Setter
- 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);
- 상속(Inheritance)
- Class는 상속을 통해 다른 Class의 기능을 물려받을 수 있다.
- 상속을 받는 Class를 subclass 또는 derived class라고 하며, 상속을 하는 Class를 superclass 또는 base class라고 한다.
// 동물 전체에 대한 클래스에요
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 메소드를 오버라이딩하여 Animall Class의 speak 메소드를 덮어쓴다.
- Static Method
- Class 레벨의 메소드를 정의할 수 있다. Class 레벨의 메소드는 인스턴스에서 호출할 수 없으며, Class 이름으로 직접 호출할 수 있다.
- 인스턴스를 만들지 않고 사용할 수 있기 때문에 유틸리티 함수, 정적 속성인 경우 인스턴스 간에 복제할 필요가 없는 데이터를 만들 때 사용된다.
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레벨에서 호출할 수 있다.
3. Closure
- 클로저의 개념
- 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합
const x = 1;
// innerFunc()에서는 outerFunc()의 x에 접근할 수 없죠.
// Lexical Scope를 따르는 프로그래밍 언어이기 때문
function outerFunc() {
const x = 10;
innerFunc(); // 1
}
function innerFunc() {
console.log(x); // 1
}
outerFunc();
- 렉시컬 스코프
- JS 엔진은 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다.
- 저장할 참조값, 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다.
- 정의된 환경에 대한 정보를 저장하는 곳 : outer
- 호출되는 환경과는 상관없이 정의된 환경에 대한 정보를 LexicalEnvironment > outer에 기억한다.
const x = 1;
function foo() {
const x = 10;
// 상위 스코프는 함수 정의 환경(위치)에 따라 결정된다.
// 함수 호출 위치와 상위 스코프는 아무런 관계가 없다.
bar();
}
// 여기보세요 여기!
// 함수 bar는 자신의 상위 스코프, 즉 전역 렉시컬 환경을 저장하여
// 기억한다.
function bar() {
console.log(x);
}
foo();
bar();
- 클로저와 렉시컬 환경
- 외부 함수보다 중첩 함수가 더 오래 유지되는 경우, 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 여전히 참조할 수 있다. ==> 이 개념에서 중첩 함수가 바로 클로저이다.
const x = 1;
// 1
function outer() {
const x = 10;
const inner = function () {
console.log(x);
};
return inner;
}
const innerFunc = outer();
innerFunc();
==> outer 함수를 호출하면 중첩 함수 inner를 return / outer 함수의 실행 컨텍스트는 실행 컨텍스트 스탭에서 팝되어 제거된다. / inner함수는 런타임에 평가된다. / inner함수는 런타임에 평가된다. inner 함수가 innerFunc에 전달되었는데, 이는 outer 함수의 렉시컬환경을 여전히 참조하고 있다. 즉 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer 함수의 렉시컨 환경까지 소멸하는 것은 아니다.
- 클로저의 활용
- 클로저는 주로 상태를 안전하게 변경하고 유지하기 위해 사용한다. (의도치 않은 상태의 변경을 막기 위해서다.)
- 상태가 의도치 않게 변경되지 않도록 안전하게 은닉(information hiding)하고 특정 함수에게만 상태 변경을 허용한다. 안전하게 변경하고 유지하기 위해 사용
- 캡슐화(encapsulation)와 정보 은닉
- 캡슐화란?
- 프로퍼티와 메서드를 하나로 묶는 것
==> 프로퍼티 : 객체의 상태(state)
==> 메서드 : 프로퍼티를 참조하고 조작할 수 있는 동작
- 프로퍼티와 메서드를 하나로 묶는 것
- 캡슐화의 정보 은닉
- 객체의 상태 변경을 방지함으로써 정보 보호
- 객체 간의 의존성(결합도 - coupling)을 낮춤
- 캡슐화란?
'JavaScript Dev. > Javascript' 카테고리의 다른 글
JavaScript 정렬(sorting) 알고리즘 (0) | 2023.12.15 |
---|---|
JavaScript 자료구조 (0) | 2023.12.10 |
13. 콜백 함수와 동기/비동기 처리 (0) | 2023.10.23 |
12. this(정의, 활용 방법, 바인딩, call, apply, bind) (1) | 2023.10.19 |
11. 실행 컨텍스트(스코프, 변수, 객체, 호이스팅) (0) | 2023.10.11 |