때때로 어떤 객체가 특정 property를 갖고 있는지를 조건으로 삼는 경우가 있다. 내 경우에는 따로 알아볼 것도 없이
if (obj.props === undefined) {
...
}
같은 방식으로 확인했다. 물론 이런 방식은 해당 key가 있든 없든 할당 된 값이 없다면 무조건 만족해버린다는 점에서 불분명하다는 단점이 있다. 하지만 기억상 거의 모든 경우에 문제가 되지는 않았다.
그러나 in operator를 사용하면 이점이 분명해지고, 코드의 의도 또한 잘 드러나게 된다. 필요할 때 적극 사용하자.
참고로 true/false를 리턴한다.
// use case
const obj = {
name: 'sangyeon',
weight: 129
}
console.log(age in obj) // false
단, integer 형태의 propperty들이 가장 먼저, 오름차순으로 정렬되어 iterate 된다. 그리고 나서는 차례대로 나오긴 한다.
분명 규칙이 있긴 하지만 객체에게 그런 역할을 요구하지 않는 것이 좋겠다...
자꾸 까먹지 말자..
객체의 복사는 Object.assgin() 메소드 또는 spread syntax {...obj}를 사용한다. 변수에 다른 객체를 그냥 할당하면 객체 자체가 아니라 객체의 주소값이 할당되기 때문이다.
그런데 어떤 객체의 속성이 원시 타입의 데이터가 아닌 객체 타입의 데이터를 가지고 있다면? 해당 속성은 역시 객체 자체가 아니라 객체의 reference를 가지고 있는 상태다. 이 때 Object.assign 메소드 또는 spread syntax를 사용하면 nested cloning된다. 즉 새로운 변수에 할당된 객체가 원본 객체의 속성에 할당된 객체의 reference를 공유하게 된다.
const objOrigin = {
spec: {
power: '10J',
expense: 500
}
};
const objCopy = Object.assign({}, objOrigin);
console.log(objOrigin.spec === objCopy.spec) // true, 제대로 복제 된 객체라면 같을 수 없다.
이런 문제를 해결하기 위한 내장 메소드가 structuredClone() 이놈이다.
const objCopyDeep = structuredClone(objOrigin);
console.log(objOrigin.spec === objCopyDeep) // false, 서로 다른 주소값을 가진 각자의 객체를 가졌다.
그러나 이것도 부족할 때가 있다. 객체의 속성에 원시타입도, 객체 타입도 아닌 함수가 할당되어 있는 경우다. 이런 경우까지 커버할 수 있는 객체 복사 메소드는 직접 만들어서 써야한다. 물론 직접 만들어 주신 선배님들을 찾는게 더 좋다.
일례로 매우 유명한 library인 lodash의 _.cloneDeep() 메소드가 있다.
핵심 개념은 unreachable이다. global 변수를 root로 여기고 이 뿌리로부터 reference된 객체들을 차례로 찾아 marking 한다. 연쇄적으로 reference된 모든 객체를 marking 했다면, 이제 그 밖의 모든 것들 즉 unreachable한 객체들을 모두 삭제하는 것이다. 이른바 'mark-and-sweep' 방법이다. 매우 상식적인 듯 하다.
이런 일련의 과정을 언제 어떤 방식으로 진행할지가 성능을 좌우한다. 예를 들어 cpu idle 상태에만 돌린다던가, 수차례 사용된 객체는 더 긴 주기로 판단한다던가 하는 알고리즘들이다. 당연히 js엔진마다 다른 알고리즘이 적용되어있다.
The value of this is the object “before dot”, the one used to call the method.
달리 말하면 this는 method가 call 될 때 비로소 값을 가지게 된다. 특이사항으로 arrow function은 이처럼 this를 갖지 못한다. 만약 arrow function에서 this가 쓰인다면, 그것은 outer context에서 참조 된 것이다.
심볼은 고유하다. 여러 context들에서 사용되는 객체를 오염시키지 않기 위해 사용하는것이 기본이기 때문이다. 그러나, global symbol registry에 등록 된 심볼은 다른 어떤 context에서도 호출 할 수 있다. 다른 위치에서 접근하고 싶은 경우도 있을테니까.
const id = Symbol.for("file_id");
이처럼 할당하는데, 이때 global symbol registry에 없는 경우 알아서 만들어 등록한 뒤 할당한다.
심볼은 string 타입으로 자동 변환되지 않는다.
예를들어 obj[0] 라던가 obj = { 100: "nice", age: 199 } 같은 곳에서 대괄호 속의 숫자나 객체의 key값은 자동으로 string타입으로 변환된다. 그러나 Symbol() 만큼은 보호된다.
흔히 표현하듯 숨겨진 속성이기 때문에 for in 같은 iteration에서도 제외 된다. 하지만 사용상 그렇다는 거지 굳이 접근 하려면 어디서든 접근할 수 있다. 코드에 장난치지 말자.
Object.getOwnPropertySymbols(obj) //or Reflect.ownKeys(obj)
자바스크립트는 자동으로 형 변환이 된다. 예를들어 문자열 "1"에 숫자 1을 곱하면 숫자가 된다. 또는 console.log(obj) 시도했더니 [object object]처럼 나오는것도 많이 봤을 것이다.
여기에도 당연히 규칙이 있고, 이 규칙을 객체마다 정의 할 수도 있다.
그러나 굳이 그 방법들까지 익혀 둘 필요는 없다고 생각한다. 때로는 나도 자동 형변환에 기댈 때가 있는데, 중요한 부분은 당연히 명시적인 방법을 사용한다. 또는 애초에 타입스크립트를 사용한다.