본문 바로가기
학원에서 배운 것/JavaScript

KDT 5th 웹개발자 입문 수업 13일차

by 쿠리의일상 2023. 2. 13.

 

 

1. 배열 array

순서가 있는 리스트

[ ] 안에 넣기

배열은 문자 뿐만 아니라 숫자, 객체, 함수 등도 포함이 가능 -> 다른 언어와의 차이점

 

let arr = [ 1, '2', true, null, undefined ]; // 배열의 선언

 

1-1. 배열의 접근

배열명[인덱스]

 

1-2. 배열값 변경

배열명[인덱스] = 값

 

1-3. length : 배열의 길이(개수)

즉, 배열의 마지막 인덱스는 arr.length - 1이 된다. 

 

1-4. 배열의 마지막에 추가 : push()

* 배열의 길이를 리턴해준다.

 

1-5. 배열의 마지막 제거 : pop()

* 제거 해주는 배열의 값을 리턴해준다.

 

1-6. 배열의 앞에 추가 : unshift()

* 배열의 길이 리턴

 

1-7. 배열의 앞에 삭제 : shift()

* 제거 해주는 배열의 값 리턴

 

1-8. 배열의 반복문

일반 for 문

for (let idx = 0; idx < arr.length; idx++) {
	//코드 실행 블럭
}

for...of 반복문

for (let element of array) {
	// 코드 실행
}

2. 객체 Object

키와 값으로 이루어진 정보의 집합체

{ } 안에 넣기

 

2-1. 객체의 선언

let superman = {

  name : 'clark',

  age : 33,

  // 키 : 값,

}

 

2-2. 객체 키로 값에 접근하기

객체명.키명

객체명['키명']

* 객체를 [ ] 를 사용하여 키에 접근하고 싶다면 꼭 문자열로 키명을 입력해줘야 한다

 

2-3. 객체의 키와 값 추가

객체명.키명 = 값;

객체명['키명'] = 값;

 

2-4. 객체의 키(값) 삭제

delete 객체명.키명;

delete 객체명['키명'];

 

2-5. 객체의 프로퍼티 존재 여부 확인

없는 키에 접근하려고 하면 undefined가 리턴된다.

객체명.없는키명; ---> undefined

in 연산자를 사용하면 키가 없다면 false, 있다면 true

'키명' in 객체명;

 

2-6. for...in 반복문

객체의 키에 접근하여 반복 실행

for(let key in obj) {
	//코드 실행
}

obj의 각 key를 문자열로 반환하므로 obj[key] 로 접근해야 값을 접근할 수 있다.

 

obj['key'] --- X, 'key'라는 이름의 키에 접근하게 됨

obj.key --- X, 위와 동일

두 가지 접근법은 key라는 이름의 키명이 존재하지 않는 한, 결국 undefined를 리턴한다.

 


3. 객체 메서드 Object Method

객체의 프로퍼티로 할당된 함수를 메서드라고 한다.

 

3-1. 메서드의 선언

let obj = {
	run : function() { },
    	fly() { }
}

메서드명 : function() { } 또는 축약형인 메서드명() { } 으로 메서드 선언 가능

 

3-2. 메서드 호출

객체명.메서드명();

 

3-3. this

const user = {
	name : 'Mike',
    sayHello : function() {
    	console.log(`Hello, I'm ${user.name}`); // ?
    }
}

 

const user = {
	name : 'Mike',
    sayHello : function() {
    	console.log(`Hello, I'm ${this.name}`);
    }
}

user.sayHello();

 

3-3-1. 일반 함수와 this

일반 함수는 만들어질 때 자동적으로 this를 가지게 된다.

function sayHello() {
  console.log(`Hello, I'm ${this.name}`);
}

let boy = {
  name : 'Mike',
  sayHello,
}
let girl = {
  name : 'Jane',
  sayHello,
}
boy.sayHello(); //Hello, I'm Mike
girl.sayHello(); //Hello, I'm Jane
sayHello(); //Hello, I'm undefined(Window)

 

3-3-2. 화살표 함수와 this

* 화살표 함수의 경우 this가 정의되지 않기 때문에(메모리상 효율적임) 상위 스코프인 전역 객체를 가리킨다.

그러므로 원하지 않은 값을 도출하지 않기 위해 화살표 함수 안에 this는 지양한다.

let sayHello = () => {
  console.log(`Hello, ${this.name} 입니다.`);
  console.log(this);
}

