Posts

[FE 면접질문] Javascript (2)

목차38

객체지향 프로그래밍

객체지향 프로그래밍이란?

  • 현실 세계의 사물처럼, 프로그래밍에 필요한 개념을 객체라는 개념으로 추상화하고, 객체들간의 상호작용을 중심으로 프로그래밍하는 패러다임을 의미한다.

객체지향 프로그래밍에서 객체란?

  • 좁은 의미로 객체는 클래스의 인스턴스라고 할 수 있고, 넓은 의미에서 객체는 속성과 행위로 이루어진 독립된 단위를 의미한다.

객체지향 프로그래밍의 특징을 설명해주세요.

  • 추상화 :  추상화는 객체간에 공통적인 특징이나 기능을 추출하는 것을 의미한다.
    (예를 들어, 강아지와 고양이를 동물이라는 공통적인 특징으로 추상화 할 수 있다.)
  • 상속 : 상속은 기존 객체의 기능을 다른 객체에서 사용할 수 있도록 넘겨주는 것을 의미한다.
  • 다형성 : 다형성은 어떤 메소드가 다양한 방식으로 동작할 수 있는 특징을 의미하는데, 오버라이딩이나 오버로딩을 통해서 구현할 수 있다.
  • 캡슐화 : 캡슐화는 필요한 데이터와 데이터를 사용하는 행위를 함께 묶어두고, 외부에서는 정해진 메소드를 통해서 객체와 상호작용 할 수 있는 특징을 의미한다.

자바스크립트는 객체지향 프로그래밍 언어인가요?

  • 자바스크립트는 여러가지 프로그래밍 패러다임을 따르는 멀티 패러다임 프로그래밍 언어고, 그 중에서도 객체지향 프로그래밍도 가능한 언어다.
  • 다만 C++나 Java 같은 클래스 기반 객체지향 프로그래밍 언어라고는 할 수 없다.

프로토타입

프로토타입에 대해 설명해주세요.

  • 자바스크립트는 객체들로 구성되어 있는데, 객체는 각각 프로토타입을 가지고 있다.
  • 프로토타입을 통해 자바스크립트의 상속이 구현되어 있는데, 프로토타입은 객체의 원형이라는 의미 그대로 상위 객체의 속성이나 메소드를 하위 객체에서 사용할 수 있다.
  • 객체에서 어떤 속성이나 메소드에 접근할 때, 현재 객체에 찾는 값이 없으면 해당 객체의 상위 프로토타입에서 접근할 값을 찾는 것을 반복한다. 이걸 프로토타입 체인이라고 한다.

Strict Mode

Strict Mode에 대해 설명해주세요.

  • Strict mode는 ES5에 추가된 문법으로, 더 엄격한 환경에서 자바스크립트 코드를 실행하도록 제한하는 문법이다.
  • Strict mode는 “use strict” 라는 키워드를 작성해서 적용할 수 있다.
  • Strict mode가 적용되면 선언하지 않은 변수를 참조하거나 delete 를 통한 식별자 제거, with 문 사용 시 에러가 발생한다.
  • Strict mode가 적용되면 일반 함수 내부에서 this를 사용하면 this에 undefined가 바인딩된다.

클래스와 Strict Mode

  • ES6에 추가된 Class 문법을 사용하면 클래스 내부의 body는 자동으로 Strict Mode로 실행된다.

Wrapper Object

래퍼 객체에 대해 설명해주세요.

  • 자바스크립트에서는 원시 타입을 제외한 모든 나머지는 객체 타입이다.
  • 이런 원시 타입도 객체 타입처럼 사용해야 하는 경우가 있는데, 이때 자바스크립트 엔진이 원시 타입의 값을 일시적으로 연관된 객체로 변경하고 사용하게 된다. 이때 사용하는 객체를 래퍼 객체라고 부른다.
  • 그리고 래퍼 객체는 사용이 끝나면 다시 원시 타입의 값으로 되돌아간다.

