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
        
    • 활용한 형태
      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
            }         
            
  • 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
        }
        
      • 상속받은 오브젝트는 반환하지 않는다.

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를 참조하려면 인스턴스를 생성하고 인스턴스의 메소드를 호출해야 한다.
    • __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를 사용할 수 있다.
      • 추가한 메서드를 인스턴스에 공유
  • 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에 연결된 프로퍼티를 설정
        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+