JavaScript 자주쓰는 관용구

close up photo of codes

 

개요


자바스크립트에서는, 처음 봤을때 전혀 해석이 안되는 문법, 이해가 되지않는 내용 등을 자주 볼 수 있습니다. 어느 정도 익숙한 사람은 당연히 사용하는 관용구가 상당히 많이 있습니다. 알아두면 좋은 자주쓰는 관용구와 그 의미를 정리해 보았습니다.

 

 

내용


+v (문자열을 숫자로 변환)

var v = "123";
console.log(+v + 100) // 223
console.log(v + 100)  // 123100

V를 숫자로 변환하는 방법중은 가장 일반적인 방법입니다. parseFloat(V)에 비해 속도가 빠르고 parseFloat와는 약간의 차이가 있습니다.(예를 들어 빈 문자열의 경우, parseFloat라면 NaN이 되지만,  +V의 경우는  0이 됩니다). 반드시 숫자변환이 검증되고 문자열등으로 숫자로 변환 할 수 없는 경우는 NaN이 리턴됩니다.

 

 


v -0 (문자열을 숫자로 변환)

var v = "123"; 
console.log((v - 0) + 100) // 223
console.log(v + 100)       // 123100

 

이번에도 숫자 변환입니다. +V에 비해 사용되는 것은 아니지만, 소스를 보는 것 만으로 어느 정도 의미가 이해하기 쉬운 메리트가 있습니다. 개인적인으로는 +V가 널리 사용되는 방법으로서 V-0을 사용하는 것보다는 +V를 사용하는 편이 좋을 듯 합니다.

 

 


v + “” (문자열로 변환)

var n = 123;
console.log(n + 123);      //246
console.log(n + "" + 123); //123123

 

문자열로 변환하기 위한 관용구입니다. 자주 사용되지만, 눈으로 내용을 보는 것 만으로도 의미를 알 수 있을까 생각합니다.

예를들어

var n = 123;
n = n.toString();

 

위 코드는 이것은 런타임 최적화를 저해하는 요소가 많은 코드이며, 같은 가독성인 V+""를 사용하는 것을 추천합니다.

그 외에도 가끔씩 보이는 것이 String() 입니다.

var n = 123;
n = String(n);

 

이것은 속도적으로는 문제가 없지만, 자칫 잘못해서 new String(n) 이라고 기술하는 분들이 간혹 있습니다. 더구나 new String에서도 에러없이 동작합니다. 하지만 절대 피해야 할 방법입니다. 왜냐하면 new String로 만들어진 String 객체는 원래 Object이며 String이 아닙니다. 이 Object를 문자열로서 평가하려고 할 때에는, 내부적으로 매번 toString 함수(혹은 valueOf 함수)가 불려 Object로부터 String에 변환되게 되어, 극한의 낭비가 되기 때문입니다.

String.prototype.valueOf = function() { return "aaa"; };
var n = 123;
var strobj = new String(n);
var str = String(n);
console.log("[" + strobj + "]"); // [aaa]
console.log("[" + str + "]"); // [123]

 

 


if(v != null) (null undefined의 체크)

var o = { value: 0 };
if(o.value != null) { console.log("value property exists"); }

 

일반적으로 많이 쓰여지기 때문에 놓치기 쉽지만 null과 undefined를 확인하는 매우 중요한 관용구입니다. 어느 정도 JavaScript에 익숙해지면, 속성의 존재 확인을 boolean의 자동 변환에 의존해 쓰기 쉽습니다.

예를 들면 if(o.value) { ... } 이런 식으로 쓰여지곤 하는데 value가 Object라면 문제가 되지 않지만, value가 숫자나 문자열일 경우, 만약 value가 0이나 빈 문자열이었을  경우에는 그것을 찾아내기가 어렵고 좀 처럼 발견하기 어려운 버그로 남아 버립니다.

if(v != null)로 비교했을 경우, V가 null 의 경우와 undefined의 경우만 걸리고, V가  0의 경우나 빈 문자열의 경우는 걸리지 않고 그대로 통과시킵니다. 이부분을 의식하지 않고 작성하는 것이 대부분이기 때문에 if(v != null)if(v)로 쓰지 않도록 조심해야 합니다.

 

 


(function(){ … })();  범위 (스코프화)