래퍼 객체 사용을 예를 들어주세요.

  • 변수에 문자열을 할당하고, 변수에 .length 라는 프로퍼티를 참조하는 경우가 있다.
  • 문자열 타입은 기본적으로 원시 타입이라서 객체처럼 프로퍼티를 가질 수 없지만, 자바스크립트 엔진에서 String이라는 래퍼 객체로 변경해주고, 객체의 프로퍼티에 접근하도록 처리하기 때문에 문제 없이 사용할 수 있다.

this

this에 대해서 설명해주세요.

  • 자바스크립트에서 this는 일반적으로 자신이 속한 객체나 생성할 인스턴스를 가리키는 자기 참조 변수다.

this 바인딩이란?

  • 바인딩은 식별자와 값을 연결하는 과정을 의미한다.
  • 자바스크립트에서 this는 호출되는 방식에 따라서 어떤 값이 연결될지 결정된다. 이걸 this 바인딩이라고 한다.

상황에 따른 this 바인딩에 대해 소개해주세요.

일반적으로, 크게 4가지 방식으로 사용할 수 있다.

  • 첫번째는 전역이나 일반 함수 내부에서 사용하는 경우, 이때 this는 전역 객체를 가리키게 된다.
  • 두번째는 오브젝트 내부의 메소드에서 사용되는 경우, 이 경우에는 호출한 객체를 가리키게 된다.
  • 세번째로 생성자 함수 내부에서 사용되는 경우, 이 경우에는 생성할 인스턴스를 가리키게 된다.
  • 마지막으로 이벤트 리스너의 콜백 함수에서 사용하는 경우, 이 경우에는 이벤트의 current target과 같은 곳을 가리키게 된다.

몇가지 예외가 있는데,

  • apply, call, bind 메소드를 통해 호출한 경우, 파라미터로 전달된 객체를 바인딩하게 된다.
  • ES6에 추가된 화살표 함수를 이용하는 경우, 화살표 함수는 특정 this를 가지고 있지 않기 때문에 바로 상위 환경에서 this를 참조한다.

실행 컨텍스트

실행 컨텍스트에 대해 설명해주세요.

  • 실행 컨텍스트는 해당 코드가 평가되고 실행되는 환경을 의미한다.
  • 코드를 실행하기 위해 필요한 정보를 모아놓은 컨텍스트들을 콜 스택에 쌓아올리고, 스택에서 pop 하면서 실행하는 것을 의미한다.

실행 컨텍스트 작동에 대해서 아는만큼 설명해주세요.

  • 실행 컨텍스트는 크게 3가지 방식으로 생성된다.
  • 프로그램이 실행될 때, 함수가 호출될 때, 마지막으로 eval 함수가 호출될 때 생성된다.
  • 실행 컨텍스트가 생성되면 자바스크립트 엔진의 콜 스택에 쌓이게 되고, 가장 위에 쌓인 실행 컨텍스트부터 처리하면서 코드를 실행하고 스택에서 제거된다.
  • 실행 컨텍스트는 ES6 기준으로 **렉시컬 환경(Lexical Environment)**과 **변수 환경(Variable Environment)**로 구성되어 있다.

렉시컬 환경

  • 렉시컬 환경은 외부 참조 환경과 환경 레코드로 구성되어 있다.
  • 외부 참조 환경은 자바스크립트의 스코프 체인과 연관된 개념으로, 상위 실행 컨텍스트의 렉시컬 환경에 연결되어 있다.
  • 환경 레코드는 값들을 저장할 때 필요하며, 식별자 바인딩을 하는 선언적 환경 레코드와 with문 같은 특수한 상황에서 사용되는 객체 환경 레코드로 구성되어 있다.

변수 환경

  • 변수 환경은 렉시컬 환경과 같이 외부 참조 환경과 환경 레코드로 구성되어 있다.