let sayHallo = function() {
  console.log(`Hallo, ${this.name} 입니다.`);
  console.log(this);
}

const boy = {
  name : "Mike",
  sayHello,
}
const girl = {
  name : "Jane",
  sayHallo,
}

 


4. 객체의 불변성

4-1. 원시 Primitive 타입

메모리 주소에 바로 값이 들어있는 구조

 

4-2. 비원시 타입, 즉 객체 Object 타입

객체, 배열, 함수... 원시 타입을 제외한 모든 타입

메모리 주소에 또 실제적인 값이 들어 있는 메모리 주소가 또 들어있는 구조 

 

const obj = {
  name : '아무개',
}
const copyObj = obj;
copyObj.name = '아무개 카피';

console.log(obj); //{ name: '아무개 카피' }
console.log(copyObj); //{ name: '아무개 카피' }
console.log(obj === copyObj); //true

즉 객체를 복사하면 객체의 시작 메모리가 전달되어 복사된 객체와 원본 객체는 동일한 객체를 가리키게 된다.

그래서 복사한 객체를 변경하면 원본 객체도 변경된다.

 

객체 사이의 값만을 비교하고 싶을 때,

console.log(JSON.stringify(obj) === JSON.stringify(copyObj));

JSON.stringify(객체명) 을 사용하여 JSON 형태로 변경시켜서 값을 비교해준다.

 


5. Constructor 생성자 함수

function User(name, age) {
	this.name = name;
    this.age = age;
    this.sayName = function() {
    	console.log(this.name);
    }
}

let user = new User('Mike', 30);
user.sayName();

생성자 함수의 첫 글자는 대문자로 (약속!)

new 연산자를 사용해서 생성자 함수를 호출

 

객체를 만들 때마다 간편하게 만들 수 있게 틀을 만들어 주는 기능이다.

 


6. Class (Syntactic sugar!)

객체 지향 프로그래밍(OOP)에서 특정 객체를 생성하기 위해 변수와 메서드를 정의하는 일종의 틀이다.

클래스를 이용하여 실제 객체를 만드는 과정을 인스턴스화라고 한다.

 

생성자 함수를 좀더 사용하기 편하도록 약간의 기능을 더한 것이 class이다.

// 생성자 함수로 객체 만들기
function Car(brand, color) {
  this.brand = brand;
  this.color = color;
  this.drive = () => {
    console.log(`${this.brand}의 ${this.color} 색 차가 주행 중입니다.`);
  }
}
const hyundai = new Car('hyundai', 'white');
hyundai.drive();


// 클래스로 객체 만들기, 인스턴스화
class ClassCar {
	constructor(brand, color) {
    	this.brand = brand;
        this.color = color;
    }
    
    drive() {
    	console.log(`${this.brand}의 ${this.color} 색 차가 주행 중입니다.`);
    }
}
const porsche = new ClassCar('porsche', 'red');
porsche.drive();

 

function 이 아닌, class 키워드를 사용하며 class 안에 constructor 함수에 매개변수를 써주는 것으로 생성자 함수에서 썼던 방법처럼 사용 해준다. 생성자 함수의 경우 메서드의 경우에도 this를 사용하여 써주지만

class의 경우 메서드는 function이 없는 함수 선언으로 만들어 준다.

 

생성자 함수의 경우 함수가 보이고, 클래스의 경우 함수가 숨겨져 있다.

 

 

생성자 함수와 class, 사용법은 거의 동일하지만 사용하는 이유는?

 

1. 암묵적으로 strict mode 가 class에선 기본적으로 실행된다. (문법적으로 안정)

2. ✨ 상속이 편리하다. ✨

  -> 생성자 함수의 경우 프로토 타입 체인으로 상속

  -> 클래스의 경우 extends 키워드로 상속

3. 정적 메서드 사용 방식이 다르다.

  -> 생성자 함수는 함수 몸체에 직접 추가

  -> 클래스는 static 키워드 사용

 

6-1. Strict mode

생성자 함수는 선언 전에 사용되어도, 호이스팅이 되므로 가능하다.

하지만 class는 호이스팅이 되지 않으므로 선언 전에 사용이 불가능하다.

 

 

6-2. 상속

기존의 클래스(틀)을 토대로 다른 클래스(틀)을 만드는 것

 

