JS 기술면접 스터디 5주차: Math.max()부터 프로토타입 기반 상속까지

2019. 9. 27. 18:19프론트엔드 개발/JS & TS

기술면접 스터디 4주차에 이어 5주차를 진행하고 관련 내용을 정리합니다. 이전의 내용이 궁금하시다면 다음을 클릭해주세요.

이번 글은 지난 1~4주차 글들과 다르게 9 JavaScript Interview Questions의 질문에 대해 다룹니다. 이 글은 스터디 내용을 복기하고 정리하는 글로 아주 상세한 기술내용을 담고 있진 않습니다. 관련해서는 이후에 따로 정리할 계획입니다.

5주차 질문은 Math.max()와 Math.min(), 컴퓨터가 다루는 소수, 지멋대로의 진법 변화, 함수 표현식과 선언식의 차이, 키워드 없는 변수 할당, 객체지향 프로그래밍과 함수형 프로그래밍, 선언적/명령형 프로그래밍의 차이, 프로토타입 기반의 상속 등에 대한 내용입니다.

1. Math.max()는 왜 Math.min()보다 작은가?

Math.max() // -infinity
Math.min() // infinity
Math.max() < Math.min() // true

Math.max()는 -infinity, Math.min()는 infinity를 반환합니다. 따라서 그 둘을 비교하면 max가 min보다 작다니 모순처럼 보입니다.

 

그러나 max, min메소드가 단순히 최댓,최솟값을 나타내는게 아니라 인자로 받은 것 중에 최댓값, 최솟값을 반환한다는 사실을 인지한다면 이 현상을 이해하기 쉽습니다. max의 인자값에 아무것도 없다면 아무 것도 없는 것 중에 가장 큰 것은 javascript가 인지하는 수 중 최솟값인 -infinity가 될 것이고, min의 경우에는 최솟값이 javascript가 인지하는 수 중 최댓값인 infinity가 될 것입니다.

 

만약 min()이 -infinity라는 가장 작은 수를 반환한다면 오히려 더 이상할 겁니다. 인자에 뭘 넣든 -infinity를 반환할테니 아무 쓸모없는 메소드가 될테니까요.

2. 왜 0.1 + 0.2 === 0.3은 false인가

0.1 + 0.2 // 0.30000000000000004

JavaScript는 소수를 처리할 때 부동소수점을 이용하여 근사치를 표현합니다(IEEE-764배정도 부동소수점 숫자 형식, 한정된 메모리 안에서 가능한 한 정확히 짐작하려다 생긴 결과) 따라서 소수점 계산도 근사치를 표현할 뿐 0.3 이라는 정확한 계산을 하지 못합니다.

 