실행 컨텍스트에서 렉시컬 환경과 변수 환경이 나눠진 이유를 알고 있나요?

  • 기존의 함수 레벨 스코프를 가지는 var 변수들은 변수 환경에 저장된다.
  • 하지만 ES6 이후 실행 컨텍스트는 내부에서 일어나는 함수 선언, 블록문, try/catch 문과 같은 곳에서 새로운 렉시컬 환경이 생성된다.
  • 그래서 새로운 블록 안에서 블록 레벨 스코프를 가지는 let, const 변수를 사용하면 렉시컬 환경이 여러개 생길 수 있기 때문에 분리했다고 생각한다.

실행 컨텍스트 작동 방식에 대해서 설명해주세요.

  • 실행 컨텍스트 작동 방식은 크게 생성 단계와 실행 단계로 구분할 수 있다.
  • 생성 단계에서는 this가 바인딩되고 스코프 체인을 위한 외부 환경 참조를 결정한다.
    그리고 이 과정에서 렉시컬 환경과 변수 환경의 환경 레코드에 식별자 정보를 수집한다.
  • 이때 환경 레코드에 변수와 같은 식별자들이 실행 단계 이전에 수집되기 때문에, 실제 선언문 이전에 변수에 접근하거나 함수를 호출할 수 있는 호이스팅이 발생한다.
  • 이후 실행 단계에서 자바스크립트 엔진이 실행 컨텍스트를 기반으로 실제 코드를 실행한다.

클로저

클로저에 대해 설명해주세요.

  • 클로저는 렉시컬 스코프 밖에서 실행되더라도, 렉시컬 환경을 기억하고 참조할 수 있는 함수다.
  • 함수 내부에서 사용하는 변수나 함수들은 함수가 선언된 시점의 스코프에서 결정되는데, 이것을 렉시컬 스코프라고 한다.
  • 일반적인 함수는 실행이 종료되면 더이상 참조할 수 없는데, 클로저는 렉시컬 환경을 기억하고 참조할 수 있다.

클로저의 장점을 설명해주세요.

  • 클로저를 사용하면 클로저가 어떤 함수 내부의 어떤 값들을 참조하고 있을 경우, 해당 함수의 생명주기가 종료되더라도 렉시컬 환경을 통해 참조할 수 있다. 이를 통해 은닉화와 캡슐화 효과를 얻을 수 있다.
  • 함수 내부 변수를 접근하는 클로저를 생성하면, 그 함수는 클로저를 통해서가 아니면 더이상 내부에 접근할 수 없기 때문이다.

클로저는 단점이 없나요?

  • 일반적인 함수는 실행이 종료되면 가비지 컬렉션에 의해 메모리에서 해제된다.
  • 하지만 클로저를 사용하면 사용이 종료된 함수 내부에 렉시컬 환경에 의해 참조가 가능하므로 메모리에서 해제할 수 없기 때문에 메모리를 사용하게 된다.

클로저를 예를 들어줄 수 있나요? (클로저를 사용해본 경험이 있나요?)

  • 커링 기법을 자바스크립트로 구현할 때 클로저를 사용한다.
  • 자바스크립트 라이브러리로 리액트를 주로 사용하는데, 리액트의 setState와 같은 훅을 클로저를 통해 구현할 수 있다.

실제 코드를 작성해보세요.

코드
// 변환 전
function sum(x, y) {
  return x + y;
}

// 변환 후
function sum(a) {
  return (b) => {
    return a + b;
  };
}
const add2 = sum(2); // add2는 sum(a)에 2를 인수로 넣은 함수가 된다.
const five = add2(3); // 2+3
  • sum 함수의 파라미터가 2개인 것을 커링 기법을 적용해서 파라미터를 분리했다.
  • 클로저의 특성으로 sum이 반환하는 함수는 a 값을 기억하게 되고, add2 함수와 같이 사용할 수 있다.
