자바스크립트에서 비트연산자는 우리에게 이상한 야생의 세계를 소개합니다. (12 & 3) = 0
이 되고 (12 & 4) = 4
가 되는 세상이죠. 진짜입니다. 콘솔에서 지금 당장 실행해보세요. 때때로 비트 연산자는 우리가 어떻게 해결해야 할지 확신이 없는 문제들을 해결해주는 해결사가 될 수도 있습니다.
오브젝트 안에 있는 4개의 독립적인 true/false 변수들의 존재를 체크하고 저장하는 가장 좋은 방에 대해 논의해보겠습니다. 이 속성들은 foo1
부터 foo4
라고 불러봅시가. JavaScript(ES6)에서의 표현법은 아마 다음과 같을 것입니다.
const myObject = {
foo1: false,
foo2: true,
foo3: false,
foo4: true,
};
상당히 직관적입니다. 하지만 우리의 어플리케이션은 이 속성들의 아주 많은 조합을 체크해야 될 필요가 있습니다. 어렵기도 하고, 언젠가는 하나의 추가적인 속성을 더 추가해야 할 수도 있습니다. 이 문제를 해결하기 위해서 두 가지 가장 확실한 옵션이 있습니다.
const hasFoo2andFoo4 = {
foo1: false,
foo2: true,
foo3: false,
foo4: true
}
const hasFoo3andFoo4 = {
foo1: false,
foo2: false,
foo3: true,
foo4: true
}
// ... 나머지 경우의 수 ...
// 그 후에
if (isEqual(myObject, hasFoo2andFoo4)) {
// 오브젝트가 Foo2와 Foo4만 가지고 있는지 알 수 있습니다.
}
보이는 그대로, 이 방법은 구립니다. 비교하기 위해 16개의 모델 오브젝트를 생성해야 할 것입니다. 이러한 작업은 작은 정보를 얻기 위해서 overhead가 너무 크다고 느껴집니다. 게다가, 우리가 나중에 또 다른 속성을 추가한다면, 모델 오브젝트를 두배로 늘려야 할 것입니다. 분명히, 이 방식은 피해야 합니다. 하지만 다른 옵션은 더 나쁠 수도 있죠...
if (myObject[2] && myObject[4] && !(myObject[1] || myObject[3])) {
// 우린 오프젝트가 Foo2와 Foo4만 갖고 있다는 것을 알 수 있습니다.
}
이 방법은 정말 생생한 악몽입니다. 클라이언트 사이드 코드에 약 백만개의 문장을 추가해야 할 것입니다. 이 방법은 처음부터 오류가 발생하기 쉬운 방법입니다. 그리고 후에 어떤 속성이 바뀌거나 새로운 속성이 추가됐을 때, 엄청난 작업이 필요할 것입니다. 그래서 우리는 어떻게 해야 할까요?
자바스크립트 내부의 모든 정수들은 2진법으로 표기될 수 있습니다. toString(2)
를 호출함으로써 그들이 어떻게 변하는지 보세요.
(1).toString(2); // 1
(2).toString(2); // 10
(3).toString(2); // 11
(4).toString(2); // 100
이제 이론을 알았습니다. 이제 중요한 부분으로 가봅시다. 이 방법에 숨어있는 진짜 트릭은 비트연산은 이 바이너리 문자열들을 직접 다루고 비교할 수 있게 해준다는 것입니다. 바이너리 문자열 오른쪽에 0을 넣어주는 <<
비트 연산은 우리의 10진법 정수를 2진법의 규칙에 맞게 증가시킵니다.
비트연산 비교에서 가장 중요한 것은 "&"와 "|"입니다. "&&"와 "||"와 형태가 매우 닮은 것은 의도적인 것입니다. "&"은 비교하는 두 개의 숫자가 교집합임을 표기하는 것입니다. "|"은 합집합을 의미합니다. 1010 & 1001
을 하게 되면 1000
이 리턴됩니다. 왜냐하면 왼쪽 끝에 있는 1
이 유일하게 공통된 1
이기 때문입니다. 1010 | 1001
은 1011
을 반환할 것입니다. 왜냐하면 OR연산은 둘 중 하나만 있어도 값이 성립하기 때문입니다.
예제를 확인해보겠습니다.
const myObject = {
foo1: false,
foo2: true,
foo3: false,
foo4: true,
}
const HAS_FOO1 = 1;
const HAS_FOO2 = 1 << 1;
const HAS_FOO3 = 1 << 2;
const HAS_FOO4 = 1 << 3;
let myBitNumber = 0
if (myObject['foo1'] === true) {
myBitNumber = myBitNumber | HAS_FOO1;
}
if (myObject['foo2'] === true) {
myBitNumber = myBitNumber | HAS_FOO2;
}
if (myObject['foo3'] === true) {
myBitNumber = myBitNumber | HAS_FOO3;
}
if (myObject['foo4'] === true) {
myBitNumber = myBitNumber | HAS_FOO4;
}
console.log(myBitNumber.toString(2)); // 1010
이제 테스트 해봅시다. 속성에 대해 비트연산 숫자를 체크하고 있다면, 체크할 수 있는 4가지가 있습니다. 우리의 숫자가 하나의 명확한 속성을 가지고 있든 아니든, 어떤 주어진 속성의 배열을 가지고 있든 아니든, 명시된 속성들만 가지고 있든 아니든 혹은 속성의 배열을 전부 가지고 있든 아니든말입니다.