기존의 클래스 변수, 메서드 등을 그대로 가져가면서 자신이 원하는 변수, 메서드를 추가 또는 변경하여 사용

class Car {
  constructor(brand, color) {
    this.brand = brand;
    this.color = color;
  }

  drive() {
    console.log(`이 차는 ${this.brand}의 ${this.color}색입니다.`);
  }
}

class ElecCar extends Car {
  constructor(brand, color, fuel) {
    super(brand, color);
    this.fuel = fuel;
  }
    // 오버라이딩
  drive() {
    console.log(`이 차는 ${this.brand}의 ${this.color}색이며, ${this.fuel}로 움직입니다.`);
  }

  showFuel() {
    console.log(`이 차는 ${this.fuel}의 동력으로 움직입니다.`);
  }
}

const tesla = new ElecCar('tesla', 'red', 'electricity');
tesla.drive();
tesla.showFuel();

부모 요소의 것을 그대로 받아올 때는 super를 사용

 

6-3. Overriding

부모 클래스의 메서드를 그대로 사용하고 싶지 않을 때 사용

상속 받은 부모 클래스의 메서드를 자식 클래스에서 임의로 변경하여 사용하는 것

class Shape {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height; 
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super(width, height);
  }
}

class Triangle extends Shape {
  constructor(width, height) {
    super(width, height);
  }
  getArea() {
    return this.width * this.height / 2;
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }
  getArea() {
    return Math.PI * Math.pow(this.radius, 2);
  }
}

 

6-4. super

부모 클래스의 프로퍼티를 자식 클래스에서 접근하고 싶을 때 사용, 필요없는 값이라도 super() 를 선언해줘야 함

부모 메서드에 접근할 때 : super.메서드명()

부모 생성자에 접근할 때 : super(constructor)

 

class Car {
  constructor(brand, color) {
    this.brand = brand;
    this.color = color;
  }

  showSpec() {
    console.log(`이 차는 ${this.brand}의 ${this.color}색입니다.`);
  }
}

class ElecCar extends Car {
  constructor(brand, color, fuel) {
    super(brand, color);
    this.fuel = fuel;
  }

  showSpec() {
    super.showSpec();
    console.log(`이 차는 ${this.brand}의 ${this.color}색이며, ${this.fuel}로 움직입니다.`);
  }

  showFuel() {
    console.log(`이 차는 ${this.fuel}의 동력으로 움직입니다.`);
  }
}

const tesla = new ElecCar('tesla', 'red', 'electricity');
tesla.showSpec();

 

7. instanceof

특정 Object가 해당 클래스를 통해 만들어졌는지 여부를 알아보는 명령어

식별자명 instanceof 클래스명 ---> true/false

 

부모 클래스를 상속 받은 자식 클래스의 인스턴스를 instanceof를 사용한다면

 

자식인스턴스 instanceof 부모클래스 ===> true 가 리턴된다!

* 식별자 instanceof Object 의 경우 true이다. 최상위 객체이므로.


8. 생성자 함수의 상속 (~ES5)

클래스 문법이 도입된 이후 거의 사용되고 있지 않지만 legacy 시스템을 유지 보수할 경우,

생성자 함수에서 상속을 구현하기 위해선 prototype이라는 객체를 사용

function Car(brand, color) {
  this.brand = brand;
  this.color = color;
  this.drive = function() {
    console.log(`${this.brand}의 ${this.color}색 자동차`);
  }
}

function ElecCar(brand, color, fuel) {
  Car.call(this, brand, color);
  this.fuel = fuel;
  this.drive = function() {
    console.log(`${this.brand}의 ${this.color}색 자동차가 ${this.fuel}로 달립니다.`);
  }
}

ElecCar.prototype = Object.create(Car.prototype);
ElecCar.prototype.constructor = ElecCar;

1. 자식클래스의 prototype을 부모 클래스의 prototype을 Object.create() 로 새로 만들어줘서 넣어주고

  -> 상속 받을 자식클래스의 프로토 타입을 Object 객체를 사용하여 부모 클래스의 프로토 타입과 동일하게

2. 자식클래스의 prototype.constructor에 자식 클래스를 다시 넣어줘야 한다.

  -> 상속 받을 자식 클래스의 생성자는 위에 선언한 생성자 함수의 것을 따르도록 설정해줘야한다.

 

그냥 ... 무슨 일 없다면, 클래스를 써서 상속을 하자.