코드
const MyReact = (function () {
  let _state;
  function useState(initialValue) {
    const state = _state || initialValue;
    const setState = (newState) => {
      _state = newState;
    };
    return [state, setState];
  }
  return { useState };
})();
  • 리액트의 state 는 초기값을 지정한 이후에 다시 렌더링이 되서 함수가 호출되더라도 초기값으로 초기화되지 않는다.
    그리고 useState로 반환된 state를 직접 변경해도 실제 상태는 변하지 않는다.
  • 이걸 _state 값을 두고, useState 를 클로저로 선언해서 구현할 수 있다.

클래스

자바스크립트에서 객체지향 프로그래밍을 어떻게 구현하나요?

  • ES5의 생성자 함수를 이용하거나, 프로토타입 메소드를 사용해서 구현할 수 있다.
  • ES6에서 추가된 클래스 문법을 이용해도 마찬가지로 객체지향 프로그래밍을 구현할 수 있다.

클래스 문법의 특징이 있나요?

  • 클래스로 선언하면 new 키워드 없이 호출할 수 없다.
  • 그리고 ES6에서 extends, super 와 같이 클래스 기반 객체지향 언어에 친숙한 키워드가 추가되었다.
  • 클래스 내부는 암묵적으로 Strict mode가 적용된다.
  • 또한 클래스는 let, const와 같이 블록레벨 스코프를 가지며, 호이스팅이 발생하지 않는 것 처럼 작동한다.
  • 마지막으로 클래스 내부에 선언된 프로퍼티들은 Enumerable이 false인 열거되지 않는 값으로 간주한다.

클래스에는 어떤 값들이 포함되어 있나요?

  • 클래스 내부에는 크게 생성자인 constructor와 프로토타입 메소드, 정적 메소드가 포함되어 있다.
  • ES2022 이후로는 클래스 내부에 변수도 선언할 수 있다.

스프레드 문법

스프레드 문법에 대해 설명해주세요.

  • ES6에 추가된 문법으로, 배열이나 문자열같이 나열할 수 있는 여러 값들을 하나 하나의 개별 값으로 분해하는 문법이다.
  • 값 앞에 점 세개를 붙여서 사용한다.

스프레드 문법은 어디에 적용할 수 있나요?

  • 불변성을 지켜야하는 배열과 객체를 얕은복사할 때 사용할 수 있다.
  • 스프레드 문법은 대상을 순회할 수 있는 iterable에 적용할 수 있다.
  • 예를 들어 배열, 문자열, Map, Set, DOM 컬렉션 등이 있다.
  • 혹은 객체 리터럴 내부에서는 Object에도 적용할 수 있다.

구조 분해 할당

구조 분해 할당을 설명해주세요.

  • 구조 분해 할당은 값을 해체해서 해체된 값을 개별 변수에 담을 수 있게 하는 문법이다.
  • 주로 배열이나 객체의 값들을 각 변수에 나눠서 할당할 때 사용한다.

구조 분해 할당을 적용한 예시가 있을까요?

  • React Query를 사용한 함수를 별도로 생성해서 사용하고자 하는 컴포넌트에서 값을 가져올 때 사용했다.
코드
// react-query를 사용한 함수
export const useGetRecentPosts = () => {
  const { data, isLoading, error, refetch } = useQuery(
    ['recentPosts'],
    async () => {
      return await axios
        .get('/api/posts/recent')
        .then((res) => {
          return res.data;
        })
        .catch((error) => {
          console.error(error);
        });
    },
    {
      staleTime: Infinity,
      refetchInterval: 60 * 1000,
    },
  );

  ...

  return { recentPosts, isLoading, error, refetch };
};

// 사용하고자 하는 컴포넌트에서 구조분해할당
const { recentPosts, isLoading, error, refetch } = useGetRecentPosts();
  • 서버로 POST 요청 시 객체 데이터를 보내면, 서버에서 req.body의 값을 구조분해할당해서 사용할 수 있다.
코드
  const { postId, postContent, postLike, postUser } = req.body;

Reference

시리즈 | 💬 개발자 면접 - shyuuuuni.log

반응형