JS 값 비교 방법
- is()
- 형태: Object.is()
-
파라미터: 비교 대상 값 비교 대상 값 -
반환: 타입까지 같으면 true, 아니면 false
- 두 개의 파라미터 값과 값 타입을 비교하여 같으면 true, 아니면 false를 반환
- 오브젝트 비교 목적이 아니다.
- []와 [] 비교, {}와 {} 비교하면 false
- 객체가 참조하는 메모리가 다르기 때문에 false를 반환한다. ```javascript const result = Object.is(10, “10”); console.log(result); // false
const one = {}, two = {}; console.log(Object.is(one, two)); // false ```
- JS 값 비교 방법
- 값과 타입까지 모두 비교: ===
- 타입은 비교하지 않고 값 비교: ==
console.log(undefined == null); // true console.log(undefined === null); // false console.log(Object.is(undefined, null)); // false
- Object.is()와 === 차이
- NaN 비교
console.log(NaN === NaN); // false console.log(Object.is(NaN, NaN)); // true console.log(NaN === 0 / 0); // false console.log(Object.is(NaN, 0 / 0)); // true
- +0, -0 비교
console.log( 0 === -0 ) // true console.log(Object.is(0, -0)); // false
- NaN 비교
- 활용한 형태
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()
- 형태: Object.assign()
- 파라미터: 열거 가능 오브젝트, 열거 가능 오브젝트(option) - 다수 작성 가능
-
반환: 첫 번째 파라미터 오브젝트
- 두 번째 파라미터의 오브젝트 프로퍼티를 첫 번째 파라미터의 오브젝트에 복사하고 첫 번째를 반환
const sports = { event: '축구', player: 11 } let dup = {} Object.assign(dup, sports); console.log(dup); // { event: 축구, player: 11 }
- own property만 복사
- 첫 번째 파라미터 작성
- 첫 번째 파라미터는 무조건 작성한다.
try { const obj = Object.assign(null, {}); } catch(e) { console.log("null 작성 불가"); } // null 작성 불가 출력
- Number, String, Symbol, Boolean 값 작성
const obj = Object.assign(100); console.log(obj); // Number {100} console.log(obj.valueOf()); // 100
- 첫 번째 파라미터에 Number를 작성하고 두 번째 파라미터를 작성하지 않았다.
- Number 인스턴스를 생성하여 파라미터 값 100을 [[PrimitiveValue]]에 설정한다.
- 생성한 인스턴스를 반환한다.
- Boolean, String, Symbol도 같은 방법으로 처리한다.
- 첫 번째 파라미터는 무조건 작성한다.
- 두 번째 파라미터 작성
- 열거 가능 오브젝트 작성
let obj = {}; Object.assign(obj, {ten: 10}); console.log(obj); // {ten: 10} const one = Object.create({}, { book: {value: 100, enumerable: false}, sports: {value: 200, enumerable: true} }); Object.assign(obj, one); console.log(obj); // {ten: 10, sports: 200} book은 열거 불가능이기 때문에 할당되지 않는다.
- 오브젝트 다수 작성
const book = {title: "책"}; const sports = {item: "축구"}; const obj = Object.assign({}, book, sports); console.log(obj); // {title: 책, item: 축구}
- 만약 프로퍼티 이름이 같은 경우 대체된다.
- 값을 작성
let obj = {ten: 10}; Object.assign(obj, undefined, null, 200); console.log(obj); // {ten: 10} const one = {un: undefined, nu: null}; Object.assign(obj, one); console.log(obj); // {ten: 10, un: undefined: nu: null}
- 값으로 작성한 undefined, null, 200이 복사되지 않는다.
- 열거 가능한 오브젝트가 아니기 때문이다.
- 값과 오브젝트를 작성
- 값이 설정된 인스턴스 형태
const obj = Object.assign(100, {book: 200}); console.log(obj.valueOf()); // 100 console.log(obj.book); // 200
- 100이 므로 Number 인스턴스가 생성된다.
- 두 번째 파라미터가 Object이므로 생성한 Number 인스턴스에 복사
- Object이므로 복사는 가능하나 Number 인스턴스에 Object를 복사하는 것은 데이터 타입이 맞지 않다.
obj: Number { book: 200 __proto__: Number [[PrimitiveNumber]]: 100 }
- 값이 설정된 인스턴스 형태
- 열거 가능 오브젝트 작성
- assign()
- deep copy
-
deep copy란 다 단계 계층 구조에서 값이 연동되지 않도록 복사하는 것을 말한다.
- object를 할당하면 프로퍼티 값이 연동된다.
- 한 쪽 오브젝트의 프로퍼티 값을 바꾸면 다른 오브젝트의 프로퍼티 값도 바뀐다.
const sports = { item: "축구" }; let copy = sports; sports.item = "농구"; console.log(copy.item); // 농구
- 한 쪽 오브젝트의 프로퍼티 값을 바꾸면 다른 오브젝트의 프로퍼티 값도 바뀐다.
- assign() 함수로 복사
const sports = { item: "축구" }; const 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안에 Object가 있는 형태로 item.title 값은 연동된다.
- 이것은 프로퍼티를 복사하지 않고 Object 참조를 복사하기 때문이다.
- 연동되지 않게 하려면 프로퍼티 단위로 복사
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); // 자바스크립트
- 프로퍼티 단위로 복사하면 연동되지 않지만, 단게의 깊이가 유동적이면 코드가 복잡해진다 -> 재귀 함수
- 다단계 계층 구조에서 값이 연동되지 않도록 복사하는 것을 deep copy, deep clone이라고 한다.
- JSON 함수 활용
const book = { item: {title: "자바스크립트"} }; const copy = JSON.parse(JSON.stringify(book)); // JSON.stringify로 문자열을 반환 후 JSON.parse()로 파싱하면 연동되지 않은 deep copy 실행 book.item.title = '책'; console.log(copy.item.title); // 자바스크립트
-
Object 변환
- entries()
- 형태: Object.entries()
- 파라미터: 열거 가능한 오브젝트
-
반환: [[Key,Value], …] 형태
- 열거 가능한 오브젝트의 {Key: Value, …}를 [ [Key, Value], …] 형태로 변환
const obj = {music:"음악", book:"책"}; const list = Object.entries(obj); for(let keyValue of list) { console.log(keyValue); // [music, 음악], [book, 책] }
- 작성한 순서가 바뀌는 경우
const obj = {10: '십', book: '책', 7: '칠'}; const list = Object.entries(obj); for(let keyValue of list) { console.log(keyValue); // [7, 칠], [10, 십], [book, 책] }
- key가 영문자일 때는 key 값을 분류하지 않고 작성한 대로 반환
- 반면, 숫자와 문자가 섞여 있으면 숫자, 문자 순서로 분류
- 문자열은 하나씩 분리
const list = Object.entries("ABC"); for( let keyValue of list) { console.log(keyValue); //[0, A], [1, B], [2, C] }
- values()
- 형태: Object.values()
- 파라미터: 열거 가능한 오브젝트
-
반환: [values] 형태
- 열거 가능한 오브젝트의 {key: value} 를 값만 [value1, value2] 형태로 반환
const obj = {music: "음악", book: "책"}; const list = Object.values(obj); for(let value of list) { console.log(value); // 음악, 책 }
- 작성한 순서가 바뀌는 경우
const obj = { 10: "십", book: "책", 7: "칠" }; const list = Object.values(obj); for(let value of list) { console.log(value); // 칠, 십, 책 }
- 문자열은 문자 하나씩 분리
const list = Object.values("ABC"); for(let value of list) { console.log(value); // A, B, C }
- fromEntries()
- 형태: Object.fromEntries() // ES2019
- 파라미터: Iterable Object
-
반환: 새로운 오브젝트
- [[key, value]] 형태를 {key: value} 형태로 변환
const list = [["one", 10], ["two", 20]]; const obj = Object.fromEntries(list); console.log(obj); // { one: 10, two: 20}
- 프로퍼티 키 값이 같으면 값 대체
const list = [ ["one", 10], ["one", 20] ]; const obj = Object.fromEntries(list); console.log(obj); // {one: 20}
- getOwnPropertyDescriptors()
- 형태: Object.getOwnPropertyDescriptors()
- 파라미터: 대상 오브젝트
-
반환: 프로퍼티 디스크립터를 포함한 오브젝트
- Object 프로퍼티 디스크립터를 반환
- Data Descriptor
const obj = {music: "음악"}; const des = Object.getOwnPropertyDescriptors(obj); for(let name in des.music) { console.log(name + " : " + des.music[name]); // value: 음악, writable: true, enumerable: true, configurable: true }
- 프로퍼티 디스크립터 중에서 데이터 디스크립터를 반환한다.
- Access Descriptor
const obj = { get music(){} } const des = Object.getOwnPropertyDescriptors(obj); for(let name in des.music) { console.log(name + " : " + des.music[name]); // get: get music() {}, set: undefined, enumerable: true, configurable: true }
- 상속받은 오브젝트는 반환하지 않는다.
- Data Descriptor
prototype과 proto
- 메소드 호출 방법
- prototype 과 __proto__에 연결된 메소드를 호출하는 방법이 다르다.
- prototype에 연결된 메소드 호출
function Book() { this.point = 100; }; Book.prototype.getPoint = function() { console.log(Object.is(this, Book.prototype)); return this.point; } console.log(Book.prototype.getPoint()); // true, undefined console.log(Book.prototype.getPoint.call(Book)); // false, undefined
- Book.prototype.getPoint()
- prototype을 작성하여 호출하면 getPoint()에서 this가 Book.prototype을 참조
- Book.prototype.getPoint.call(Book)
- this가 Book을 참조
- this.point를 참조하려면 인스턴스를 생성하고 인스턴스의 메소드를 호출해야 한다.
- Book.prototype.getPoint()
- __proto__에 연결된 메소드 호출
- 인스턴스에 생성하여 호출
- new 연산자로 생성한 인스턴스 구조
function Book() { this.point = 100; }; Book.prototype.getPoint = function() { return this.point; } const book = new Book(); console.log(book.getPoint()); // 100
- 인스턴스에 함수로 추가
- 함수로 추가
- new 연산자로 인스턴스를 생성하고 인스턴스의 프로퍼티로 함수를 추가. 다른 인스턴스와 공유하지 않음
- 인스턴스에 추가한 후의 인스턴스 구조
function Book { this.point = 100; }; Book.prototype.getPoint = function() { return this.point; }; const obj = new Book(); obj.setPoint = function(param) { this.point = param; }; obj.setPoint(200); // book 객체의 __proto__와 같은 레벨로 들어간다. console.log(obj.getPoint()); // 200 // book 객체의 __proto__안에 들어간다. const newObject = new Book(); console.log(newObject.getPoint()); // 100
- 인스턴스의 프로퍼티로 설정했기 때문에 새로운 인스턴스를 생성하면 setPoint()를 사용할 수 없다
- 인스턴스 프로퍼티로 연결한 것과 prototype에 연결한 메서드의 차이
- 함수로 추가
- __proto__에 메서드 추가
- 메서드 추가
- __proto__에 function을 추가하면 prototype에 설정되며 메서드로 추가하는 것과 같다
- __proto__에 추가한 후의 prototype의 모습
function Book(param) { this.point = param; }; Book.prototype.getPoint = function() { return this.point; }; const obj = new Book(100); obj.__proto__.setPoint = function(param) { this.point = param; } const newObj = new Book(200); newObj.setPoint(100); console.log(newObj.getPoint()); // 100
- obj.proto.setPoint
- obj를 펼치면 __proto__에 setPoint가 표시된다.
- Book.prototype을 펼치면 setPoint가 표시된다.
- 새로운 newObj 인스턴스를 생성해도 setPoint를 사용할 수 있다.
- obj.proto.setPoint
- __proto__에 추가한 후의 prototype의 모습
- 추가한 메서드를 인스턴스에 공유
- __proto__에 function을 추가하면 prototype에 설정되며 메서드로 추가하는 것과 같다
- 메서드 추가
- setPrototypeOf()
- 형태: Object.setPrototypeOf()
- 파라미터: 오브젝트 또는 인스턴스, 오브젝트의 prototype 또는 null
-
반환: 첫 번째 파라미터
- 인스턴스 사용
- 첫 번째 파라미터의 prototype으로 두번째 파라미터 설정
let obj = {0: 10, length: 1}; Object.setPrototypeOf(obj, Array.prototype);
- obj는 인스턴스이다.
- 인스턴스에는 prototype이 없으며 __proto__가 있으므로 __proto__에 설정하는 것과 같다.
- obj __proto__에 Object.prototype은 사라지고 Array.prototype이 설정된다.
- 첫 번째 파라미터에 인스턴스 작성
- setPrototypeOf() 실행 후 인스턴스 구조
- ES5에 getPrototypeOf()가 있음
- 첫 번째 파라미터의 prototype으로 두번째 파라미터 설정
- prototype 사용
- 첫 번째 파라미터의 prototype을 작성
- 두 번째 파라미터의 prototype에 연결된 프로퍼티를 설정
function Book() {}; Book.prototype.getBook = function() {}; function Point() {}; Point.prototype.getPoint = function() {}; Object.setPrototypeOf(Point.prototype, Book.prototype);
- Point.prototype에 Book.prototype에 연결된 프로퍼티를 설정
- Point.prototype에 설정하므로 이것을 펼치면 Book.prototype.getBook 이 있어야 하는데 없다.
- 또한, Point.prototype에 연결한 메서드가 지워지지 않고 유지된다.
- 한편, Point.prototype.__proto__를 펼치면 getPoint()가 표시된다.
- setPrototypeOf() 함수는 prototype에 __proto__를 만들고 여기에 설정한다. prototype을 확장시킨다.
- 확장이 목적이지 상속을 위한 것은 아니다.
- super등의 상속 처리 키워드를 제공하는 Class를 사용하는 것이 좋다.
** 출처1. 인프런 강좌_자바스크립트 ES6+