Object.is()
두 개의 파라미터의 값과 값 타입을 비교해 같으면 true / 아니면 false를 반환한다.
아래의 코드를 보면 Number 10과 String 10은 타입이 다르기 때문에 false가 반환된다.
빈 객체의 경우 똑같을 것 같지만 false가 나오는 이유는 참조형인 객체의 특성 상 각자 참조하는 메모리의 값이 다르기 때문에 같지 않다고 인식하기 때문이다. 애초에 해당 메서드는 오브젝트끼리 비교하기 위해 만들어진 목적이 아니기도 하다.
const result = Object.is(10, "10");
console.log(result) // false
const one = {}, two = {};
console.log(Object.is(one, two)) // false
이 is() 메서드는 === (일치 연산자)와 유사하다. === 도 값과 데이터 형식을 비교하기 때문이다.
하지만 Object.is()가 조금 더 정확한 부분이 있다.
console.log(1 === 1); // true
console.log(1 === '1'); // false
console.log(Object.is(1, 1)); // true
console.log(Object.is(1, '1')); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
console.log(-0 === +0); // true
console.log(Object.is(-0, +0)); // false
NaN과 부호가 붙은 0을 비교하는 부분에서 큰 차이가 있다.
NaN은 자기 자신과도 같지 않아 일반적인 === 를 사용하면 false가 나온다.
하지만 Object.is() 에서는 true가 나오는데, Object.is()는 NaN을 자기 자신과만 동등한 값으로 처리하기 때문이다.
부호가 붙은 0의 경우에도 ===는 모두 같다라고 보고 있는데, 이것보단 Object.is()로 판별한 false가 더 정확하다. 이전 게시글에서 JS의 Number를 다룰 때 부호를 담당하는 64비트가 있는데 이 부분의 값을 고려하면 (1일 경우 양수, 0일 경우 음수) 부호가 다르면 값도 다르다고 봐야 하기 때문이다.
function check(data) {
if (Object.is(typeof data, "object")) {
console.log(data)
} else {
console.log("object 타입 아님")
}
}
check({value: 10}); // {value: 10}
check(200); // object 타입 아님
이런 식으로도 활용할 수 있다.
Object.assign() : 얕은 복사(shallow copy)
두 번째 파라미터의 오브젝트 프로퍼티를 첫 번째 파라미터의 오브젝트에 복사해 첫 번째를 반환한다.
자신이 만든 프로퍼티만 복사할 수 있으며 상속받은 프로퍼티는 복사할 수 없다. (own property만 복사 가능)
아래 코드처럼 첫 번째 파라미터의 빈 오브젝트에 두 번째 파라미터의 오브젝트 프로퍼티를 복사했다.
그래서 obj를 콘솔에 찍었을 때 똑같이 나오는 것이다.
const obj = Object.assign({}, { foo: 1, bar: 2 });
console.log(obj); // { foo: 1, bar: 2 }
복사한 것을 붙여 넣을 첫 번째 파라미터는 반드시 작성되어야 하며 없을 경우 TypeError가 발생한다.
try {
const obj = Object.assign(null, {});
} catch(e) {
console.log("null 작성 불가")
};
// null 작성 불가
첫 번째 파라미터에 오브젝트가 아니라 다른 형태의 값이 들어오면 어떻게 될까?
const obj = Object.assign(100);
console.log(obj.valueOf()) // 100
오류가 날 것 같았는데 100이라는 값이 출력되었다.
그럼 obj는 지금 어떻게 생겨 먹은 걸까?
콘솔에서 찍어 보니 obj는 Number 인스턴스로 Number가 되어 PrimitiveValue에 주어진 값을 저장한 상태가 됐다.
JS에서 Object.assign() 메서드는 원시 값을 객체로 변환하는 내부 과정이 있다.
따라서 100이 Number 객체로 자동 변환되고, Number 객체에 속성이 복사된다.
obj를 콘솔에 찍어보면 object라고 나오는 부분이 그렇다.
두 번째 파라미터에는 열거 가능한 오브젝트를 작성해야 하며 오브젝트를 다수 작성할 수도 있다.
아래 코드를 보면 obj2에는 {ten: 10}을 복사해 할당했다.
두 번째 one은 book이랑 sport가 들어 있는 object인데, obj2에 one을 복사해 보니 enumerable: false인 book이 복사되지 않은 걸 확인할 수 있다. 즉 열거 가능한 오브젝트가 아니라면 복사할 수 없다는 것이다.
let obj2 = {};
Object.assign(obj2, {ten: 10});
console.log(obj2) // {ten: 10}
const one = Object.create({}, {
book: {value: 100, enumerable: false},
sports: {value: 200, enumerable: true}
});
Object.assign(obj2, one);
console.log(obj2) // {ten: 10, sports: 200}
오브젝트를 이런 식으로 콤마로 구분해 다수 작성할 수 있다.
const book = {title: "책"};
const sports = {item: "축구"};
const obj = Object.assign({}, book, sports);
console.log(obj) // {title: "책", item: "축구"}
위에서 열거 가능한 오브젝트가 아니면 복사할 수 없다고 했다.
아래의 예시를 보면 undefined, null, 200 같이 열거 가능한 오브젝트가 복사되지 않는 것을 다시 한 번 확인할 수 있다.
un, nu의 경우 값이 undefined, null이지만 열거 가능한 오브젝트이기 때문에 복사를 할 수 있다.
let obj4 = {ten: 10};
Object.assign(obj4, undefined, null, 200);
console.log(obj4) // {ten: 10}
const one = {un: undefined, nu: null};
Object.assign(obj4, one);
console.log(obj4) // {ten: 10, un: undefined, nu: null}
아래의 코드를 보면 {}가 아니라 100이 들어왔다.
const obj5 = Object.assign(100, {book: 200});
console.log(obj5.valueOf()); // 100
console.log(obj5.book) // 200
값을 저장한 Number 객체라 valueof()로 확인했을 때는 100이 나온다.
object인 두 번째 파라미터는 이상하게 생겼지만 일단 복사가 된 것을 확인할 수 있다.
강사님도 이런 형태로 쓰는 건 데이터 형태에 맞지 않고, 설명하기 위해 사용하셨다.
실제로 사용할 일은 없지만 그냥 오브젝트, 열거 가능한 객체면 되는구나! 라고 이해하면 될 것 같다.
Deep copy
Object를 할당하면 프로퍼티 값이 연동된다.
연동이란 한 쪽 오브젝트의 프로퍼티 값을 바꾸면 다른 오브젝트의 프로퍼티 값도 바뀌는 것을 말한다.
아래와 같이 sports라는 오브젝트가 있고, 이를 copy라는 변수에 할당했다.
sports 오브젝트의 밸류 값을 농구로 수정한 후 copy를 콘솔에 찍어보면 똑같이 수정됨을 알 수 있다.
const sports = {
item: "축구"
};
let copy = sports;
sports.item = "농구";
console.log(copy.item) // 농구
Object.assign()을 써서 복사했을 때는 위와 같이 연동이 이뤄지지 않는다.
const sports = {
item: "축구"
};
let copy = {};
Object.assign(copy, sports);
sports.item = "농구";
console.log(copy.item) // 축구
하지만 그럼에도 아래와 같이 연동이 되는 경우가 있다.
const book = {
item: {title: "자바스크립트"}
};
let copy = {};
Object.assign(copy, book);
copy.item.title = "책";
console.log(book.item.title) // 자바스크립트
Object.assign()은 얕은 복사를 수행한다.
지금 book은 객체 안애 객체가 들어 있는 형태인데, 이 메소드는 속성 값이 객체일 경우 참조를 복사하게 된다.
즉 프로퍼티를 복사한 게 아니라 객체의 메모리 값을 복사한 것인데, copy.item과 book.item은 같은 객체를 참조하고 있기 때문에 연동이 이뤄지는 것이다.
따라서 객체의 중첩된 속성까지 그대로 복사를 하기 위해서는 깊은 복사(Deep copy)를 수행해야 한다.
이는 재귀적인 방식이나 외부 라이브러리를 사용해서 수행할 수 있다.
const book = {
item: {title: "자바스크립트"}
};
let copy = {};
for (let key in book) {
let value = book[key];
copy[key] = {};
for (let name in value) {
copy[key][name] = value[name];
};
};
book.item.title = "책"
console.log(copy.item.title) // 자바스크립트
book 객체를 생성하고 item 의 값으로 오브젝트를 가지도록 초기화 했다.
빈 객체인 copy를 생성한 후, book을 for... in 루프로 순회해 book 객체의 속성을 반복한다. 여기서는 현재 item 속성만 존재한다. value 변수에 현재 반복되고 있는 속성의 값인 {title: "자바스크립트"} 객체를 할당했다.
내부에 있는 for... in 루프를 사용해서 value 객체의 속성을 반복한다. 여기에는 title 속성만 있다. ({title: "자바스크립트"} 이기 때문에) copy[key][name] = value[name]을 통해 보면 copy.item.title은 value.title 값을 복사하면서 자바스크립트가 나오게 된다.
이렇게 다단계 계층에서 값이 연동되지 않도록 복사하는 것을 딥 카피라고 한다.
JSON 함수를 사용하면 위보다 조금 더 간단하게 복사할 수 있다.
const book = {
item: {title: "자바스크립트"}
};
const copy = JSON.parse(JSON.stringify(book));
book.item.title = "책";
console.log(copy.item.title) // 자바스크립트
JSON.stringify()로 문자열로 변환한 후 파싱하면 연동되지 않는다.
(JSON.stringify()만 하면 {"item":{"title":"자바스크립트"}}처럼 문자열로 만들어진다.)
'👋🏻 JavaScript > 📖 자바스크립트 ES6+' 카테고리의 다른 글
[JS] prototype을 살짝! 알아보자 (0) | 2023.06.21 |
---|---|
[JS] Object 변환에 대해 알아보자 (0) | 2023.06.20 |
[JS] String 오브젝트 중 다양한 메서드를 알아보자 (0) | 2023.06.20 |
[JS] String 오브젝트 중 유니코드(Unicode)를 알아보자 (0) | 2023.06.20 |
[JS] Number 오브젝트를 알아보자 (0) | 2023.06.19 |