이를 해결하기 위해

  1. Number.prototype.toFixed([digits])을 이용해 digits(소수점 몇번째자리 까지 표시할 것인지 정할 수)을 정해 근사값으로 처리(고정소수점표기, 이하 반올림)
  2. 정확도가 그렇게 중요하다면 Binary Coded Decimals 같은 라이브러리를 사용하는 방법도 있음(https://formats.kaitai.io/bcd/javascript.html)

등의 방법을 사용할 수도 있습니다.

3. 018 - 017 은 왜 3인가?

이것은 자바스크립트의 silent type conversion과 관계있습니다. 즉 자바스크립트가 자신이 계산할 수 있는 방식에 맞게 값의 타입을 임의대로 처리하기 때문입니다.

 

javascript는 숫자 맨 앞에 0이 붙으면 8진법으로 처리합니다. 018에서 01은 8진법으로, 8은 10진법으로 보아 18로 간주합니다. 017은 전체 8진법 처리하여 15를 나타냅니다.

 

따라서 자바스크립트는 018 - 017을 18 - 15로 바꿔서 계산하여 3을 반환합니다.

4. 함수 표현식은 함수 선언식과 어떻게 다른가?

// 함수 선언식
function sum(x,y){ return x+y }
// 함수 표현식(ES5)
var sum = function(x,y){ return x+y }
// 함수 표현식(ES6)
const sum = (x,y)=>{ return x+y }

함수 표현식은 변수에 익명함수를 할당하는 것을 뜻하며 변수 호이스팅을 따릅니다.(var는 변수 선언/초기화되고, const/let은 변수 선언만)
함수 선언식은 이와 달리 함수 호이스팅을 따르는데, 함수 호이스팅에서는 함수 선언, 초기화, 할당이 한번에 이루어지며, 이 때문에 함수의 선언위치에 관계없이 어디에서든 참조가 가능합니다.

 

호이스팅에 대해선 3주차: 호이스팅부터 이벤트 버블링까지의 2번에서 자세히 다루고 있으니 참고하세요.

 

스터디를 정리한 학습 노트에는 이게 클로저와도 관계가 있다고 적혀있는데 정확히 기억이 나질 않아서 더 쓰진 못하겠네요...ㅇ_ㅇ;;;

5. var, let, const의 차이점은 무엇인가?

var는 함수범위의 스코프를 가지고, 실행 컨텍스트(Execution Context)에 있어 이 키워드로 선언되는 변수는 어디서 작성되든 선언과 undefined로 초기화되며 따로 값이 할당되기 전까지 undefined인 채로 있습니다.
letconst는 블록범위의 스코프를 가지고, 실행 컨텍스트에 있어 이 키워드로 선언되는 변수는 따로 값을 할당하기 전까지 선언만 된 상태로 있습니다.
let로 선언한 변수는 재할당이 가능한 반면 const로 선언한 변수는 재할당이 불가능합니다. 그러나 할당된 객체를 변경하는 것은 관계없이 가능합니다.

6. 키워드 없이 변수에 할당을 하면 무슨 일이 발생하는가?

자바스크립트에서는 var, let, const 없이도 변수를 선언, 할당할 수 있으며 이 경우 그 변수는 전역에 올라가게 됩니다. 그러나 단순히 이렇게 설명하면 var와의 차이가 느껴지지 않으니 함수 스코프인 var의 특성과 비교하며 설명해보겠습니다.

function a(){ var x=1 }
function b(){ y=1 }
a();
b();
// 함수를 실행해야 변수 x와 y가 할당됩니다.
console.log(x); // Uncaught ReferenceError: x is not defined
console.log(y); // 1

var는 함수 스코프이기 때문에 변수 x를 함수 밖에서 호출시 레퍼런스 에러가 발생합니다. 그러나 키워드 없이 선언한 y는 전역 스코프가 되어(window.y) 함수 밖에서 호출해도 문제없이 1을 반환합니다.

그러나 키워드없이 선언하는 것은 전역을 오염시키고 메모리 누수를 발생시킬 수 있습니다.

One common reason for memory leaks is the accidental use of global variables. Any time we define a variable in JavaScript without a keyword var, let or const, then that is automatically considered to be a global variable. Unless foo is already defined, the expression foo = "bar" is equivalent to window.foo = "bar".

메모리 누수가 발생하는 일반적인 원인 중 하나는 부지불식간에 쓰이는 전역변수입니다. 언제든지 우리는 자바스크립트에서 var, let, const 없이 변수를 선언할 수 있고 그 변수는 전역변수가 됩니다. foo가 이미 선언되어 있지 않았더라도 foo = "bar"라는 표현은 window.foo = "bar" 동일하게 작동합니다.

What JavaScript Developers Can Learn from C++ 인용

또한 프로그램이 커지고 복잡해질수록 변수간 충돌이 발생하게 될 것이므로 지양해야 합니다. ES6 이후 전역을 보호하기 위해 함수 스코프의 var보다 더 엄격한 블록 스코프를 사용하는 let과 const를 사용하는 추세이므로 키워드 없는 변수 선언과 var 변수 선언은 모두 지양합니다.

7. 객체지향 프로그래밍(OOP)와 함수형 프로그래밍(FP)의 차이점은 무엇인가?

객체지향프로그래밍은 데이터(프로퍼티)와 데이터를 조작하는 함수(메소드)로 이루어진 객체를 이용해 프로그래밍을 현실세계와 비슷하게 구성하여 객체간의 협력으로 문제를 해결하려는 것입니다.


ES5까지는 prototype으로 객체지향을 구현했으나 ES6부터는 class가 도입되었습니다. 그럼에도 불구하고 private이 없어 완전한 캡슐화(은닉)가 이뤄지지 않는 등 부족한 점이 몇몇 있습니다.

 

함수형 프로그래밍은 자바스크립트는 함수가 1급객체(함수가 변수에 대입될 수 있고, 다른 함수의 인자로 넘길 수도 있고, 함수를 반환할 수 있는 특징)이기 때문에 함수형 프로그래밍이 가능합니다.


함수형 프로그래밍에서 사용되는 개념들로 순수함수, 불변성 등이 있습니다. 순수함수는 파라미터에 의해서만 반환값이 결정되는 함수(인자가 같으면 무조건 결과값은 동일해야 함)로 부수효과(side effect)가 없어야 합니다. 함수가 전역 변수를 건드리거나 함수 내부, 애플리케이션의 다른 상태를 변경하는 것이 부수효과라고 합니다.

// 순수함수
function(a,b)=>{return a+b;}

// 순수함수가 아님
let c = 5;
function(a,b)=>{return a+b+c}

불변성은 원본 데이터를 변경하지 않고 필요시에는 복사본을 변경함으로써 지킬 수 있습니다.
이외에 고차함수(함수가 함수를 변경), 재귀 등도 함수형 프로그래밍에 필요한 개념들입니다.

8. 선언적 프로그래밍과 명령형 프로그래밍

선언적 프로그래밍은 무엇을 할 것인가에 초점을 두며 대표적인 예시로 함수형 프로그래밍이 있습니다. reduce와 재귀함수 등을 사용하는 특징이 있습니다. 재귀함수는 반복을 할 때 현재의 맥락과 이전의 맥락이 완전히 분리되어 상태를 공유하지 않고 오직 인자를 통해서만 맥락간 소통이 이뤄질 뿐입니다. reduce도 콜백함수가 재귀함수와 비슷한 방식으로 작동합니다.

 

명령형 프로그래밍은 어떻게 할 것인가에 초점을 두며 대표적인 예시로 객체지향형 프로그래밍이 있습니다. for와 while 루프, if와 switch문을 사용하는 특징이 있습니다. 반복 간의 맥락이 단절되는 재귀함수와 다르게 for문과 while문은 반복 간 맥락이 같아 상태를 공유합니다.

// 명령형 프로그래밍적 접근
// for문은 반복이 하나의 맥락 아래에서 이뤄지고 각 반복은 result라는 상태를 공유하며 그 상태를 변경함으로서 과업을 수행합니다.
const sumArray = array => {
  let result = 0;
  for (let i = 0; i < array.length; i++) { 
    result += array[i]
  }; 
  return result;
}

// 선언형 프로그래밍적 접근
// reduce의 콜백함수는 반복 간 맥락이 단절되어 오직 인자로 소통할 뿐 상태를 공유하지 않습니다. 
const sumArray = array => array.reduce((x, y) => x + y);

9. 프로토타입 기반의 상속은 무엇인가?

객체지향 프로그래밍을 구현하기 위해 JavaScript는 프로토타입 기반의 상속체계를 갖고 있습니다. 자바스크립트의 모든 객체는 최종적으로 Object.prototype의 인스턴스입니다.


기본 프로토타입의 작동을 임의대로 덮어쓰거나 확장할 수 있지만 이것은 자바스크립트의 기본적 작동방식을 기대하는 다른 개발자와의 협업을 방해하기 때문에 권장되지 않으며, 만약 특별한 프로퍼티나 메소드를 부여해야 한다면 프로토타입을 상속받는 커스텀 객체를 만드는 것을 권합니다.


그러나 사실 JavaScript 창조자 Brendan Eich는 오히려 이러한 확장을 가능하게 하기 위해 프로토타입 시스템을 도입한 것이라 밝혀 빌트인 객체의 프로토타입을 확장을 권하지 않는 것을 교조적으로 따를 필요는 없어 보입니다.

 


틀렸거나 더 좋은 설명이 있다면 댓글로 알려주세요. 많은 분들에게 큰 도움이 될 거에요.

 

도움이 되셨다면 좋아요와 댓글 부탁드립니다. 감사합니다