본문 바로가기
JAVASCRIPT

[JS][문법] 콘텍스트 규칙

by KBS 2022. 2. 14.
728x90

콘텍스트 규칙

자바스크립트 문법 규칙 중에는 같은 문구지만 어디에서 어떤 식으로 사용하느냐에 따라 서로 다른 의미를 가지는 경우가 있다. 하나씩 떨어뜨려 놓고 보면 상당히 헷갈릴 수 있다.

중괄호

자바스크립트에서 중괄호가 나올 법한 곳은 크게 두 군데이다.

객체 리터럴

첫째 객체 리터럴이다.

var a = {
  foo: bar(),
};

 

{}는 a에 할당될 값이므로 객체 리터럴이 맞다.

레이블

방금 전 코드에서 var a = 부분을 삭제하면 어떻게 될까?

{
  foo: bar();
}

 

{}는 어디에도 할당되지 않은, 그저 고립된 객체 리터럴 처럼 보인다. 하지만 전혀 그렇지 않다.

여기서의 {}은 평범하 코드 블록이다. 혼자 떨어져 있는 모양세가자바스크립트에서는 그리 자연스럽지 않지만, 문법적으로는 100% 옳은 코드다.

그런데 그저 평범한 코드 블록에 불과하다면 foo : bar() 구문이 특이하게 보이는 이유는 무엇이고, 어떻게 이런 코드가 문법에 부합한단 말인가?

그건 자바스크립트에서 레이블문이라 부르는, 거의 잘 알려져있지 않은 기능 덕분이다. 즉, foo는 bar() 문의 레이블이다. 그런데 갑자기 레이블문 얘기는 왜꺼낸 걸까?

자바스크립트에 만약 goto 문이 있었다면 goto foo하여 코드 실행을 특정 위치로 점프하는 것이 이론적으로 가능할 것이다. goto는 코드를 아주 난해하게 만드는 주범이며, 자바스크립트가 goto문을 지원하지 않기로 한건 잘한일이다.

ㅎ하지만 제한적이기는 해도 자바스크립트에는 레이블 점프라는 특별한 혀앹의 goto 장치가 대신마련되어 있다. continue와 break 문은 서택적으로 어떤 레이블을 받아 goto 처럼 프로그램의 실행 흐름을 '점프'시킨다.

foo : for(var i = 0; i< 4; i++){
  for (var j = 0; j <4; j++){
    if(j == i){
      continue foo;
    }

    fi((j * i) % 2 == 1){
      continue;
    }

    console.log(i, j);
  }
}

 

코드를 보면 알 수 있듯이 홀수 배수 3 1은 지나갔지만 레이블 루프 점프 역시 11 및 22 순회를 건너뒤었다.

아마도 바깥쪽 루프로 나가야 할 지점에 안족 루프에서 break __ 처럼 사용해야 레이블 루프 점프를 좀 더 잘 활용하는 모습이 될 것 같다. 레이블 break를 쓰지 않고 작성하면 같은 로직이라도 어색해진다.

방금 전 예제를 레이블 없는 break로 작성하려면 아무래도 함수 한 두개가 더 필요하고 공유된 스코프 변수 접근등을 신경 써야 하므로 코드가 더 복잡해지고 혼란스러워 질 수 있따. 따라서 이런 경우라면 레이블 break 사용이 더 나은 선택이라 할 수 있다.

레이블은 비 루프 블록에 적용할 수 있는데, 단 이런 비 루프 레이블은 break만 참조할 수 있다.

function foo() {
  bar: {
    console.log("안녕?");
    break bar;
    console.log("실행X");
  }
  console.log("world");
}

foo();

// 안녕
// world

 

레이블 루프/블록은 사용 빈도가 극히 드물고 못마땅한 구석도 많아 가능한 한 피하는 게 상책이다. 이를 테면 루프 점프를 할 바에야 차라리 함수 호출이 더 낫다 하지만 제한적이나마 도움이 되는 경우가 없지 않으므로 만약 레이블 점프 기능을 사용할 의도라면 우리가 뜻한 바를 상세한 주석으로 잘 문서화 해야 한다.

