키와 값으로 구성된 속성의 집합이며, 중괄호로 선언한다.
객체는 참조 자료형이다.
객체는 동적으로 추가, 또는 삭제를 할 수 있다.
⇒ 때문에 객체 내부에 없는 속성(프로퍼티)에 접근해도 undefined를 리턴 받는다.
추가 : 객체의 속성에 접근해서 값을 할당
삭제 : delete 키워드를 사용해서 특정 속성을 삭제
const userObj = {
name: "철수",
age: 20
};
// 객체 속성에 접근하는 방법
console.log(userObj.name);
// 객체 속성을 수정하는 방법
userObj.name = "영희";
대괄호 표기법으로도 접근할 수 있지만, 배열과 구분하기 위해 점 표기법을 주로 사용한다.
데이터를 정의하는 데 있어서 간편하게 데이터를 정의할 수 있는 방법이다.
// 리터럴
const userObj = {
name: "철수"
};
// 반 : 생성자 함수
const userObj = new Object();
userObj2.name = "철수";
기타 리터럴 표현:
// 배열 리터럴
const userArr = [];
// 문자 리터럴
const userName = "철수";
// 불리언 리터럴
const isUser = false;
// 숫자 리터럴
const num = 10;
// null 리터럴
const inUser = null;
객체 속성 값이 존재하는지 여부를 불린 값으로 알려주는 메서드다.
console.log(userObj.hasOwnProperty("name")); //true or false
객체 내부의 값을(자기 자신을) 호출하려면:
const userObj = {
name: "철수",
age: 20,
getName: function() {
console.log(userObj.name); // 또는 console.log(this.name);
}
};
userObj.getName();
this는 예약어로, 보통 함수 내부에서 많이 쓰인다.
this는 항상 함수를 호출한 객체를 가리킨다. 누가 자기를 호출했는가를 가리킨다.
const userObj = {
name: "철수",
getName: function () {
console.log(this);
console.log(userObj.name); // 철수
console.log(this.name); // 철수
}
}
userObj.getName(); // getName: ƒ ()name: "철수"[[Prototype]]: Object
객체의 키와 값이 같으면 생략할 수 있다 (단축 문법):
// 함수를 외부에 정의하고
function getName() {
console.log(this);
}
// 객체로 불러올 수도 있음
const userObj = {
name: "철수",
getName,
};
userObj.getName();
단축 함수:
const userObj = {
name: "철수",
// 단축 함수 (실무에서 많이 쓰이는 방식)
getName() {
console.log(this);
}
};
userObj.getName();
화살표 함수는 예외적으로 동작한다. 화살표 함수로 실행한 this는 그 함수가 선언된 스코프(어휘적 스코프)를 가리킨다.
const userObj = {
name: "철수",
getName: () => {
console.log(this);
}
};
userObj.getName(); // window를 가리킨다
const userObj = {
name: "철수",
age: 20,
};
// for-in으로 순회
for (let key in userObj) {
console.log(key, userObj[key]);
}
const userObj = {
name: "철수",
age: 20,
};
// 매개변수로 넣은 객체의 키 값을 배열 형태로 리턴
console.log(Object.keys(userObj));
const keysArr = Object.keys(userObj);
keysArr.forEach((key) => {
console.log(key, userObj[key]);
});
const userObj = {
name: "철수",
age: 20,
};
// 키는 필요 없고 값에 바로 접근하고 싶을 때 사용
console.log(Object.values(userObj)); // ['철수', 20]
const userObj = {
name: "철수",
age: 20,
};
Object.entries(userObj).forEach(([key, value]) => {
console.log(key, value);
});
// name 철수
// age 20
console.log(Object.entries(userObj)); // [ [ 'name', '철수' ], [ 'age', 20 ] ]
// 1. 얕은 복사 -> 객체, 배열 (참조 자료형)
const user = {
name: "철수",
};
const copyUser = { ...user };
user.name = "영희";
console.log(user, copyUser); // { name: '영희' } { name: '철수' }
const copyUser3 = user;
user.name = "미나";
console.log(user, copyUser3); // { name: '미나' } { name: '미나' }
// 2. 깊은 복사 -> 기본 자료형
const copyUser2 = user;
let a = 10;
let b = a;
a = 20;
console.log(b); // a의 값이 변경된 것에 대해 영향을 받지 않음 => 깊은 복사가 된 것이다
깊은 복사에서 얕은 복사로 바꾸는 것은 불가능하지만, 얕은 복사를 깊은 복사로 바꾸는 것은 가능하다.
스프레드 연산의 깊은 복사는 1단계에 한해서만 동작한다.
const user = {
name: "철수",
age: 20,
today: { eat: "meat" }, // today: 0x002
};
const user2 = { ...user };
user.age = 30;
user.today.eat = "vegitable";
console.log(user, user2);
이 코드는 얕은 복사(Shallow Copy)의 특성을 보여주는 예제다
출력결과:
// user
{
name: "철수",
age: 30,
today: { eat: "vegitable" }
}
// user2
{
name: "철수",
age: 20,
today: { eat: "vegitable" } // today 객체는 참조가 복사되어 같이 변경됨
}
이는 스프레드 연산자가 1단계 깊이만 복사하고, 중첩된 객체는 참조를 복사하기 때문이다.
⇒ 해결 방법
// JSON 문자열로 바꾸고 다시 파싱
const user2 = JSON.parse(JSON.stringify(user));
// 중첩된 객체까지 완벽하게 복사
혹은 lodash 라이브러리 사용 (cloneDeep())
객체 데이터 속성을 설명하는 것이다.
value, writable, enumerable, configurable
따로 오류가 뜨진 않는다.
const userObj = {
age: 20,
gender: "male",
};
userObj.name = "철수";
Object.defineProperty(userObj, "name", {
value: "철수",
writable: true, // 수정 가능 여부
enumerable: false, // 열거 가능한 속성인지 = 순회할 때 접근 가능한 속성인지 여부
configurable: true, // 설정에 대한 변경 가능 및 삭제 가능 여부 // false => delete 키워드로도 삭제 안 됨
});
userObj.name = "영희"; //writable: true => 값이 바뀜 // false => 오류가 뜨진 않지만 값이 바뀌지 않음
for (let key in userObj) {
console.log(key);
} // enumerable: true => age / gender / name // false => 안 뜸
get, set
클래스에서 많이 사용하는 문법으로, 객체에서 이렇게까지 조작하는 경우는 드물다.
const userObj = {
firstName: "John",
lastName: "Hash",
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
set fullName(name) {
const splitNames = name.split(" ");
this.firstName = splitNames[0];
this.lastName = splitNames[1];
}
}
userObj.fullName = "Jane Doe"; // 그대로 변경되지 않고, userObj 내부의 set이 실행됨
console.log(userObj);
// Object.seal(); 객체의 속성을 추가하거나 삭제하는 것을 막는다
const userObj = Object.seal({
name: "철수",
})
userObj.age = 20;
delete userObj.name;
console.log(userObj); // { name: '철수' }
// Object.preventExtensions() : 속성 추가 불가
const userObj2 = Object.seal({
name: "철수",
})
userObj2.name = "영희";
userObj2.age = 20;
delete userObj2.name;
console.log(userObj2); // { name: '영희' }
// Object.freeze() => seal + preventExtensions
메서드 체이닝: 객체의 메서드는 연결해서 사용할 수 있다.
메서드 체이닝을 구현하려면 자기 자신을 내보내야 한다 (= 반환하는 것이 this여야 한다).
const calculate = {
value: 0,
add: function (n1) {
this.value += n1;
return this;
},
subtract: function (n1) {
this.value -= n1;
return this;
},
getResult: function () {
return this.value;
}
}
const result = calculate.add(10).subtract(5).getResult();
console.log(result);