2021-11

source: categories/daily-issue/issue7.md

2022-04-25 (월)

질문

  1. 자바스크립트의 장점이 무엇이라고 생각하나요?
    • 러프한 것. 잘못 작성해도 에러로 인식 X. 하지만 이것이 나중엔 큰 단점이 됨
  2. 자바스크립트 데이터 타입에 대해 설명해주실 수 있나요?
    • 원시타입, 참조타입, 원시타입엔 무엇무엇이 있고.. 참조타입은 주소를 공유. 원시타입, 참조타입 구분하는건 아주 중요함
  3. var에 대해 설명해주세요.
    • 변수선언 방식, 굉장히 러프, 오버라이딩 문제가 발생할 가능성이 큼 , 함수 스코프이기 때문에
  4. TDZ에 대해 설명해주세요.
    • .. 스코프 관련 일시적으로 값이 없는 상태? 라고만함.. 아 여기서 좀 꼬임
  5. null, undefined의 차이에 대해 설명해주세요.
    • 기억은 잘 안나지만 null은.. undefined는 이런경우가 없는데.. false로 판단되어야할 때 true로 판단되는 경우가 있어서 이를 주의해야된다고 답함.
  6. truthy, falsy가 뭔가요?
    • true로 인식되는 값, false로 인식되는 값. 숫자 0, 빈문자열 같은거..
  7. strict mode가 뭐죠?
    • 엄격 모드. 에러 발생 여지가 있는 것 사전 차단.
  8. ===, ==의 차이가 뭐죠?
    • 데이터 타입까지 비교하는지 안하는지 차이
  9. 형변환이 뭔가요?
    • 데이터 타입을 변환하는 것. 여러 방법이 있지만 난 Number, String 같은 빌트인 오브젝트로 형변환하는걸 선호한다고 함. 명시적이니깐
  10. 함수에 대해 설명해주세요.
    • 표현식, 선언식이 있고 각각은 어떻고.. 선언식은 호이스팅이되고.. 표현식은 안되고.. 이런거?
  11. 함수 스코프 / 블록 스코프가 뭔가요?
    • 말 그대로 함수 스코프 블록 스코프 , 예시로 for문 들었던거 같음
  12. call, apply, bind가 뭔가요?
    • 3 메소드 모두 this 참조를 바꿔줄 수 있는 메소드라고 함
    • 차이점 1. call, apply / bind 이렇게 두개로 나눌 수 있다고 함. call, apply는 모두 함수를 실행시키지만 bind는 함수 자체를 반환한다고 함. 그래서 콜백함수 같은데 많이 쓰인다고 함.
    • 차이점 2. call, apply는 서로 2번째 인자로 무엇을 넘기는지에 차이가 있다고함. 한번에 배열로 넘기느냐, 아니면 두번째, 세번째, 네번째.. 이렇게 나눠서 넘기느냐의 차이.
  13. =>, 일반함수가 뭔가요?
    • 화살표 함수, 일반함수
    • 문법적으로 명시적으로 차이가 있다고 함, 화살표 함수 간결
    • this 자체가 다르다고함. 일반함수는 그 함수를 실행시킨 대상을 this로 바라보고 화살표함수는 부모의 this를 바라본다고함
    • 그리고 메모리적으로도 이런 이유 때문에 화살표 함수가 가볍다고함
  14. 즉시실행 함수가 뭔가요?
    • 말 그대로 즉시 실행하는 함수.
    • 이 함수를 사용하는 이유 -> 스코프 설정
    • es6 모듈문법이 나오기 전에 네임스페이스로 오버라이딩 문제를 방지하기 위해 사용하던 기법이라고함
    • 더불어 밖에 콘솔창에서 접근이 안돼서 보안에도 좋다고함
  15. 클로져가 뭔가요?
    • 스코프 범위를 제한하는 기법
  16. 호이스팅이란?
    • 자바스크립트 엔진이 코드를 받아들이는 순서
    • 함수 선언문 먼저 인식하고 코드를 실행하기 때문에 해당 함수보다 위에서 그 함수를 호출해도 실행이 된다고함
  17. 스코프 체인이란 뭔가요?
    • 참조 데이터를 해당 스코프에서 부모스코프로 나아가면서 찾는걸 스코프 체인이라고함
  18. 동기 / 비동기에 대해 설명해주세요.
    • 동기 적인 코드는 그 코드가 끝날 때까지 그 다음고크 실행 안됨.
    • 비동기는 쉽게 말해 다른 사람한테 일을 맡기는거라고함. 그게 자바스크립트 엔진에서 백그라운드라고 함. 그래서 다른 코드를 실행시켜 효율적이라고 함.
  19. 프로미스란?
    • 비동기를 동기적으로 보이게끔 해줘서 개발자를 도와주는 역할이라고 함.
  20. async / await란?
    • 프로미스 기반. then 메소드 체이닝보다 코드를 깔끔하게 보이도록 도와주는 기법이라고함
  21. 일급함수란?
    • 함수를 인자로도 전달할 수 있고 반환 자체도 함수를 반환할 수 있다고함 이것이 일급함수라고함.
    • 이 특징을 활용하여 함수형 프로그래밍을 할 수 있다고함
    • 로직 자체는 가만히있고 인풋 아웃풋으로 코드를 작성하는거라 가독성도 좋고 유지보수에도 좋다고함
  22. 버블링, 캡쳐링이란?
    • 이벤트 전파. 하위에서 위로가느냐. 위에서 아래로가느냐 차이.
    • 버블링 이슈는 해결해본적 있다고함. preventDefault / stopPropagation
  23. 트랜스파일이란?
    • babel 같은거.
    • es6 이후 문법을 es5로 변환. 크로스 브라우징 때문에 필요.
  24. 웹팩?
    • 의존성 트리를 작성해 필요한 것들만 번들링 해주는 것이 최고의 장점이라고 말함
    • 그밖에도 많은 부분을 자동화 처리해준다고 말함
  • 사용 기술 스택 : vuejs, vanilla js, typescript