블록

곧잘 회자되는 자바스크립트 함정 중엔 이런 것도 있다.

[] + {}; // "[Object object]"
{
}
+[]; // 0

 

마치 + 연산자가 첫 번재 피연산자에 따라 다른 결과를 내놓는 것처럼 보인다. 하지만 실제로는 전혀 상관없다.

윗 줄에서 엔진은 + 연산자 표현식의 {}를 실제 값으로 해석한다.

그러나 아랫줄의 {}는 동떨어진 빈 블록으로 간주한다. 블록 끝을 꼭 세미 콜론으로 끝내야 한다는 법은 없으므로 문제될 건 없다. 결국 + [] 포현식에서 명시적으로 []를 숫자 0으로 강제변환 한다.

객체 문해

ES6 분해 할당, 구체적으로는 객체 분해 시 {}를 사용한다.

function getData() {
  return {
    a: 42,
    b: "foo",
  };
}

var { a, b } = getData();

console.log(a, b); // 42 "foo"

 

var {a, b} = ..이 ES6 분해 할당의 형식이다.

{} 를 이용한 객체 분해는 명몀ㅇ된 함수에도 활용할 수 있는데, 이를테면 암시적인 객체 프로퍼티 할당과 비슷한 간편구문이다.

function foo({ a, b, c }) {
  console.log(a, b, c);
}

foo({
  c: [1, 2, 3],
  a: 42,
  b: "foo",
});

{}은 전적으로 사용 콘텍스트에 따라 의미가 결정되는데, 여기서 문법과 구문의 차이점을 엿볼 수 잇다. 자바스크립트 엔진이 우리가 예상치 못했던 방향으로 해석 하는 것을 방지하는 차원에서라도 이러한 미묘한 차이를 이해하는 것이 좋다.

else if 와 선택적 블록

다음 코드가 잘 작동한다고 해서 자바스킬브테 else if 절이 존재한다고 믿는 건 미신을 신봉하는 것과 같다.

if (a) {
  //..
} else if (b) {
  // ..
} else {
  // ..
}

 

실은 else if같은 건 없다. 자바스크립트 문법의 숨겨진 특성이다. ifelse 문은 실행문이 하나밖에 없는 경우 블록을 감싸는 {}를 생략해도 된다. 우리는 다음과 같은 코드를 무던히도 많이 봤을 것이다.

if (a) doSomething(a);

 

여러 스타일 가이드 문서에는 다음처럼 단일 문 블록도 {}로 감싸라고 조언한다.

if (a) {
  doSomething(a);
}

 

그러나 정확히 동일한 문법 규칙이 else 절에도 적용되어 좀 전에 봤던 코드는 실제로는 항상 이렇게 파싱된다.

if (a) {
  // ..
} else {
  if (b) {
    // ..
  } else {
    // ..
  }
}

 

즉, else if라고 쓰는건 표준 스타일 가이드의 위반 사례가 되며, 단일 if 문과 같이 else를 정의한 셈이다.

물론 else if 는 이미 누구나 다 쓰는 관용 코드고, 한 단계 하위로 들여 쓰기를 하는 효과가 있어 나름 매력은 있다. 어던 식으로 코딩하든 우리의 스타일 가이드와 규칙을 명시적으로 준수하고 else if를 정확한 문법 규칙인 양 넘겨짓지는 말자.

 


참고

  • You Don't Know JS - 타입과 문법, 스코프와 클로저( 한빛 미디어 )
728x90

'JAVASCRIPT' 카테고리의 다른 글

[JS][문법] Switch  (0) 2022.02.14
[JS][문법] 연산자 우선순위  (0) 2022.02.14
[JS][문법] 표현식의 부수 효과  (0) 2022.02.13
[JS][문법] 문과 표현식  (0) 2022.02.13
[JS][강제변환][마무리] 추상 관계 비교  (0) 2022.02.13

댓글