;(function() {
  var x = "hello world";
  console.log(x);
})();

 

소스 코드의 처음과 끝을 보게 되면, 모르는 사람에게는 수수께끼의 function으로 보여집니다. JavaScript는 함수 안에 쓰지 않는 변수 선언은 모두 글로벌 변수로 취급되어 버리기 때문에, 글로벌 스코프에 영향이 가지 않게 함수식으로 코드 전체를 둘러싸고 있습니다.

함수식을 선언함과 동시에, 실행하는 처리부분을 같이 쓰고 있기 때문에, 좀 더 알기 쉽게 다시 써보면

var f = function() {
  var x = "hello world";
  console.log(x);
};
f();

 

위의 내용과 같습니다. (이 경우, f는 글로벌 변수)

그리고 function의 앞에 왜 괄호가 「(」있는가에 대해서는, 이것이 없으면 함수식이 아니고 보통의 함수 선언이라고 해석되어 버려, 구문 에러가 되어 버립니다.

첫 번째 세미콜론은 없어도 괜찮습니다. 그러나 자신이 모르는 곳에서 라이브러리 등으로 파일 결합되었을 경우, 직전의 파일의 마지막에 세미콜론이 빠져 있다고 구문 에러가 되어 버리는 경우도 발생하기 때문에, 만약을 위해서 세미콜론을 붙이는 경우입니다.

 

 


&& (if 문 생략)

var o = { f: function() { console.log("hello world"); } };
o && o.f && o.f();

 

&& 연산자는, Java등에서는 boolean형 밖에 사용할 수 없지만, JavaScript에서는 어떠한 형태에도 사용할 수 있습니다. 이것을 이용해 if문을 쓰지 않고 오브젝트의 존재 판정을 할 때 매우 자주 사용되는 관용구입니다.

예를 들어 위의 예를 if 문을 사용하여 쓰면 다음과 같이 됩니다.

var o = { f: function() { console.log("hello world"); } }; 
if(o) {
  if(o.f) {
    o.f();
  }
}

 

이처럼 중복 if 문을 작성하지 않기 위해 &&를 사용하여 간략하게 사용 할 수 있습니다. 특히 함수의 앞에서, 객체가 특정의 프로퍼티를 가지고 있는지를 확인해, 가지고 있는 경우는 그것을 실행하는 처리를 할 때에 자주 사용됩니다.

안에 대입을 쓸 때는 대입을 괄호로 묶지 않으면 퍼싱에러 parse error가 되므로 조심해야 합니다.

o && o.options && o.options.players > 50 && (massiveFlag = true);

 

덧붙여 이 관용구는 숫자 변환 및 문자열 변환, Object가 내부에서 암묵적으로 boolean으로 변환되는 것을 이용하고 있습니다. 숫자 0이나 , NaN 빈 문자열, null, undefined가 False로 변환되지만, 이 관용구를 사용하는 경우는 예기치 않은 자동 변환을 피하기 위해서도 null이나 undefined의 확인하는 것이 좋습니다.

 

 


|| (if문 생략)

이것도 if문의 생략입니다.

var v = (o && o.f) || function() { console.log("hello world"); };
v();

 

&&와 마찬가지로 if 문을 생략하고 쓸 때 사용됩니다. 변수의 대입시에 undefined를 피하기 위해서 사용되는 경우가 많습니다.

이것을 if 문으로 쓰게 되면,

var v;
if(o) {
  if(o.f) {
    v = o.f;
  }
}
if(!v) {
  v = function() {
    console.log("hello world");
  }; 
}
v();

 

이 됩니다. &&와 같이 사용할 수 있어서, 둘 다 동시에 사용되는 경우가 많습니다.

이쪽도 암묵의 형태 변환을 이용하고 있어서, 어느 값이 True, 어느 값이 False가 되는지는 확실하게 파악하는 것이 좋습니다.

&&나 ||를 사용하는 것과 if문을 사용하는 것은 스피드면에서 거의 차이가 없습니다.

 

 


v = v || {}

function f(o) {
  o = o || {};
  ...
}

 

함수의 맨 앞부분에서, 디폴트 인수의 설정등에 자주 사용되는 관용구입니다. 예를 들어0가 undefined일 때에 디폴트 값으로 빈 객체를 설정하는 등의 용법으로 사용됩니다.

 

 


+v || 0 (숫자로 변환, 그리고 NaN 이외를 검증)

var n = +v || 0;

 

V가 숫자 변환이 실패하면 NaN이 아닌 0이 되는 관용구입니다. V0이였을 경우도 0가 됩니다.

 

 


v | 0 (정수화)

var i = v | 0;

V를 정수로 만듭니다. JavaScript에서는 비트 연산은 정수형에 대해서만 적용할 수 있기 때문에 이것을 이용하여 강제적으로 정수로 만드는 관용구입니다. V가 문자열인 경우에도 먼저 숫자화가 된 다음 정수화됩니다.

 

Math.floor도 비슷한 처리이지만, 마이너스 숫자를 취급했을 경우의 결과 값이 다르므로 주의해야 합니다.

console.log(-2.1 | 0);         // -2
console.log(Math.floor(-2.1)); // -3

 

그리고, 비트 연산자는 연산자의 우선 순위를 틀리는 경우가 많기 때문에, 수식중에서 사용할 때에는 괄호를 쓰는 편이 좋습니다.

 

 


~~v (정수화)

console.log(~~(2.1)); // 2

 

V | 0과 같이 정수화하는 관용구입니다. V | 0를 사용하는 쪽이 일반적이고 한눈에 이해하기 쉽습니다.

비슷한 관용구로 V >> 0 (비트 시프트)도 있습니다.

 

 


!! (논리화)

var b = !!v;

 

V의 값을 boolean으로 변환하는 관용구입니다. 예를 들어 빈 문자열, 0, NaN, undefined, null등을 False로 설정하고, 그렇지 않으면 True로 설정합니다.

실제로는, True/False를 명확하게 해야 하는 상황은 그다지 많이 없지만, 다른 언어로부터의 이식등의 라이브러리등에서는 종종 있습니다.

 

 


if(v != v) (NaN체크)

var v = +"a"; // NaN
if(v != v) { console.log("v is Not a Number!"); }

 

NaN 체크입니다. NaN은 자바스크립트에서 유일하게 자신과의 비교에 실패하는 값입니다. (NaN과 비교는 상대가 어떤 값이라도 반드시 실패합니다). 따라서 숫자의 경우에도 0과 NaN을 비교할 때 이 관용구를 사용할 수 있습니다.

그러나 이것은 isNaN함수와 같은 결과가 됩니다. NaN 체크에는 isNaN을 사용하는 편이 읽기 쉬운 코드가 될거 같습니다.

isNaN함수는 문자열 ("a")을 숫자로 변환했을 때 NaN으로 변환되는 것도 NaN이라고 판별합니다. 반면, V != V라고 순수하게 NaN만을 판별할 수 있습니다 (숫자 이외의 값에 대해서는, isNaNV != V의 동작의 결과가 다르기 때문에 주의해야 합니다.

 

 


~array.indexOf() (배열 값 검색)

var a = [1, 2, 3, 4, 5];
if(!~a.indexOf(0)) { console.log("0 is not in a"); }
if(!~a.indexOf(3)) { console.log("3 is not in a"); }
if(~a.indexOf(5)) { console.log("5 is in a"); }

 

indexOf 한정의 관용구입니다. indexOf 함수는 요소가 없으면  -1을 반환하고 존재하는 경우 요소의 index를 반환합니다. 간단히 말하면 실패 == -1, 성공 >= 0입니다.

거기에 비트 부정을 넣습니다. 비트 부정으로 왜 그렇게 되는지에 대해서는 생략하지만,  -1의 비트 부정은 0이 되고, 0을 포함한 그 외의 모든 값의 비트 부정은  0이외가 됩니다.

따라서, indexOf의 반환값에 비트 부정을 걸었을 경우, 실패시(-1)의 경우만 0, 성공시는 0이외가 되므로, 그것을 이용해 배열안에 요소가 있는지 어떤지를 알 수 있습니다.

이것은 가독성이 나쁜 편에 속하며, 보통 a.indexOf(V) != -1과 비교했을 경우에 비해 속도적으로 전혀 메리트가 없습니다. 일부 오픈 소스 프로젝트에서 다양하기 때문에 의미를 알아두는 것에 의미를 두고, 직접 작성하는 것은 권장하지 않습니다.

문자열의 경우에도 이용할 수 있지만, 최근의 브라우저에서는 indexOf를 사용하는 것보다 정규 표현식을 사용하는 편이 더 빠릅니다.

var s = "Hello world"; 
if(/e/.test(s)) { console.log("e exists"); }

 

 

 


new Function(“return this;”)() (글로벌 객체의 취득)

var global = new Function("return this;")();
console.log(global.JSON);

 

글로벌 객체의 취득입니다. 일반적으로 브라우저의 글로벌 객체는 window에서 가져올 수 있습니다. 하지만 node.js등에서는 이것을 이용할 수 없기 때문에 이용 환경에 따라서는 위의 관용구로 객체를 가져오게 됩니다.

이전에는 좀 더 쉽게

var global = function(){ return this; }();

 

이런식으로 가져올 수 있었는데, Strict Mode에서는 함수내의 this가 undefined를 돌려주게 되기 때문에 이 방법은 사용할 수 없습니다. 항상 strict mode가 있는 것을 전제로, new Function을 이용하는 편이 좋겠습니다.

 

 


function() { return !this; }() (Strict Mode체크)

var isStrict = function() { return !this; }();

 

strict mode인지 여부를 확인하는 관용구입니다. 위의 strict mode 내에서는 this가 undefined이 되는 것을 이용하고 있습니다.

현재 strict mode인가 어떤가를 체크하는 것은 좀처럼 없지만, 알아두는것에 의미를 두었습니다.

 

 


typeof(v) == “undefined” (undefined체크)

if(typeof(o) == "undefined") { console.log("undefined detected"); }

 

undefined 체크로 자주 볼 수 있는 구문이지만, 예를 들어 같은 방식으로 undefined를 확인하고 싶을 때,

if(o === void 0) { console.log("undefined detected"); }

 

라고 써도 undefined의 체크는 가능합니다. (void는 어떤 파라메터에 대해서도 undefined를 돌려주는 구문이며, ===체크는 null와 undefined를 구별해 비교합니다) 그러나,  0자체가 로컬 스코프나 글로벌 스코프로 선언되어 있지 않은 경우, 0의 참조의 시점에서 참조 에러가 일어나 버립니다.

브라우저가 준비한 내장 오브젝트의 존재를 확인할 때 자주 사용 합니다. 이전 JavaScript에서는 존재하지 않는 JSON 객체가 있는지 확인하고 싶을 때 단순히 JSON을 참조하면 참조 오류가 발생합니다. 그렇다고 window.JSON에서 확인하면 node.js 등의 window가 없는 환경에서의 동작을 확인 할 수 없게 됩니다. 단지 존재 체크만의 경우에는 typeof 방식을 사용해 존재 확인을 하는 방법이 가장 좋습니다.

고정 문자열끼리의 비교이기 때문에 속도면에서는 typeof이 더 빠르며 최적화되어 있습니다.

덧붙여 undefined를 void 0이 아니고 undefined라고 참조하는 것은, 내부적으로는 window.undefined를 참조하게 되는것 같으므로, 좋은 방법이라고 할 수 는 없습니다.

 

 


v >>> 0 (부호 없는 정수화)

var n = 0x80000000;
console.log(n); // 2147483648 == 0x80000000
console.log(n | 0) // -2147483648
console.log(n | 1) // -2147483647
console.log((n | 1) >>> 0) // 2147483649

 

32비트 정수 값을 강제로 부호 없이 변환하는 관용구입니다. JavaScript로 바이너리를 쓸 때 도움이 되겠습니다.

JavaScript에서 숫자는 모두 부동 소수점형(float형)으로 취급되지만, 비트 연산을 할 때에는 내부적으로 ToInt32가 불려 32비트의 정수치로 변환됩니다. 이 때의 부호가 붙고, 최상위 비트 경우에는 마이너스의 값으로서 처리되어, 그 후의 계산에 영향이 있습니다. 이러할 때에 이 관용구를 사용해, 내부적으로 ToUint32가 불려져 부호 없이 정수형으로 한 번 변환해 주고, 그 값을 그 후의 계산에 이용할 수 있게 됩니다.

바이너리를 다루거나 비트 계산을 효율화하려고 할 때에 높은 빈도로 사용되어 집니다.

 

 

 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