클로저

  • 클로저는 주변의 상태 (lexical environment)의 참조와 함께 번들로 묶인 함수의 조합입니다.
  • 즉, 클로져는 우리에게 inner함수에서 outer함수의 스코프에 접근을 가능하게 해줍니다.
  • 자바스크립트에서 클로저는 함수가 생성될 때마다 생성됩니다.
  • 간단히 말하자면 함수가 선언될 때(실행X) 외부의 lexcial environment를 참조?하게 되는 현상?이다.

  • 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.
  • 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
  • 어휘적 범위 지정 (Lexical scoping)
function init() {
    var name = 'Mozilla'; // name은 init에 의해 생성된 지역 변수이다.
    function displayName() { // displayName()은 내부 함수이며, 클로저이다.
        alert(name); // 부모 함수에서 선언된 변수를 사용한다.
    }
    displayName();
}

init();
  • init()은 지역 변수 name과 함수 displayName()을 생성한다.
  • displayName()init() 안에 정의된 내부함수이며 init() 함수 본문에서만 사용할 수 있다.
  • 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점이다.
  • 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init()에서 선언된 변수 name에 접근할 수 있다.
  • 만약 displayName()가 자신만의 name 변수를 가지고 있었다면, name 대신 this.name을 사용했을 것이다.

  • 위 코드를 실행하면 displayName() 함수 내의 alert()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력한다.
  • 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다.
  • 이는 어휘적 범위 지정(lexical scoping)의 한 예이다.
  • 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다.
  • 단어 "lexical"은 이런 사실을 나타낸다.
  • 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.

TDZ

new Car('red'); // ReferenceError

class Car {
  constructor(color) {
    this.color = color;
  }
}
greet('World'); // Hello, World

function greet(who) {
  return `Hello, ${who}!`;
}
  • TDZ은 let, const, class 구문의 유효성을 관리한다.
  • 자바스크립트에서 변수가 동작하는 방식은 중요하다.

TDZ는 무엇인가?

const white = '#FFFFFF';
white; // => '#FFFFFF'
white; // throws `ReferenceError`
const white = '#FFFFFF';
  • const white = ‘#FFFFFF' 구문 전 줄까지, white 변수는 TDZ에 있다.
  • TDZ에 있는 white 변수에 접근하게 되면 , ReferenceError: Cannot access ‘white' before initialization 자바스크립트 에러가 발생한다.
  • TDZ 시맨틱은 선언 전에 변수에 접근하는 것을 금지한다. TDZ는 징계를 내린다: 변수 선언 전에 어떤 것도 사용하지 않는다.

TDZ의 영향을 받는 구문

const 변수

// Does not work!
pi; // throws `ReferenceError`
const pi = 3.14;
const pi = 3.14;
// Works!
pi; // => 3.14

let 변수

// Does not work!
count; // throws `ReferenceError`
let count;
count = 10;
let count;

// Works!
count; // => undefined
count = 10;

// Works!
count; // => 10

class 구문

// Does not work!
const myNissan = new Car('red'); // throws `ReferenceError`

class Car {
  constructor(color) {
    this.color = color;
  }
}
class Car {
  constructor(color) {
    this.color = color;
  }
}

// Works!
const myNissan = new Car('red');
myNissan.color; // => 'red'

constructor() 내부의 super()

  • 부모 클래스를 상속받았다면, 생성자 안에서 super()를 호출하기 전까지 this 바인딩은 TDZ에 있다.
