우리가 흔히 알고 있는 객체를 Object라고 하는데 기본적으로 함수도 따지고 보면 object이고 배열도 object이고 우리가 실행하는 js의 global도 object이다.
이렇게 object는 광범위한 개념을 가지고 있는데 우리가 주로 객체라고 부르며 사용하게 될 것은 아래처럼
const user = {
'': 1,
' ': 1, // 'id': 1, '0y': 2 모두 OK!
123: 1, // user[123], user['123'] OK, but user.123 is SyntaxError!!
12345n: 2, // user[12345], user[12345n], user['12345'] OK, but user['12345n'] is undefined!
true: 1, // OK user[true] user.true
id: 2,
[`name`]: 'Hong', // But, `name`: 'Hong'은 SyntaxError: Unexpected template string!
[Symbol()]: 'Hong', // OK But, Symbol(): 'Hong'은 SyntaxError: Unexpected token ':'
[`${new Date()}`]: 365, // OK! 'Sun Jul …': 365
'my-friends': ['Han', 'Kim'],
getInfo: () => `${this.id}-${this.name}`, // OK! But, this is not user!
getInfo2() { return `${this.id}-${this.name}`; }, // OK! getInfo의 최종 <f.o>
}
와 같다. user라는 object 즉 객체가 있고 이 안에 포함되어 있는 값들은 property라고 부른다.
이 property라는 개념이 처음 들을 땐 이해가 안될 수 있는데 그냥 object가 가지고 있는 속성들로 위의 예시에서 보자면 user라는 녀석은 object를 가지고있다.(식별자)
이때 user 안의 "":1과 " ":1, getInfo(){...},[Symbol()]:'Hong'들 하나하나가 user의 속성, 즉 프로퍼티라고 생각하면 된다.
이때 위의 예시처럼 객체의 프로퍼티로는 다양한 값들이 들어올 수 있는데 단순한 원시타입부터 객체, 배열, 함수, 추가로 심볼까지 들어올 수 있다.
일명 메서드로 객체 프로퍼티에서 사용되는 함수들을 통칭 메서드라 부르고 최근에는 조금 더 구분해서 부르고 있다
user 객체에서 함수가 2종류가 있는데 하나는 getInfo와 getInfo2()2가지가 있다
2종류모두 method라 할 수 있지만 정확히 더 구분하자면 getInfo는 함수 리터럴(값)을 가지는 key이고 getInfo2()가 method의 성격에 더 가깝다 생각한다.
보통 객체 내부의 함수를 작성할 때 this를 많이 사용하는데 이때 getInfo2와 같은 메서드는 사용시 무조건 user.getInfo2()와 같이 사용할 수 밖에 없고 어디 변수에 할당한다 하더라도 getInfo2()가 return하는 값이 담길 뿐이다.
그렇기에 getInfo2가 사용되는 this는 객체 자신이 되는 것인데 그러면 객체 리터럴을 가진 getInfo도 user.getInfo()로 사용하지 않나? 생각할 수 있다.
정확히는 getInfo를 그대로 사용할 때 user.getInfo()처럼 사용한다면 자기 자신이 바인딩 되어 this가 자기 자신이 되겠지만 엄연히 객체 리터럴을 가진 key로 어떤 변수에 const getInfoFucn = user.getInfo 이런식으로 담게 되면 getInfo의 값인 함수 자체가 getInfoFunc에 담기게 되어 getInfoFunc는 하나의 함수가 된다.
고로 이때 getInfoFunc를 실행한다면 엉뚱한 this가 나오는 것을 확인할 수 있다(global)
주의해서 작성해주자!
우리는 보통 프로퍼티를 변경하거나 만들 때 처음 객체 리터럴을 만들 때 할당하는 식으로 하거나
obj.id = 1
obj['name'] = 'lee'
이런식으로 .과 []를 이용해서 할당하기도 할것이다.
변경또한 똑같이 재할당하는 식으로도 하는데 몇가지를 진행해보면 [Symbol]타입은 출력이 안되는 것을 확인할 수 있다.
Object.keys(user)와 Object.values(user)를 사용해서 살펴봐도 symbol타입은 출력되지 않는다.
이때 Object.keys와 Object.values는 각각 Object가 가지고 있는 메서드로 인자로 전달된 객체의 모든 key들과 모든 value들을 각각 배열로 출력해준다.
하지만 Symbol 타입과 같은 것들은 출력해주지 않는데 어떻게 해야 출력이 가능할까?
Reflect를 사용하면 된다
Reflect엔 많은 기능들이 있는데 그 중에 ownKeys라는 메서드가 있는데 이 경우 Reflect.ownKeys(user)를 찍어볼 경우 Symbol까지 포함한 모든 key들이 출력되는 것을 확인할 수 있다
Reflect를 이용해서 오브젝트를 복사할 때 일반적으로 출력되지 않는 심볼타입들도 전부 가져와 복사 할 수 있고 현재 계속해서 Reflect가 발전중이니 유용할 것 같다.
이외에도 여러가지가 잇는데
.과 []을 이용해 프로퍼티에 접근할 수 있다(이때 []는 다이렉트로 key값을 적을 경우엔 무조건 string으로 작성해줘야하며 변수를 넣을 수도 있다in을 통해 해당 키값이 존재하는지 확인할 수 있다. 같은걸로 hasOwnProperty(key)가 있다Reflect.has(obj,key)로 확인할 수 있다Reflect를 쓰지않고 Object의 메서드를 사용하려면 Object.getOwnPropertySymbols(user)를 사용하면 된다.delete를 사용하자getOwnPropertyDescriptor(obj,key)는 해당 프로퍼티의 value값과 프로퍼티 속성들이 나온다.(작성가능한지? 노출시킬건지? 수정가능한지? 등)getOwnPropertyDescriptors(obj) s를 붙여주면 모든 프로퍼티를 확인할 수 있다Object.defineProperty(obj,key,변경할 내용)을 이용한다면 해당 프로퍼티의 속성들을 변경할 수 있는데Object.keys(obj) 와 Object.values(obj)로 각각의 키들과 값들을 배열로 받을 수 있다Object.entries(obj)를 사용하면 키와 값이 2차원 배열 즉, [[key,value],[key,value]]형식으로 나오게 된다Object.fromEntries([[key,value],[key,value]])을 통해 2차원배열을 객체로 변경할 수 있다obj가 있는데 해당 obj를 다음과 같은 방법으로 새롭게 만들었다Object.assign({},obj)와 {...obj}, new Object(obj) 면 전부 같을까?obj === new Object(obj)만 true가 나오게 된다Object.preventExtensions(obj)를 넣게 되면 객체가 확장되는 것을 방지 한다( 추가(X),삭제,읽기,쓰기,재정의) x를 제외한 나머지는 가능하다Object.seal(obj) 추가(X),삭제(X),재정의(X), 읽기,쓰기Object.freeze(obj)는 아예 얼려버리는 것으로 읽기를 제외한 모든것이 안된다(enumerable만 false)