객체
객체에 대한 개략적인 설명
자바스크립트 -> 총 8개의 자료형. 7개 = 하나의 데이터만 담을 수 있어, 원시형
객체 = 다양한 데이터를 담을 수 o (따라서, 객체는 원시형이 아니다!)
키(key, 문자형만(혹은 심볼형. 만약 문자형도 아니고 심볼형도 아닌 값이 오면 문자형으로 자동 형변환된다.))와 값(value, 모든 값)의 쌍 = 프로퍼티(property)
키를 이용해 프로퍼티를 찾고, 수정, 삭제
빈 객체를 만드는 두 가지 방법
let user = new Object(); // '객체 생성자' 문법
let user = {}; // '객체 리터럴' 문법 (주로 사용하는 방법 -> 왜?)
간편성, 가독성, 속도
가독성
function Person(name, age) { this.name = name; this.age = age; } var John = new Person("John Doe", 30);
객체 이름과 생성자 함수의 이름이 다르다. 생성자 함수 이름 = Person, 객체 이름 = John
function Person(name, age, address, phone) { this.name = name; this.age = age; this.address = address; this.phone = phone; this.getInfo = function() { return this.name + " is " + this.age + " years old and lives at " + this.address + ". Phone: " + this.phone; }; } var John = new Person("John Doe", 30, "123 Main St, Anytown, USA", "555-1234"); console.log(John.getInfo());
여러 개의 프로퍼티를 가진 복잡한 객체일수록 생성자 문법을 사용하면 복잡하다.
위 코드를 객체 리터럴로 작성할 경우 아래와 같이 쓰일 수 있다.
var John = { name: "John Doe", age: 30, address: "123 Main St, Anytown, USA", phone: "555-1234", getInfo: function() { return this.name + " is " + this.age + " years old and lives at " + this.address + ". Phone: " + this.phone; } };
속도
뿐만 아니라, 리터럴 방식으로 작성할 경우 오버헤드가 작아져 객체를 더 빨리 생성할 수 있다.
키가 복수의 단어로 이루어진 경우
키를 ""로 묶어야 하고, 값에 접근할 때는 . 을 사용하는 대신, 객체명["복수의 단어로 이루어진 키"] 처럼 배열과 같은 방식으로 접근. 이를 대괄호 표기법이라 하는데, 대괄호 표기법을 이용하면 키 문자열 대신 문자열 변수가 올수도 있음. ex. let John = "나는 존 입니다" Person[John]
계산된 프로퍼티
프로퍼티 이름을 동적으로 할당하고 싶다면, 객체 내에 프로퍼티를 작성할 때, [변수명] 를 이용할 수 있다.
ex)
let book = permission ? "comic" : "study";
let boughtBook = {
[book]: true,
}
console.log(boughtBook.comic)
단축 프로퍼티
프로퍼티의 값이 변수이고, 키와 값의 변수 이름이 서로 같다면, ex) name: name 두 번 적을 필요 없이 한 번만 적으면 된다. ex) name,
undefined
자바스크립트의 중요한 특징: 존재하지 않는 프로퍼티에 접근하려 해도 에러가 발생하지 않고 undefined 를 반환한다.
in 연산자를 이용해서, 프로퍼티의 존재 여부 확인 가능!
let person = {
hair: "brown",
eye: "blue",
}
console.log("hair" in person); //true
console.log("foot" in person); //false
let hairstyle = "hair";
console.log(hairstyle in person); //true
'for...in' 반복문
for(let body in person){
console.log(body); // hair, eye 가 출력됨
console.log(person[body]) // brown, blue 가 출력됨
}
한 가지 주의할 점은, 자동 객체 정렬을 제공한다는 것!
let Person = {
"37": "John",
"22": "Mina",
}
위 Person 을 for in 돌려 보면, 37 이 먼저, 22가 나중에 나와야할 것 같지만, 22가 먼저, 37이 다음으로 나온다. 자동 객체 정렬이 되었기 때문! (키 타입이 숫자이면 그렇게 나온다..!) 이를 방지하기 위해서는 + 를 붙여주고, 출력시에도 + 를 붙여주면 된다.
let Person = {
"+37": "John",
"+22": "Mina",
}
for(let age in Person){
console.log(+age); // 37 22
}
참조에 의한 객체 복사
객체와 원시형의 가장 큰 차이점: 객체는 참조에 의해 저장되고, 복사된다!
객체는 메모리 내 어딘가에 저장되고, 변수 user 엔 객체를 참조할 수 있는 값! 메시지 모양 이모티콘이 저장된다. c 언어로 생각하면, 주소가 저장되는 것! 따라서 객체가 할당된 변수를 복사할 땐, 객체의 참조값이 복사되고 객체는 복사되지 않는다.
객체를 서랍장에 비유하면 변수는 서랍장을 열 수 있는 열쇠라고 할 수 있습니다. 서랍장은 하나, 서랍장을 열 수 있는 열쇠는 두 개인데, 그중 하나(admin)를 사용해 서랍장을 열어 정돈한 후, 또 다른 열쇠로 서랍장을 열면 정돈된 내용을 볼 수 있습니다.
let fruit = {
berry: "blueberry",
}
let food = fruit;
fruit.berry = "strawberry";
console.log(food.berry); // strawberry
객체를 독립된 객체로 복제하고 싶다면 어떻게 해야 할까?
방법 1) 빈 객체를 만들고, for in 을 사용해 모든 프로퍼티 복사
let food2 = {};
for (let prop in food) {
food2[prop] = food[prop];
}
방법2) Object.assign 이용하기
Object.assign(dest, [src1, src2, src3...])
src1, src2, src3 는 모두 객체이며, 이 객체의 모든 프로퍼티들이 dest 객체로 복사된다. 그리고 마지막으로 dest 를 반환한다. 만약, 동일한 키가 dest 에 있는 경우라면 src 의 키로 대체된다.
let clone = Object.assign({}, user);
를 이용하면 for in 을 사용하지 않고도 객체를 복제할 수 있다.
중첩 객체 복사
만약, 프로퍼티 중에 원시값이 아닌, 다른 객체를 참조하는 값이 있다면 어떻게 해야 할까? Object.assign({}, user) 를 사용할 경우, 복제를 원했지만, 복사만 될 수 있다. 왜? 객체는 참조에 의해 복사되니까!!! 새로운 객체를 만들어서 값을 복사한다 한들, 복제되지 못함. 이 경우, 프로퍼티의 값을 모두 검사하면서 값이 객체이면 해당 객체의 구조를 복사해주는 깊은 복사를 해야 함. 라이브러리 lodash의 메서드인 _.cloneDeep(obj)을 사용하면 깊은 복사를 할 수 있음!
메서드와 this
객체 = 엔티티. 객체에게 동사(행동)을 부여해 주는 것이 메서드.
let person = {
hair: "brown",
eyes: "blue",
}
person.eat = function () {
console.log("eat");
}
person.eat(); // eat
function sleep() {
console.log("sleep");
}
person.sleep = sleep;
person.sleep(); //sleep
person = {
watch() {
console.log("watch");
}
}
person.watch();
person.hair; // 에러 남. 이제 watch 밖에 없는 객체가 되어서.
메서드와 this
많은 메서드가 자신이 속한 객체의 프로퍼티를 사용하게 되는데 이때 필요한 것이 this.
let Person = {
hair: "brown",
eye: "blue",
sleep() {
console.log(`close your ${this.eye}`)
}
}
여기서 this 대신에 Person.eye 를 사용할 수도 있지만, 혹시 모를 에러가 발생할 수 있으니, this 사용을 권장.
자바스크립트에서 this 는 어느 함수에나 올 수 있고, this 의 값은 런타임에 결정되기 때문에 컨텍스트에 따라 달라진다. 따라서 동일 함수여도 다른 객체에서 쓰였다면 this 의 값은 서로 다르게 나올 수 있다.
화살표 함수는 this 가 없다.
화살표 함수에서 this 를 참조할 수는 있지만, 이 경우 this 는 화살표 함수를 가리키는 것이 아니라 외부에 있는 일반 함수의 this 가 된다.
let person = {
hair: "brown",
eye: "blue",
sleep() {
let wash = () => {
console.log(`wash your ${this.hair}`);
}
}
}
여기서 this 는 wash 의 this 가 아니라 sleep 의 this 가 된다.
new 연산자와 생성자 함수
비슷한 객체를 여러 개 생성해야 할 경우 생성자 함수를 사용하면 편리하게 만들 수 있다! 생성자 함수에서는 지켜야 할 약속이 두 가지 있는데,
1. 함수의 첫 글자는 반드시 대문자
2. 실행할 때는 new 를 붙여서 실행한다.
function Person(name) {
this.name = name;
this.eye = "blue",
this.hair = "hair",
this.sleep = function sleep() {
console.log(`close your ${eye}`);
}
}
let person1 = new Person('현수');
let person2 = new Person('지헌');
여기서 생성자 함수가 실행되는 메커니즘은
1. 빈 객체를 만들고 this 를 할당한다.
2. this 에 프로퍼티들을 추가한다.
3. this 를 반환한다.
이다.
생성자 함수와 return 문
생성자 함수는 자동으로 this 를 리턴하므로, 일반적으로는 return 이 없다. 만약 return 문이 있다면,
1. 원시형을 반환하는 경우 -> 무시
2. 객체를 반환하는 경우 -> this 대신 객체를 반환
하게 된다. 그러나 return 을 사용하는 생성자 함수는 거의 없다.
옵셔널 체이닝 '?.'
자바스크립트는 존재하지 않는 프로퍼티에 접근해도, 에러를 내지 않고 undefined 를 반환한다. 그런데 undefined 아래에 있는 프로퍼티에 접근하면 에러가 난다. 예를 들어서 Person.eye.color 는 undefined 를 반환하겠지만, Person.eye.color.eyecolor 에 접근하면, 에러가 난다.
이를 막기 위해 옵셔널 체이닝을 이용한다.
Person.eye?.clolor?.eyecolor 를 이용하면, ?. 앞에 있는 프로퍼티의 값이 null 혹은 undefined 일 때 평가를 멈추고 undefined 를 반환한다.
?. 는 연산자가 아닌 문법이기 때문에 ?.() 를 이용해 객체 내 함수에 접근할 수도 있고, ?.[] 를 이용해 객체 내 키에 접근할 수도 있다.
'모던 자바스크립트 튜토리얼' 카테고리의 다른 글
[SOPT]JS 스터디 6주차 에러 핸들링, 프로미스, async await (0) | 2023.05.27 |
---|---|
SOPT 자스 스터디 5주차 프로토타입, 콜백, 프로미스 (0) | 2023.05.21 |
SOPT 자스 스터디 4주차 (1) | 2023.05.14 |
[SOPT] JS 스터디 3주차 (1) | 2023.05.06 |
[SOPT] JS 스터디 1주차 (0) | 2023.04.09 |
댓글