{}
기호를 이용하여 객체를 생성할 수 있습니다.
const food = {
name: '피자',
price: 5000,
};
console.log(food);
console.log(
food1.name, // 마침표 프로퍼티 접근 연산자
food1['price'] // 대괄호 프로퍼티 접근 연산자
);
비어있는 객체를 생성 후 프로퍼티를 추가/수정할 수 있습니다.
// 빈 객체 생성
const food2 = {};
console.log(food2);
// 프로터피 추가
food2.name = '샐러드';
food2.price = '6000';
console.log(food2);
// 프로터피 수정
food2['price'] = '6500';
console.log(food2);
표현식(어떤 값을 반환하는 코드)으로 키값을 정의할 수 있습니다. 대괄호를 사용해야 합니다.
let idx = 0;
const obj = {
['key-' + ++idx]: `value-${idx}`,
['key-' + ++idx]: `value-${idx}`,
['key-' + ++idx]: `value-${idx}`,
[idx ** idx]: 'POWER'
}
console.log(obj);
프로퍼티를 삭제할 때, delete
연산자를 사용합니다.
const person1 = {
name: '홍길동',
age: 20,
};
delete person1.age;
console.log(person1);
객체 선언 시 프로퍼티의 키와 value에 들어갈 상수명 또는 변수명이 동일할 시 단축 표현이 가능합니다.
const x = 1, y = 2;
const obj1 = {
x: x,
y: y
}
console.log(obj1);
const obj2 = { x, y }
console.log(obj2);
메서드는 객체의 축약 표현으로 정의된 함수 프로퍼티를 말합니다. 따라서 이렇게 정의된 함수만 메서드라고 부릅니다.
// 일반 함수 프로퍼티 정의
const person = {
name: '홍길동',
state: function (formal) {
return formal
? `정답입니다.`
: `틀렸습니다.`;
}
}
console.log(person.state(true));
// 메서드로 정의
const person = {
name: '홍길동',
state (formal) {
return formal
? `정답입니다.`
: `틀렸습니다.`;
}
}
console.log(person.salutate(true));
같은 형식의 여러가지 객체를 만들 때, 효율적으로 객체를 생성하기 위해 생성자 함수를 이용합니다.
생성자 함수명은 대문자
로 시작합니다. 생성자 함수는 new
연산자와 함께 사용되며, 생성자 함수로 만들어진 객체를 인스턴스
라고 부릅니다.
생성자 함수는 객체가 아닌 함수이기 때문에 메서드 정의가 불가능합니다.
// 생성자 함수
function People (name, no) {
this.name = name;
this.no = no;
this.introduce = function () {
return `안녕하세요, ${this.no}호 ${this.name}입니다!`;
}
}
// 인스턴스 생성
const people1 = new People('영수', 1);
const people2 = new People('광수', 2);
const people3 = new People('영철', 3);
console.log(people1, people1.introduce());
console.log(people2, people2.introduce());
console.log(people3, people3.introduce());
function으로 선언된 함수는 기본적으로 생성자 함수의 기능을 갖습니다. new 키워드와 같이 사용하면 그 함수는 생성자 함수가 됩니다.
function doNothing() {};
console.log(new doNothing()); // doNothing {}
먼저, 생성자 함수로 만들어진 인스턴스들은 프로토타입
을 통해서 유기적으로 계속 연결되어있습니다.
생성자 함수에 함수를 추가해보겠습니다.
People.prototype.intro2 = function () {
return `감사합니다.`;
};
console.log(people1.intro2()); // 함수가 추가되기전에 만들어진 인스턴스지만, 새로운 함수 호출이 가능합니다.
아래 코드는 위의 People 생성자 함수처럼 똑같은 형태의 객체를 반환하는 함수입니다.
function getPeopleObj (name, no) {
return {
name, no,
introduce () {
return `안녕하세요, ${this.no}호 ${this.name}입니다!`;
}
}
}
const people4 = getPeopleObj('영수', 1);
사용성이 달라보이지 않는데 왜 굳이 생성자 함수를 만들어서 사용할까요?
생성자 함수로 만든 인스턴스 people1과 그냥 객체를 반환하는 함수로 만든 people4의 로그를 찍어보면 다른점을 찾을 수 있습니다.
인스턴스는 프로토타입의 constructor
체인이 해당 생성자 함수를 포함하고있습니다.
또, instanceof
키워드를 사용하여 특정 생성자 함수에 의해 만들어졌는지의 여부 반환도 다릅니다.
console.log(people1, people1 instanceof People); // true
console.log(people4, people4 instanceof People); // false
프로토타입 기반인 자바스크립트의 문법을 타 언어의 클래스와 비슷한 문법으로 포장하여 객체 지향 프로그래밍
을 지향하기 위한 문법적 설탕입니다. 클래스를 사용하면 객체를 생성하고 관리하는 방법이 더 직관적이고 간결해집니다.
클래스를 정의하기 위해서 class 키워드를 사용합니다.
class People {
constructor (name, no) {
this.name = name;
this.no = no;
}
introduce () { // 메서드
return `안녕하세요, ${this.no}호 ${this.name}입니다!`;
}
}
// 인스턴스 생성
const people1 = new People('영수', 1);
const people2 = new People('광수', 2);
const people3 = new People('영철', 3);
// 클래스는 함수
console.log(typeof people1); // function
클래스를 이용해서 새로운 객체를 만들 때 호출되는 특이한 메서드입니다. 이 메서드는 객체를 초기화시켜줍니다. 따라서 메서드 안에 객체의 초기 속성을 설정할 수 있습니다.
class Person {
constructor (name, age, married = false) {
this.name = name;
this.age = age;
this.married = married;
}
}
const person1 = new Person('영수', 30);
const person2 = new Person('광수', 31);
console.log(person1, person2);
constructor
이어야 합니다. 클래스 내부에 생성된 함수로, 인스턴스 생성 후 메서드를 호출합니다.
class Dog {
bark () {
return '멍멍';
}
}
const dog1 = new Dog();
console.log(dog1.bark());
constructor 밖에서 this 없이 인스턴스의 프로퍼티를 정의합니다.
// 필드값이 지정되어 있으므로 constructor 메서드 필요없음
class Slime {
hp = 50;
op = 4;
attack (enemy) {
enemy.hp -= this.op;
this.hp += this.op/4;
}
}
const slime1 = new Slime();
const slime2 = new Slime();
slime1.attack(slime2);
정적 멤버는 인스턴스 멤버와 달리 클래스 자체에 속해 있습니다. 즉 인스턴스가 아닌 클래스 레벨에서 존재하기 때문에, 클래스가 정의될 때 한 번만 메모리에 할당됩니다. 이 정적 멤버들은 클래스의 모든 인스턴스에서 공유되기 때문에 인스턴스가 생성되어도 별도의 메모리 공간을 차지하지 않습니다.
그래서 인스턴스 생성 없이 클래스 차원에서 메서드 호출이 가능하며, 인스턴스를 통해서는 접근할 수 없습니다.
class Company {
// 정적 변수와 메서드
static brand = '애플';
static contact () {
return `${this.brand}입니다. 무엇을 도와드릴까요?`; // 정적 메서드에서는 정적 필드만 사용가능합니다.
}
constructor (name, price) {
this.name = name;
this.price = price;
}
introduce () {
return `새로나온 ${this.name}의 가격은 ${this.price}입니다!`;
}
}
console.log(Company.contact()); //인스턴스 생성 없이 클래스 차원에서 호출 가능
static 변수와 메서드는 클래스의 인스턴스가 아닌 클래스 자체에 속해 있으며, 인스턴스 간에 공유되어야 하는 데이터나 기능을 관리하는 데 유용합니다. 이를 통해 공통된 기능 제공, 데이터 공유, 메모리 절약, 클래스 수준의 설정 관리 등을 할 수 있습니다.
static 메서드는 유틸리티 함수나 클래스와 관련된 전역적인 동작을 정의할 때 특히 유용합니다.
class
생성자 함수
class
class Employee extends Person {
constructor(name, age, jobTitle) {
super(name, age);
this.jobTitle = jobTitle;
}
}
생성자 함수
function Employee(name, age, jobTitle) {
Person.call(this, name, age);
this.jobTitle = jobTitle;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
class
생성자 함수
class
static
키워드를 사용해 정적 메서드나 속성을 쉽게 정의할 수 있습니다. 이는 클래스 자체에 속하는 메서드나 속성입니다. 따라서 인스턴스를 생성하지 않아도 사용가능합니다. class MathUtil {
static add(a, b) {
return a + b;
}
}
console.log(MathUtil.add(2, 3)); // 5
생성자 함수
function MathUtil() {}
MathUtil.add = function(a, b) {
return a + b;
}
console.log(MathUtil.add(2, 3)); // 5
클래스와 생성자 함수는 모두 객체를 생성하는 방법이지만, 클래스는 객체 지향 프로그래밍 패턴을 더 직관적으로 지원하고, 다양한 기능이 내장되어 있습니다.. 반면, 생성자 함수는 자바스크립트의 오래된 방식으로, 더 유연하지만 코드가 복잡해질 수 있습니다.
클래스가 더 현대적이고, 대부분의 새로운 코드에서는 클래스를 사용하는 것이 추천됩니다.
접근자 프로퍼티는 getter, setter 함수
라고도 부르며 함수명 앞에 get
과 set
을 붙입니다. 스스로는 값을 갖지 않고 다른 프로퍼티의 값을 읽거나 저장할 때 사용합니다.
생김새는 함수처럼 지정되었지만 프로퍼티처럼 사용합니다.
const person1 = {
age: 17, // 데이터 프로퍼티
// 접근자 프로퍼티
get koreanAge () {
return this.age + 1;
},
// 접근자 프로퍼티
set koreanAge (krAge) {
this.age = krAge - 1;
}
}
console.log(person1, person1.koreanAge); // person1.koreanAge() 이렇게 사용하지 않음.
클래스에서도 사용 가능합니다.
class Company {
constructor (name, price) {
this.name = name;
this.price = price;
}
get newItems(){
return `${새로나온 신상 ${this.name}의 가격은 ${this.price}입니다!}`;
}
set newItemsPrice(newPrice){
if( typeof newPrice !== 'number') return;
if(newPrice <= 0) return;
this.price = newPrice;
}
}
자바스크립트의 필드는 기본적으로 public으로 은닉되지 않습니다.
데이터 은닉
: private 필드와 메서드를 사용하면 클래스 외부에서 객체의 내부 상태를 직접 수정하지 못하도록 보호할 수 있습니다. 이를 통해 객체의 무결성을 유지할 수 있습니다.
캡슐화
: 객체 지향 프로그래밍의 중요한 개념 중 하나인 캡슐화를 구현하는 데 도움이 됩니다. 내부 구현을 숨기고, 외부에 노출할 필요가 없는 부분을 은닉할 수 있습니다.
유지보수
: 클래스 내부의 동작을 자유롭게 변경할 수 있어, 유지보수가 용이해집니다. 외부에서 접근하지 못하므로, 클래스의 내부 구조를 바꿔도 외부 코드에 영향을 미치지 않습니다.
private한 필드명과 메서드명 앞에는 #
기호를 붙여 선언합니다. 이 필드와 메서드는 클래스 외부에서 접근할 수 없으며, 오직 클래스 내부에서만 접근하고 호출 가능합니다.
class Person {
// private 필드
#name;
#age;
constructor(name, age) {
this.#name = name;
this.#age = age;
}
// public 메서드
getDetails() {
return `Name: ${this.#name}, Age: ${this.#age}`;
}
// private 메서드
#calculateBirthYear() {
const currentYear = new Date().getFullYear();
return currentYear - this.#age;
}
getBirthYear() {
return this.#calculateBirthYear();
}
}
const person = new Person('Alice', 30);
console.log(person.getDetails()); // "Name: Alice, Age: 30"
// 외부에서 private 필드에 접근하려고 하면 오류 발생
console.log(person.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(person.getBirthYear()); // 올바르게 계산된 출생 연도 반환
상속(Inheritance)은 한 클래스가 다른 클래스의 속성과 메서드를 물려받아 사용하는 개념입니다. 이를 통해 코드의 재사용성을 높이고, 객체 지향 프로그래밍(OOP)에서 중요한 개념인 다형성을 구현할 수 있습니다.
상속을 사용하면, 부모 클래스의 기능을 자식 클래스가 물려받아 사용할 수 있습니다. 자식 클래스는 부모 클래스의 모든 속성과 메서드를 그대로 사용할 수 있으며, 필요에 따라 새로운 속성이나 메서드를 추가하거나, 부모 클래스의 메서드를 재정의(오버라이딩)할 수도 있습니다.
extends
키워드를 사용합니다.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 부모 클래스의 생성자를 호출
this.breed = breed;
}
// 부모 클래스의 메서드를 재정의 (오버라이딩)
speak() {
console.log(`${this.name} barks.`);
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // "Buddy barks."
super는 부모 클래스의 생성자(constructor)나 메서드를 호출할 때 사용됩니다.
자식 클래스에서 부모 클래스의 생성자를 호출할 때는 super()를 사용해야 합니다. super()를 호출하지 않으면 this 키워드를 사용할 수 없기 때문에, 자식 클래스에서 부모 클래스를 상속받은 속성을 초기화하려면 반드시 super()를 호출해야 합니다.
자식 클래스는 부모 클래스의 메서드를 재정의(오버라이딩)할 수 있습니다. 자식 클래스에서 동일한 이름의 메서드를 정의하면, 부모 클래스의 메서드를 덮어쓰게 됩니다.
만약 자식 클래스에서 부모 클래스의 메서드를 호출하고 싶다면, super.메서드명()을 사용하면 됩니다.
...
기호를 이용해서 객체를 펼칠 수 있습니다. 특정 객체의 프로퍼티를 포함하는 새로운 객체 생성에 유용하게 사용됩니다.중복되는 프로퍼티는 뒤의 것이 덮어씌어집니다.
const class1 = {
a: 1, b: 'A', c: true
};
const class2 = {
d: { x: 10, y: 100 }, e: [1, 2, 3]
};
const class3 = {
...class1, z: 0
}
const class4 = {
...class2, ...class3, ...class2.d
}
주의할 점은, 얕은 복사
를 하므로 원본 객체를 수정할 시 복제한 객체의 참조값도 같이 변합니다. 원시값만 있는 객체만 값에 의한 복사를 합니다.
const class1 = {
x: 1,
y: { a: 2 },
z: [3, 4]
};
const class2 = { ...class1 };
class1.x++;
class1.y.a++;
class1.z[0]++;
console.log(class1); // {x:2, y: {a:3}, z:[4,4] }
console.log(class2); // {x:1, y: {a:3}, z:[4,4] }
객체안의 필요한 프로퍼티를 짧은 코드로 변수(또는 상수)에 할당시킬 수 있습니다.
const obj1 = {
x: 1, y: 2, z: 3
};
const {x, y, z} = obj1;
console.log(x, y, z);