1. 프로퍼티 삭제
- delete 연산자를 통해서 객체에서 프로퍼티를 삭제
- 자체 프로퍼티만 삭제할 뿐 상속된 프로퍼티는 삭제 하지 않는다.
- 상속된 프로퍼티를 삭제하려면 반드시 해당 프로퍼티를 정의한 프로토타입 객체에서 삭제해야함
- delete는 변경 가능 속성이 false 인 프로퍼티는 제거하지 않는다.
(내장 객체의 일부 프로퍼티, 변수 선언이나 함수 선언으로 생성된 전역 개체의 프로퍼티는 변경 불가)
- 변경 불가인 프로퍼티를 삭제하려면 Type Error 발생(strict 모드인 경우)
let obj = {x: 1}; // obj에는 자체 프로퍼티 x가 있고 toString을 상속
delete obj.x; // --> true 반환 : 프로퍼티 x를 삭제
delete obj.x; // --> true 반환 : x가 존재하지 않으므로 아무 일도 일어나지 않는다.
delete obj.toString; // --> true 반환 : toString은 자체 프로퍼티가 아니므로 아무 일도
일어나지 않는다.
delete 1; // --> true
// 다음은 일반 모드의 결과이며, 스트릭트 모드에서는 모든 TypeError 발생
delete Object.prototype; // --> flase 반환 : 프로퍼티 변경 불가
var x = 1; // 전역 변수 선언
delete globalThis.x; // --> false 반환 : 해당 프로퍼티는 삭제 불가
function f() {} // 전역함수 선언
delete glbobalThis.f; // --> false 반환 : 삭제 불가
globalThis.x = 1; // 변경 가능한 프로퍼티 생성(let, var는 불가)
delete x // --> true : 이 프로퍼티를 삭제 가능(스트릭트 모드에서 SyntaxError 발생)
delete globalThis.x // 명시적으로 전역 객체를 참조해야 동작
2. 프로퍼티 테스트
- 주어진 이름을 가진 프로퍼티가 존재하는지 확인이 필요할 때,
in 연산자, hasOwnProperty(), propertyIsEnumerable() 메서드를 사용
let obj = {x:1};
// in : 왼쪽 프로퍼티 이름이 오른쪽 객체에 자체프로퍼티
이거나 상속된 프로퍼티로 존재하는지 체크
"x" in obj // --> true 반환 : obj에는 자체 프로퍼티 x가 있음
"y" in obj // --> false 반환 : obj에는 프로퍼티 y가 없음
"toString" in obj // --> true 반환 : obj는 toString을 상속함
// hasOwnProperty : 주어진 이름을 가진 자체 프로퍼티가 있는지 체크
obj.hasOwnProperty("x") // --> true 반환 : obj에는 자체 프로퍼티 x가 존재
obj.hasOwnProperty("y") // --> false 반환 : obj에는 프로퍼티 y가 없음
obj.hasOwnProperty("toString") // --> false 반환 : toString은 상속된 프로퍼티임
// propertyIsEnumerable() : 자체 프로퍼티에 존재하면서 열거 가능 속성인지 체크
obj.propertyIsEnumerable("x") // --> true 반환: obj에는 열거 가능 프로퍼티 x가 존재
obj.propertyIsEnumerable("toString") // --> false 반환: 자체 프로퍼티 x
Object.prototype.propertyIsEnumerable("toString") // --> false : 열거 불가
// in과 동일한 기능을 하는 !== 사용하기도 함
obj.x !== undefined // --> true 반환: obj에는 프로퍼티 x가 있음
obj.y !== undefined // --> false 반환: obj에는 프로퍼티 y가 없음
obj.toString !== undefined // --> true 반환: obj에는 toString을 상속
3. 프로퍼티 열거
- for/in 루프는 상속 여부 구분하지 않고 열거 가능한 프로퍼티를 순회 한다.
다만, 객체가 상속하는 내장 메서드는 열거 불가이다.
let obj = {x:1, y:2, z:3}; // 열거 가능한 자체 프로퍼티 세개
obj.propertyIsEnumerable("toString") // --> false: 열거 불가
for(let p in obj) { // 프로퍼티 순회
console.log(p) // x,y,z를 출력하지만 toString은 아님
}
// 상속된 프로퍼티가 열거되는 것을 막을 때는 다음과 같이 루프 바디 안에서
// 명시적으로 체크한다
for (let p in obj) {
if(!obj.hasOwnProperty(p)) continue; // 상속된 프로퍼티 건너뜀
}
for (let p in obj) {
if(type obj[p] === "function") continue; // 메서드는 건너뜀
}
- 프로퍼티 이름을 배열로 저장할 수 있는 함수
1) Object.keys() : 객체의 열거 가능한 자체 프로퍼티 이름을 배열로 반환
(열거 불가 프로퍼티, 상속된 프로퍼티, 이름이 심벌인 프로퍼티 제외)
2) Object.getOwnPropertyNames() : 이름이 문자열이기만 하면 열거 불가인 자체 프로퍼티
이름도 배열로 반환
3) Object.getOwnPropertySymbols() : 열거 가능 여부를 따지지 않고 이름이 심별인 자체
프로퍼티 배열 반환
4) Reflect.owneKeys() : 열거 가능 여부, 문자열/심벌 여부와 상관없이 자체 프로퍼티
이름 전체 배열 반환
- 프로퍼티 열거 순서
1) 이름이 음이 아닌 정수인 문자열 프로퍼티가 첫 번째로 나열되며 작은 수에서
큰 수 순으로 열거된다. 따라서 배열 및 배열 비슷한 객체의 프로퍼티도 순서대로 열거된다.
2) 배열 인덱스와 비슷한 프로퍼티를 모두 열거한 다음에는 음수나 부동 소수점 숫자처럼 보이는
프로퍼티를 포함해 이름이 문자열인 프로퍼티를 열거한다. 이 프로퍼티는 객체에 추가된 순서
대로 열거된다. 객체 리터럴로 정의된 프로퍼티는 리터럴에 쓰인 순서를 따른다.
3) 마지막으로, 이름이 심벌인 프로퍼티를 객체에 추가된 순서대로 열거된다.
4. 객체 확장
- 특정 객체의 프로퍼티를 다른 객체의 프로퍼티로 복사하는 행위
// 기본 복사 방법
let target = {x:1}, source = {y:2, z:3}
for(let key of Object.keys(source)) {
target[key] = source[key];
}
// Object.assing 으로 복사하는 방법
Object.assign({}, defaults, obj);
// 객체를 새로 생성하고 기본 값을 복사한 다음,
// 이 기본 값을 obj의 프로퍼티로 덮어 쓴다.
// 분해 연산자 사용하여 복사하는 방법(*)
o = {...defaluts, ...o};
// Object.assign()과 마찬가지이지만 기존 프로퍼티는 덮어 쓰지 않는다.
// 심벌 프로퍼티를 복사하지 않는 것과 같다.
function merge(target, ...source) {
for(let source of sources) {
for(let key of Object.keys(source)) {
if(!(key in target) { //Object.assign과 다른 점임
target[key] = source[key];
}
}
}
return target;
}
Object.assign({x:1}, {x:2, y:2}, {y:3, z:4}) // --> {x:2,y:3,z:4}
merge({x:1}, {x:2, y:2}, {y:3, z:4}) // --> {x:1,y:2,z:4}