class MuscleCar extends Car {
  constructor(color, power) {
    this.power = power;
    super(color);
  }
}

// Does not work!
const myCar = new MuscleCar('blue', '300HP'); // `ReferenceError`
  • 이 코드를 보면 constructor() 안에서 super()가 호출되기 전까지 this를 사용할 수 없다.
  • TDZ는 인스턴스를 초기화하기 위해 부모 클래스의 생성자를 호출할 것을 제안한다.
  • 부모 클래스의 생성자를 호출하고 인스턴스가 준비되면 자식 클래스에서 this 값을 변경할 수 있다.
class MuscleCar extends Car {
  constructor(color, power) {
    super(color);
    this.power = power;
  }
}

// Works!
const myCar = new MuscleCar('blue', '300HP');
myCar.power; // => '300HP'

기본 함수 매개변수(Default Function Parameter)

  • 기본 매개변수는 글로벌과 함수 스코프 사이의 중간 스코프(intermidiate scope)에 위치한다.
  • 기본 매개변수 또한 TDZ 제한이 있다.
const a = 2;
function square(a = a) {
  return a * a;
}
// Does not work!
square(); // throws `ReferenceError`
  • 기본 매개변수 a는 선언 전에 a = a 표현식의 오른쪽에서 사용되었다.
  • a에서 참조 에러가 발생한다.
  • 기본 매개변수는 선언 및 초기화 다음에 사용되어야 한다.
  • 이 경우 init과 같은 다른 변수로 선언하여 사용한다.

var, function, import 구문

  • 위에서 설명한 것들과 반대로 var, function 선언은 TDZ에 영향을 받지 않는다.
  • 이것들은 현재 스코프에서 호이스팅 된다.
  • var 변수는 선언하기 전에 접근하면, undefined를 얻게 된다.
// Works, but don't do this!
value; // => undefined
var value;
  • 그러나 함수는 선언된 위치와 상관없이 동일하게 호출된다.
// Works!
greet('World'); // => 'Hello, World!'
function greet(who) {
  return `Hello, ${who}!`;
}

// Works!
greet('Earth'); // => 'Hello, Earth!'
  • 당신은 함수 구현보다 호출에 더 관심이 있기 때문에 종종 함수 선언 전에 호출하게 된다.
  • 함수 선언 전에 호출해도 에러가 발생하지 않는 이유는 호이스팅 때문이다.
  • 흥미로운 점으로 import 모듈 역시 호이스팅 된다.
// Works!
myFunction();
import { myFunction } from './myModule';
  • import 구문이 호이스팅 되기 때문에, 자바스크립트 파일 시작 부분에서 디펜던시 모듈을 가져오는 것이 좋다.

TDZ에서 typeof 연산자의 동작

  • typeof 연산자는 변수가 현재 스코프 안에 선언되었는지 확인할 때 유용하다.
  • 예를 들어서, notDefined 변수는 선언되지 않았다.
  • 이 변수에 typeof 연산자를 적용하면 에러가 발생한다.
typeof notDefined; // => 'undefined'
  • 변수가 선언되지 않았기 때문에, typeof notDefined는 undefined로 평가한다.
  • 그러나 TDZ의 변수에서 typeof 연산자를 사용하면 다르게 동작한다.
  • 다음과 같은 경우에 에러가 발생한다.
typeof variable; // throws `ReferenceError`
let variable;

현재 스코프 안에서 TDZ 동작

  • TDZ은 선언문이 존재하는 스코프 범위 안에서 변수에 영향을 준다.

function doSomething(someVal) {
  // Function scope
  typeof variable; // => undefined
  if (someVal) {
    // Inner block scope
    typeof variable; // throws `ReferenceError`
    let variable;
  }
}
doSomething(true);
  • 이 코드는 2개의 스코프를 가진다.
  1. 함수 스코프
  2. let 변수가 선언된 내부 블록 스코프
  • 함수 스코프에서 typeof variable는 undefined로 평가된다. 여기서는 let variable 구문의 TDZ에 영향을 주지 않는다.

  • typeof variable 구문의 내부 스코프에서는 선언 전에 변수를 사용하면 ReferenceError: Cannot access ‘variable' before initialization 에러가 발생한다. TDZ는 내부 스코프에서만 존재한다.

결론

  • TDZ는 const, let, class 구문의 유효성에 영향을 미치는 중요한 개념이다. TDZ는 선언 전에 변수를 사용하는 것을 허용하지 않는다.
  • 반대로 var 변수는 선언 전에도 사용할 수 있기 때문에 var 사용은 피하는 것이 좋다.
  • TDZ는 언어 스펙에 맞도록 좋은 코딩 습관을 만들어주기 때문에 좋다고 생각한다.

코딩 테스트