1. 객체와 프로퍼티란?
- 하나의 데이터만 담을 수 있는 원시형 타입이 아닌 다양한 유형의 데이터를 담을 수 있다.
1) 객체를 만드는 법
- 객체 생성자
let user = new Object(); // '객체 생성자' 문법
- 객체 리터럴
let user = {}; // '객체 리터럴' 문법
2) 프로퍼티
- 키 값으로 이루어져 있고, 객체이름.객체프로퍼티 로 객체의 값을 얻을 수 있음
- delete로 프로퍼티 삭제 가능
let user = { // 객체
name: "John", // 키: "name", 값: "John"
age: 30 // 키: "age", 값: 30
};
// 프로퍼티 값 얻기
alert( user.name ); // John
// 프로퍼티 값 삭제
delete user.age;
3) 대괄호 표기법
- 점 표기법과 다르게 어떠한 변수나, 두 개 이상의 변수로 된 프로퍼티에 접근이 용이하다.
let user = {
name: "John",
age: 30
};
let key = prompt("사용자의 어떤 정보를 얻고 싶으신가요?", "name");
// 변수로 접근
alert( user[key] ); // John (프롬프트 창에 "name"을 입력한 경우)
// 점 표기법은 불가
alert( user.key ) // undefined
4) 프로퍼티를 변수로 지정하는 법
- 또한 프로퍼티의 경우, 계산된 값을 다른 변수에서 받아와 사용할 수도 있다.
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");
let bag = {};
// 변수 fruit을 사용해 프로퍼티 이름을 만들었습니다.
bag[fruit] = 5;
- 이렇듯 프로퍼티를 기존 변수에서 받아와 사용하는 경우가 있기에, 변수를 프로퍼티로 쓸 때는 단축해서 사용하는 것이 가능하다.
function makeUser(name, age) {
return {
name, // name: name 과 같음
age, // age: age 와 같음
// ...
};
}
5) 객체를 순회하는 법
- 객체에 있는 프로퍼티를 확인하거나, 객체에 접근하고 싶을 때는 in과 for in을 적절히 활용하자.
let user = {
name: "John",
age: 30,
isAdmin: true
};
alert( "age" in user ); // user.age가 존재하므로 true가 출력됩니다.
for (let key in user) {
// 키
alert( key ); // name, age, isAdmin
// 키에 해당하는 값
alert( user[key] ); // John, 30, true
}
2. 객체는 참조 복사에 의해 동작한다.
- 객체와 원시 타입의 근본적인 차이는, 객체는 ‘참조에 의해(by reference)’ 저장되고 복사된다는 것이다.
1) 객체 변수를 참조하는 법
- 변수 객체가 그대로 저장되는 것이 아닌, 객체가 저장되어있는 '메모리 주소’인 객체에 대한 '참조 값’이 객체 변수에 저장된다.
let user = { name: "John" };
let admin = user; // 참조값을 복사함
2) 참조한 객체 끼리의 비교
- 따라서 동일한 객체에 대한 여러 변수의 참조가 가능하다.
아래 코드는 동일한 객체를 참조하여 복사했기 때문에, 비교할 때도 동일한 참조값을 비교하게 된다.
let a = {};
let b = a; // 참조에 의한 복사
alert( a == b ); // true, 두 변수는 같은 객체를 참조합니다.
alert( a === b ); // true
- 하지만 아래의 경우 두 객체는 다른 참조값을 가지고 있음을 확인할 수 있다.
let a = {};
let b = {}; // 독립된 두 객체
alert( a == b ); // false
- 따라서 동일한 객체를 복사가 아닌 복제를 하고싶다면, (복제를 통해 독립적인 객체를 만들고 싶다면)
새로운 객체를 만든 다음 기존 객체의 프로퍼티들을 순회해 원시 수준까지 프로퍼티를 복사하거나 Object.assign하는 방법이 있겠다.
3. 객체 메소드와 객체의 프로퍼티를 다루는 this
1) 메소드를 만드는 법
- 함수 표현식으로 바로 객체의 프로퍼티로서 메소드를 할당하는 방법이 있다.
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("안녕하세요!");
};
user.sayHi(); // 안녕하세요!
- 또한 함수 표현을 생략해서 간단히 메소드를 등록할 수도 있다!
// 단축 구문을 사용하니 더 깔끔해 보이네요.
user = {
sayHi() { // "sayHi: function()"과 동일합니다.
alert("Hello");
}
};
- 또한 함수를 따로 선언하여 함수를 메소드로 등록할 수도 있다.
// 함수 선언
function sayHi() {
alert("안녕하세요!");
};
// 선언된 함수를 메서드로 등록
user.sayHi = sayHi;
user.sayHi(); // 안녕하세요!
2) 메소드에서 객체에 접근하고 싶을 때, this 활용법
- 메서드 내부에서 this 키워드를 사용하면 객체에 접근할 수 있다.
let user = {
name: "John",
age: 30,
sayHi() {
// 'this'는 '현재 객체'를 나타냅니다.
alert(this.name);
}
};
- 또한 this 키워드가 아니라 obj.f()의 형태로도 f가 this처럼 쓰여서 f를 호출하는 동안의 obj객체를 참고할 수 있다.
이거 매우 신기 ❗️
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// 별개의 객체에서 동일한 함수를 사용함
user.f = sayHi;
admin.f = sayHi;
// 'this'는 '점(.) 앞의' 객체를 참조하기 때문에
// this 값이 달라짐
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (점과 대괄호는 동일하게 동작함)
- 또한 this는 런타임에 따라 함수 호출과 동시에 값이 할당된다고 볼 수 있다.
- 따라서 자유로운만큼 여러 객체에 대한 접근을 할 수 있다.
3) this와 화살표 함수
- 참고로, 화살표 함수의 경우 함수 영역에 대한 범위가 모호해 고유한 this를 가지지 않는다.
따라서 화살표 함수 안에서 this를 써도 외부 함수의 this를 참고하게 된다 !
이것도 신기 ❗️
let user = {
firstName: "보라",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // 보라
4. new 연산자, 생성자 함수를 통해 여러 객체를 만들기
1) 생성자 함수란?
- 생성자 함수는 new 연산자를 통해 함수 형태로 선언된 객체를 재사용하는 것이다.
- 그렇기 때문에 처음에 빈 객체로 this를 할당하고, 생성자 함수를 통해 새로운 프로퍼티를 만드는 순서로 동작한다.
- 1번 코드 (생성자 함수 이용)
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("보라");
alert(user.name); // 보라
alert(user.isAdmin); // false
- 2번 코드
let user = {
name: "보라",
isAdmin: false
};
- 위 코드 2개는 똑같이 동작한다. 문득 봤을 때는 2번 코드가 더 간결해보인다. 그렇다면 1번 코드에 해당하는 생성자 함수를 왜 쓰는 것일까?
- 그것은 바로 재사용이 가능하다는 점에서이다. 1번 코드를 사용하면, 아래와 같이 비슷한 프로퍼티를 가진 객체를 틀에 찍듯이 만들어낼 수 있다!
new User('ET')
new User('Bom')
5. 옵셔널 체이닝 '?.'
1) 옵셔널 체이닝이란?
- 이는 &&과 비슷하게, 여러 프로퍼티가 겹쳐서 사용 될시에 중간에 없는 프로퍼티가 있을 경우 안전하게 대처할 수 있다는 장점을 가진다.
- 아래와 같이 없는 프로퍼티에 접근하려는 구문이 있다고 해보자.
let user = {}; // 주소 정보가 없는 사용자
alert(user.address.street); // TypeError: Cannot read property 'street' of undefined
- 이를 해결하고자 &&연산자의 추가 기능을 이용해 없는 값에 대한 예외 처리를 할 수 있었다.
alert( user && user.address && user.address.street ); // undefined, 에러가 발생하지 않습니다.
- 하지만 && 연산자를 사용할 경우 코드가 길어질 수 있다.
따라서 등장한 게 옵셔널 체이닝!
앞의 프로퍼티가 존재하지 않으면 undefined를 반환한다.
alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.
- 하지만 옵셔널 체이닝을 사용할 때에는, 1) 존재하지 않아도 되는 대상에 대해서 사용하는 것과, 2) ?. 앞의 변수는 존재해야 올바른 예외처리를 할 수 있음을 잊지 말자!
2) 옵셔널 체이닝을 이용한 다양한 구문
- '?.()'를 이용해 객체의 메소드에 접근할 수 있다.
let user1 = {
admin() {
alert("관리자 계정입니다.");
}
}
let user2 = {};
user1.admin?.(); // 관리자 계정입니다.
user2.admin?.();
- '?.[]'을 이용해서 객체의 프로퍼티에 접근할 수 있다.
let user1 = {
firstName: "Violet"
};
let user2 = null; // user2는 권한이 없는 사용자라고 가정해봅시다.
let key = "firstName";
alert( user1?.[key] ); // Violet
alert( user2?.[key] ); // undefined
저번 2주차 과제를 하는데, 많은 시간을 바닐라 js의 객체를 핸들링하는 부분에서 사용했다.
그만큼, 많이 사용되는 개념이고 js의 데이터의 꽃이라고 할 수 있으니 이번을 계기로 더 친해져야 겠다. ☺️
https://ko.javascript.info/object
'Web > javascript' 카테고리의 다른 글
[javascript] 동기식 언어인 javascript가 통신을 하는 방법 (0) | 2023.06.04 |
---|---|
[Javascript] JS의 함수를 잘 활용하고 있나요? - JS 함수에 숨겨진 기능 톺아보기 (0) | 2023.05.18 |
[javascript] 배열 총정리 (0) | 2023.05.06 |
[JS 기본] 함수선언문 VS 함수표현식 / 콜백함수, 화살표함수 친해지기 (0) | 2023.04.10 |
[JS 기본] IF문과 논리연산자에 숨겨진 기능 (0) | 2023.04.10 |