본문 바로가기

문법

본 내용은 카일 심슨의 YOU DON'T KNOW JS 타입과 문법, 스코프와 클로저(2017)를 읽고 서술적인 내용을 Q&A 형식으로 정리한 글입니다.

1. 문과 표현식의 차이는?

const a = 2*3 // ----- (1)
const b = a // ----- (2)
b // ----- (3)

문은 '문장'이며 표현식은 '어구'와 일맥상통한다.(연산자는 '구두점')

 

모든 표현식은 단일한 특정 값으로 계산된다.

 

(1)의 우항, (2)의 우항, (3) 모두 표현식이며, const가 빠진 (1), (2)는 할당 표현식이라고도 부른다.

 

(1), (2)는 const 변수를 선언하는 선언문이다. (3)은 표현식이기도 하지만 그 자체가 문이 되기도 하여 표현식 문이라고 한다.

 

모든 문은 완료값을 가진다. 콘솔 창에 어떤 선언문을 작성한 후 완료하면 하단에 undefined가 뜨는데 이 값이 곧 선언문의 완료값이다.

 

undefined 뿐만 아니라 { } 블록은 내부의 가장 마지막 문/표현식의 완료값을 자신의 완료값으로 반환한다.

var b;

if (true) {
  b = 3* 6
}
// 18

 

2. 자바스크립트의 콘텍스트 규칙 중 두 가지 이상의 의미로 사용되는 예시들은?

2-1. { }

객체 리터럴로 쓰이는 경우

function foo() {
  console.log('hello!')
}

const obj = {
  func: foo()
}

 

레이블 문으로 쓰이는 경우

{
  func: foo() // 위의 foo()와 동일
}

레이블 문은 goto문처럼 실행 흐름을 바꿀 수 있다.

{
  loop1: for(let i=0;i<arr.length;i++) {
    for(let j=0;j<list.length;j++) {
      if ((i+j) % 2 === 1) continue loop1 // loop1으로 점프
      
      if ((i+j) / 5 === 0) continue // 일반 continue
      console.log(i, j)
    }
  }
}

`break ${레이블 명}` 으로 사용하면 해당 레이블 다음 코드를 실행한다.

 

블록으로 간주되는 경우

단 +연산자의 강제변환 규칙을 주의한다.

{} + [] // (1)

[] + {} // (2)

(1)의 {}은 블록으로 간주되어 빈 객체가 아닌 빈 블록으로 간주된다. 따라서 + [] 가 되어 0이다.

반면, (2)의 {}은 빈 객체로 간주되므로 강제변환 규칙에 의해 [] => "", {} => "[object Object]"으로 해석되어 "[object Object]"가 된다.

 

객체분해

const obj = {
  a: "1",
  b: "2"
}

const {a, b} = obj

 

3. 연산자 우선순위

3.1 쉼표(,)의 우선순위

쉼표는 가장 낮은 우선순위를 가지므로 ()로 묶지 않는 이상 가장 나중에 처리된다.

let a = 27, b

b = (a++, a) // ------ (1) 28
--------------------
let c = 27, d

d = a++, a // ------ (2) 27

(1)은 a++로 a의 값을 증가시킨 후, a를 b에 할당(=)함

(2)는 a++ 수행 전의 결과인 a(27)을 d에 할당(=)후 a의 값을 증가시킴

 

3.2 = 와 &&, || 연산자

할당보다 논리 연산자의 우선순위가 더 높다.

const numberRegex = /[0-9]+/gi

if (str && (matches = str.match(numberRegex))) {
  // ... 
}

if (str && matches = str.match(numberRegex)) { // (str && matches) = str.match(...)로 간주되므로 Error
  // ... 
}

 

3.3 &&와 || 우선순위 비교

false && true || true

위 코드의 결과는 (false && true) || true === true일까, false && (true || true) === false 일까?

 

정답은 true로 &&연산자가 ||연산자보다 우선순위가 더 높다.

 

더 복잡한 케이스도 있다.

const a = 27
const b = "juj"
const c = false

const result = a && b || c? c || b? a : c && b : a

result는 a && b || c (===> false니까) ? c ||  b? a : c && b : a = a === 27이 답이다.

 

3.4 삼항 연산자와 &&, || 연산자

아까 위의 예시에서 삼항연산자와 논리 연산자의 우선순위 비교는 논리 연산자가 더 높다.

 

따라서 아래처럼 해석된다.

const result = (a && b || c)? ((c || b)? a : (c && b)) : a

(책에서는 &&, ||가 끈끈한 우정을 나눴다고 표현하면서 삼항연산자의 우선순위가 더 높았을 경우 따로 동작할 텐데 그렇지 않다고 함)

 

3.5 결합성

동등한 우선순위를 가진 연산자가 2개 이상 나열될 때 그룹핑(소괄호로 묶어 먼저 연산하는 것)을 하는 방법은 결합성에 의해 결정된다.

&&, +와 같은 연산자들은 모두 좌측 결합성이지만 삼항연산자, 할당(=) 연산자는 우측 결합성을 띈다.

a? b : (c? d : e)

a = b = c = 27 // (c = 27)먼저 수행하고 b,a 순으로 할당

 

4. TDZ에 대해 설명하시오.

TDZ란, 초기화되지 않아 변수를 참조할 수 없는 영역을 말한다.

 

let, const 블록 스코프, class, super(), 매개변수가 tdz에 영향을 받는다.

{
  console.log(a) // ReferenceError
  let a
}
function foo (a=2,b=a+b+2) { // b 초기화 전에 b 참조
  // ...
}

foo() // ReferenceError

매개변수의 초기값은 함수에 인자를 넘기지 않거나, undefined를 넘길 때 적용된다.

 

5. 매개변수에 인자를 넘기지 않았을 때와 undefined를 넘겼을 때의 차이점은?

arguments 배열의 할당 여부가 다르다. 직접 undefined를 넘기면 arguments 배열에 할당된다.

function foo(a=1, b=a+1) {
  console.log(arguments.length)
  console.log(arguments)
}

foo() // 0, Arguments [callee: (...), Symbol(Symbol.iterator): ƒ]
foo(10) // 1, Arguments [10, callee: (...), Symbol(Symbol.iterator): ƒ]
foo(10, undefined) // 2, Arguments(2) [10, undefined, callee: (...), Symbol(Symbol.iterator): ƒ]
[참고] ES6부터는 arguments의 사용보다 rest(...) 인자를 사용하는 것을 권장한다.
example.
function foo(...args) {
  // ...
}

 

6. try...finally 블록 사용시 주의할 점에 대해 설명하시오.

finally는 try 블록에서 값이 리턴되기 전에 실행된다.

만약 finally에서 값을 return하거나 에러를 throw하면 이전에 try에서 반환하는 값은 무시된다.

function foo() {
  try {
    return "This is try block"
  } finally {
    console.log("finally is run")
  }
}

foo()
/**
* finally is run
* "This is try block"
*/
function foo() {
  try {
    return "This is try block" // 무시됨
  } finally {
    return "This is finally block"
  }
}

foo() // "This is finally block"
function foo() {
  try {
    throw new Error("This is Try Block Error") // 무시됨
  } finally {
    throw new Error("This is Finally Block Error")
  }
}

foo() // Uncaught Error: This is Finally Block Error

 

'Books > YOU DON'T KNOW JS' 카테고리의 다른 글

스코프와 클로저  (0) 2020.10.05
강제변환  (0) 2020.08.23
타입, 값 그리고 네이티브  (0) 2020.08.04