웹개발

52가지 프론트엔드 면접 질문들 - 자바스크립트

별을 보고 걷는 사람 2023. 8. 21. 04:12
 

52 Frontend Interview Questions - JavaScript

Introduction A large number of beginner and experienced developers have started to...

dev.to

 

1. 자바스크립트에는 무슨 데이터 타입들이 존재하는가?

  1. Number - 숫자
  2. String - 스트링 (문자)
  3. Boolean - 불리언, 참 또는 거짓
  4. Object - 객체
  5. null - "아무것도 없음", "비었음", 또는 "알려지지 않은 값"을 대표하는 특수한 값
  6. undefined - "값이 할당되지 않았음." 어떤 변수가 선언되었으나 그에 할당된 값이 없을 때 이 타입이 배정된다.
  7. Symbol - 유일하고 변형시킬 수 없는 테이터 타입으로, 객체 속성들을 위한 식별자로 사용될 수 있다.
  8. BigInt - 거대한 숫자를 창조하는 데 사용된다.

2. "==" 와 "===" 의 차이가 무엇인가?

연산자 "=="는 추상적으로 동일한가를 확인하고 "==="는 엄격한 동일성을 확인한다. 

다시 말해, "==" 연산자는 비교 이전에 필요한 타입 전환을 실행하지만 "===" 는 타입 전환을 실행하지 않는다.

그러므로 "===" 연산자를 사용할 때 두 값이 같은 타입이 아니면 거짓 (false) 을 돌려준다.

 

3. 변수를 선언하는 방법들에는 무엇이 있는가?

변수를 선언하는 데에는 네 가지 방법들이 있다.

  foo = 123; 
  var foo = 123; 
  let a = 123; 
  const a = 123;

"var" 키워드를 사용하여 변수를 선언하는 것은 첫번째 방식과 유사하다. 이런 방식으로 선언된 변수들의 범위는 전역, 또는 그 함수까지고 블록 범위는 안 된다는 것이 단점이다.

"let" 과 "const" 가 선호되는 변수 선언 방식들이다. 이것들은 블록 범위를 가지고 있는데, 그 말은, 예를 들어 어떤 함수 안에서 하나의 변수가 선언되면 그 함수 바깥에서는 그것이 보이지 않는다는 뜻이다. "const" 변수는 변형 불가지만 객체 (Object) 의 경우 그 안의 속성들은 바꿀 수 있고, 배열 (array) 인 경우 그것을 수정하거나 그 요소를 추가할 수 있다.

 

4. null 과 undefined 의 차이가 무엇인가?

두 옵션들 다 비어 있는 값을 표현한다. 만약 우리가 변수를 하나 초기화 했지만 거기에 값을 할당하지 않는다면 거기에는 특수한 표식이 지정된다 - undefined 다. Null 은 수동으로 지정된다.

Null 은 "아무것도 없음", "비어 있음", 또는 "알려지지 않은 값" 을 대표하는 특수한 값이다. 만약 우리가 어떤 변수의 값을 지우고자 하면 우리는 그것을 foo = null 로 지정하면 된다.

 

5. 화살표 함수가 일반 함수와 다른점들.

  1. 화살표 함수는 arguments 객체 (Object) 를 사용할 수 없다.
  2. 문법이 다르다
  3. 자기만의 맥락이 없다. 이것을 참조할 때, 화살표 함수는 주위 범위로부터 맥락을 가지고 온다
  4. 화살표 함수는 생성자 (constructor) 함수로 사용될 수 없다. 다시 말해 "new" 키워드로 불러질 수 없다.

6. 클로져가 무엇이고 왜 필요한가?

클로져는 자신이 접근 가능한 모든 외부 변수들 및 함수다. 예를 들어 자신의 부모의 변수를 에워싸고 함유하는 내포된 함수가 있다.

function parent() { 
    const a = 5; 
    return function child() { 
        console.log(a); // child closes over the variable 'a'; 
    } 
}

 

7. 템플릿 리터랄이 무엇인가?

템플릿 리터랄은 백틱 (`) 에 감싸인 것으로, 그 안에 여러 줄의 스트링을 허용한다. 그 안에 수식을 내장하는 것도 허용한다.

const name = 'John';
const text = `User's name is ${name}`;

console.log(text) // User's name is John

 

8. Set (세트) 와 Map (맵) 이 무엇인가?

맵은 키-값 (key-value) 쌍을 원칙으로 작동하는 하나의 무리 (collection) 로, 객체 (Object) 와 유사한 데이터 구조를 가지고 있다. 그러나 맵과 객체의 주요 차이점은 맵의 경우 어떤 타입의 키도 다 허용한다는 것이다. 

세트는 키가 없는 콜렉션 타입으로, 각 값이 오직 한 번만 나타날 수 있는 배열이다. 세트는 자기 안에 유일값들만 저장한다.

 

9. 객체 (Object) 안 어떤 속성의 존재 여부는 어떻게 확인하는가?

첫번째 방법은 hasOwnProperty 함수를 사용하는 것으로, 모든 객체에서 이용 가능하다.

두번째 방법은 "in" 연산자를 사용하는 것이다. 그러나 "in" 연산자를 사용할 때는 주의해야 한다. 연관된 모든 원형들 전부를 확인하기 때문이다.

 

10. 객체 (Object) 의 속성에 어떻게 접근하는가?

첫번째 방법은 정태적 방법으로, 점을 사용하는 것이다: obj.a

두번째 방법은 역동적 방법으로, 대괄호를 사용하는 것이다: obj['a']

const obj = {
  year: 2023,
  name: "John"
}

console.log(obj['year']) // 2023
console.log(obj.name) // John

 

11. 배열을 활용하기 위한 주요 메소드들은 무엇인가?

  • forEach - 그 배열을 도는 반복 메소드로, 아무것도 리턴하지 않는다. 일반적인 for 고리에 비해 좀 더 우아한 대체 방식을 제공해준다.
  • filter(callback, [args]) - 주어진 함수를 사용하는 배열을 필터링하는 데 사용되는 메소드. 원 배열로부터 callback(item, i, arr) 함수가 참값으로 돌려준 요소들만 포함된 새로운 배열을 생성한다.
  • map(callback, [args]) - 배열을 변형시키기 위한 메소드. 한 배열의 각 요소에 대하여 callback(item, i, arr) 함수를 불러 나온 결과로 구성된 새로운 배열을 생성한다.
  • reduce(callback, [initValue]) - 중간 결과를 유지하면서 배열의 각 요소를 순차적으로 처리하는 메소드.

 

12. 객체 (Object) 를 생성하는 방법들에는 무엇이 있는가?

생성자 (constructor) 함수 사용하기: 

function User(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
const user = new User('John', 'Johnson');
console.log(user); // { firstName: 'John', lastName: 'Johnson' }

문자 표기법 (생긴 그대로) 사용하기:

const user = {
  firstName: 'John',
  lastName: 'Johnson'
};

오브젝트 클래스 사용하기:

const user = Object.create({
  firstName: 'John',
  lastName: 'Johnson'
});

 

13. 프로미스 (Promise) 란 무엇인가?

프로미스는 비동기 코드와 함께 작동하도록 고안된 객체 (Object) 다. 이것은 자신의 상태를 직접 관리한다. 처음에 프로미스는 계류 상태에 있지만, 비동기 코드가 성공적으로 실행되면 (작업) 이행 상태로, 오류가 발생하면 (작업) 거부 상태로 전환한다. 하나의 프로미스는 두 개의 회신 (callback) 함수를 수용한다.

  • onFulfilled: 프로미스가 이행될 때 촉발됨
  • onRejected: 프로미스가 거부될 때 촉발됨

사용 패턴은 다음과 같다.

  1. 어떤 코드를 비동기적으로 실행해야 할 때 프로미스를 생성하고 그 코드를 돌려준다.
  2. 그 프로미스를 받자마자 그 외부 코드는 onFulfilled 와 onRejected 함수들을 건네준다.
  3. 이 과정을 완료하자마자 그 비동기 코드는 이 프로미스를 이행 또는 거부 상태로 전환하고 자동적으로 상응하는 회신 함수를 호출한다.

 

14. async/await 가 무엇이고 어떻게 사용하는가?

async/await 는 프로미스와 함께 작동하기 위한 특수한 문법이다. 

async 문법과 함께 선언된 함수는 항상 프로미스를 돌려준다.

await 키워드는 자바스크립트 해석기가 실행을 계속하기 이전에 await 쪽의 프로미스가 이행될 때까지 기다리게 만든다. 이행이 되고 나면 프로미스는 그 결과를 돌려주고, 코드 실행이 진행된다. await 는 일반적인 함수들에서는 사용될 수 없다.

 

 

15.  어떤 객체 (Object) 가 배열인지를 어떻게 아는가?

어떤 객체 (Object) 가 배열인지 아닌지를 확인하려면 Array.isArray() 메소드를 이용하면 된다. 이것은 객체 (Object) 를 입력값으로 받아서 그 객체 (Object) 가 배열이면 참, 배열이 아니면 거짓을 돌려준다.

const obj1 = { person: 'John' }
const obj2 = new Array(2)
const obj3 = []

console.log(Array.isArray(obj1)) // false
console.log(Array.isArray(obj2)) // true
console.log(Array.isArray(obj3)) // true

 

16. 스프레드 연산자의 목적이 무엇인가?

스프레드 연산자 (...) 는 배열과 객체 (Object) 의 내용물을 꺼내는 데에 사용된다.

이것은 배열 및 스트링과 같이 반복 가능한 요소들을 확장시킬 수 있게 해준다.

  • 함수를 한 번 호출할 때 예상되는 아규먼트의 수가 0 또는 그 이상인 함수들에 사용된다.
  • 배열을 문자 그대로 또는 식으로 쓸 때
  • 키-값 쌍의 수가 0 또는 그 이상인 객체 (Object) 를 문자 그대로 쓸 때
const date = [2000, 3, 7] 
const newArray = [...date] // [2000, 3, 7]

 

17. 객체 (Object) 를 복사할 때 참조 의존 (reference dependency) 을 어떻게 피하는가?

객체 (Object) 에 내포된 객체 (Object) 가 없을 때, 예시)

const obj = {
 firstName: 'John',
 lastName: 'Johnson'
}

이 경우 스프레드 연산자나 Object.assign() 메소드를 사용할 수 있다:

const copy = {...obj}
// or
const copy = Object.assign({}, obj)

객체 (Object) 에 내포된 객체 (Object) 가 있을 때:

const obj = {
    data: {
        id: 1
    }
}

이 경우 깊은 복사를 수행해야 한다.

느리지만 돌아가는 방법은:

const copy = JSON.parse(JSON.stringify(obj))

이 메소드는 원형이나 함수를 가지고 있지 않은 객체 (Object) 에 적합하다.

다른 방법으로 lodash 라이브러리의 deepClone() 함수를 사용할 수도 있다.

 

18. 함수의 맥락을 어떻게 바꾸는가?

  1. bind() 메소드를 사용하면 묶인 맥락과 함께 새로운 함수를 돌려준다.
function foo() {
    return this
}
const obj = { name: 'John' }
const newFoo = foo.bind(obj)
console.log(newFoo()) // { name: 'John' }

  2. call() 과 apply() 메소드를 사용한다. 주요 차이는 call()은 아규먼트들을 순차로 수용하는 반면 apply()는 아규먼트들의 배열을 두 번째 파라미터로 수용한다는 것이다.

function foo() {
    return this
}

const obj = { name: 'John' }
foo.call(obj, 'arg1', 'arg2') // { name: 'John' }
foo.apply(obj, ['arg1', 'arg2']) // { name: 'John' }

 

19. 3변수 (ternary) 연산자는 무엇인가?

3변수 연산자는 if-else 문을 위한 축약된 표기법이다. 이 연산자는 물음표와 콜론으로 표현된다. 3변수라고 불리는 이유는 세 개의 아규먼트를 받는 유일한 연산자이기 때문이다.

 

조건? 식 1 : 식 2

num >= 10 ? 'more than 10' : 'less than 10'
// is equal to
if (num >= 10) {
    return 'more than or equal to 10'
}
return 'less than 10'

 

20. 구조 해체 (destructuring) 란 무엇인가?

구조 해체는 배열이나 객체 (Object) 의 내용물을 다수의 변수로 꺼낼 수 있게 해주는 문법이다.

const arr = ['John', 'Johnson']
const [firstName, lastName] = arr
console.log(firstName, lastName) // John Johnson

OR

const obj = {
    firstName: 'John',
    lastName: 'Johnson'
}
const { firstName, lastName } = obj;
console.log(firstName, lastName) // John Johnson

 

21. DOM 이 무엇인가?

DOM 의 뜻은 Document Object Model (문서 객체 모형) 이다. 이것은 HTML 문서를 태그의 나무로 대표한다.

예시)

DOM 안의 모형 각각이 하나의 객체다.

 

HTML 문서의 기본 요소들은 태그들이다.

문서 객체 모델 (DOM) 에 따르면 각 HTML 태크는 하나의 객체다. 내포된 태그들은 그들의 부모 요소의 "자식들" 이다. 한 태그 안의 글자 역시 하나의 객체다. 이 모든 객체들은 자바스크립트를 사용하여 접근가능하고, 이것들을 사용하여 페이지를 조작할 수 있다.

 

22. 사건 고리 (Event loop) 란 무엇인가?

사건 고리 - 코드의 실행을 관리하는 구조 장치. 이것은 사건의 처리와 과업의 실행을 올바른 순서로 다룬다. 사건 고리의 주요한 목적은 자바스크립트가 단일 스레드 환경에서 작동함에도 비동기 작업들을 다룰 수 있도록 하는 것이다. 서버 요청과 같은 비동기 작업이 완성되면 사건 고리는 그에 상응하는 사건을 사건 대기 행렬에 가져다 놓는다. 사건 고리는 그 행렬을 계속해서 돌면서 사건들이 도착한 순서대로 그것들을 처리한다. 사건 고리는 그 대기 행렬에서 하나의 사건을 받아 실행으로 넘긴다. 만약 그 사건이 회신 또는 핸들러를 포함하고 있으면 그것이 불러지고 그 사건에 연관된 코드가 실행된다. 사건 고리는 또한 타이머 및 마이크로태스크 (프로미스) 와 같은 다른 과업들도 다룬다. 이것은 일관성을 지키고 코드 실행의 주 스레드를 막지 않도록 모든 과업들의 실행 순서를 관리한다.

 

요약하면, 자바스크립트의 사건 고리는 대기 행렬 내 사건들을 다루고 그에 상응하는 코드를 올바른 순서대로 실행함으로써 비동기 작업들을 관리한다. 이것은 자바스크립트가 비동기 작업들을 하고 있을 때 즉각적으로 반응하고 효율적으로 자운을 활용할 수 있도록 해준다.

 

 

이 주제는 중요하고 별개의 글에서 다뤄져야 할 정도로 가치 있으므로 제시된 링크의 영상 시청을 매우 추천하는 바이다.

더 배우기

 

23. 원형 상속이란 무엇인가?

자바스크립트의 모든 객체는 속성을 가지고 있다 - 원형(프로토타입)이다. 메소드와 속성들이 그 원형에 추가될 수 있다. 그 원형을 기반으로 하여 다른 객체들이 생성될 수 있다. 생성된 객체는 자동적으로 원형의 메소드와 속성들을 상속받는다. 그 객체 안에 속성이 부재하면 원형 안에서 그것을 찾는 작업이 수행된다.

 

24. 임의 연쇄 연산자란 무엇인가?

임의 연쇄 연산자 (?.) 는 (?.) 의 결과가 undefined 나 null 일 경우 평가를 멈추고 undefined를 돌려준다.

이 user 객체를 살펴보자. 대부분의 유저들은 user.address.street 와 함께 주소 (user.address) 를 가지고 있다. 이 경우 임의 연쇄 연산자는 주소 안에 구체적인 거리 (user.address.street) 를 가지고 있지 않은 유저의 거리의 접근하려고 할 때 오류가 나는 것을 막도록 도와준다.

const user = {};

console.log(user.address.street) // Error!
console.log(user?.address?.street) // undefined. No Error

 

25. 그늘 (shadow) DOM 이란 무엇인가?

그늘 DOM 은 하나의 웹 페이지에서 요소들의 구조와 스타일을 캡슐에 싸는 것을 허용하는 웹 표준의 세트다. 이것은 하나의 요소 안에 기거하는 DOM의 특수한 분절을 대표한다. 그늘 DOM은 그 페이지의 전반적인 구조와 상충하지 않는 고립되고 스타일 적용된 내용을 가지고 있는 컴포넌트와 위젯을 생성하는 데에 사용된다.

 

26. 회귀 (recursion) 란 무엇인가? 어떻게 사용하는가?

회귀 하나의 함수가 자기 내부에서 스스로를 재사용하여 문제를 해결하는 문제 해결 접근 방식이다. 단순한 용어로, 어떤 함수가 자기자신을 호출할 때를 말한다.

 

회귀 함수는 다음으로 구성되어 있다:

  1. 종료 조건 혹은 기본 케이스
  2. 회귀의 단계 - 문제를 더 단순한 형태로 축소하고자 하는 방식
function factorial(x) {
   if (x === 0) {
      return 1;
   }
   return x * factorial(x - 1);
}

기본 케이스는 필수 조건이다. 이걸 안 하면 함수 호출의 무한 고리로 인해 스택 오버플로우로 이어진다.

 

27. 함수 식과 함수 선언의 차이는 무엇인가?

함수 선언은 함수를 선언하는 전통적인 방식이다.

function foo() {
    console.log('Hello World');
}

함수식:

let foo = function() {
    console.log('Hello World');
}

함수 선언을 하면 그 함수가 생성되고 다른 어떤 값들과 다르지 않게 하나의 변수에 할당된다. 근본적으로, 함수는 "foo" 변수에 저장되는 값이기 때문에 어떻게 정의되든지 상관 없다. 그러나 함수 선언은 코드 블록이 실행되기 전에 처리되는데, 그 뜻은 전체 코드 블록에 걸쳐 가시화되어 있다는 것이다. 반면 함수식은 실행 흐름이 그에 달했을 때에서야 생성된다.

 

28. 생성자 (constructor) 함수란 무엇인가?

생성자 함수란 객체를 생성하는 데 상ㅇ되는 일반 함수다. 그러나 이것을 사용하는 데에는 따라야 할 두 가지 규칙이 있다.

  1. 생성자 함수의 이름은 대문자로 시작해야 한다.
  2. 생성자 함수는 new 연산자를 사용하여 호출되어야 한다.
function User(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
    this.role = 'user'
}

const user = new User('John', 'Johnson')
console.log(user.firstName) // John

new 연산자를 사용하여 생성자 함수가 생성되면 다음과 같은 일이 일어난다.

  1. 새로운 빈 객체가 생성되고 여기에 할당된다.
  2. 생성자 함수 안의 코드가 실행된다. 전형적으로, 이 코드는 이 객체를 변형시키고 새로운 속성들을 추가한다.
  3. 이것의 값이 돌려진다.

29. 한 객체로부터 키들의 목록과 값들의 목록을 어떻게 얻는가?

Object.keys() 를 이용하여 키들의 목록을 얻고 Object.values() 를 이용하여 값의 목록을 얻을 수 있다.

const user = {
    firstName: 'John',
    lastName: 'Johnson'
}

const keys = Object.keys(user)
const values = Object.values(user)

console.log(keys) // ['firstName', 'lastName']
console.log(values) //

 

30. ES6의 새로운 기능들의 예를 대보아라.

가장 흔한 것들은

  • let 과 const -  블록 범위와 함께 변수를 선언하는 새로운 키워드 let 과 const 의 도입.
  • 화살표 함수 - 화살표 함수의 개념은 더 간결하고 깔끔한 함수 정의를 하게 해준다.
function add(a, b) { return a + b } // Regular function
const add = (a, b) => a + b // Arrow function
  • 기본 파라미터 - 함수 파라미터들에 대해 기본값을 정의할 수 있다.
function greet(name = 'Anonymous') { console.log(Hello, ${name}!) }
greet(); // "Hello, Anonymous!"
greet('John') // "Hello, John!"
  • 스프레드 연산자 (...) - 스프레드 연산자는 함수 아규먼트들에 대해 배열이나 객체의 요소들을 꺼낼 수 있게 해주고 새로운 배열 및 객체들을 생성할 수 있게 해준다.
const numbers = [1, 2, 3];
console.log(...numbers) // 1 2 3
const array1 = [1, 2, 3]; 
const array2 = [...array1, 4, 5] // [1, 2, 3, 4, 5]
  • 구조 해체 - 구조 해체는 배열이나 객체로부터 값을 추출하여 변수에 할당할 수 있도록 해준다.
const person = { name: 'John', age: 30, city: 'London' }
const { name, age } = person;
console.log(name, age) // "John 30"
const numbers = [1, 2, 3]
const [first, second] = numbers;
console.log(first, second); // 1 2

 

31. ES6 에서 클래스 상속은 어떻게 하는가?

클래스 상속은 부모 클래스의 이름 뒤에 "extends" 키워드를 사용하면 된다.

class User {
    firstName = 'John'
    lastName = 'Johnson'
}

class Customer extends User {
    cart
}

 

32. 자바스크립트의 마이크로태스크, 매크로태스크가 무엇인가?

자바스크립트에서 마이크로태스크와 매크로태스크는 사건 고리 안에서 실행되어야 하는 과업들의 타입을 의미한다.

마이크로태스크는 브라우저가 페이지를 재발행하기 전에 현재 사건 고리 내에서 실행되어야 하는 과업들이다. 이것들은 보통 Promise.then(), process.nextTick() (Node.js 의 경우), 또는 MutationObserver 와 같은 메소드를 사용하여 실행 행렬에 더해진다. 마이크로태스크의 예시에는 프로미스 핸들러와 DOM 돌연변이의 실행도 포함된다. 

반면 매크로태스크는 현 사건 고리가 끝난 후, 또는 변형된 것들이 화면에 그려지기 전에 실행되어야 하는 과업들이다. 이것에는 setTimeout, setInterval, requestAnimationFrame, 및 입력 사건과 네트워크 요청과 같은 것을 사용하여 사건 행렬에 더해지는 과업들을 포함한다.

매크로태스크는 현 사건 고리 속 모든 마이크로태스크가 실행된 후에 실행된다. 마이크로태스크와 매크로태스크의 차이는 실행순서를 결정하고 자바스크립트 내에서 서로 다른 과업들의 우선순위를 관리하도록 해주기 때문에 중요하다. 마이크로태스크가 더 높은 우선순위를 가지고 있고 매크로태스크 이전에 실행되어 인터페이스 업데이트를 더 빠르게 해주고 주 자바스크립트 실행 스레드를 막지 않도록 해준다.

 

33. 발생기 (generator) 란 무엇인가?

발생기는 값들을 필요한 대로 순차적으로 하나 씩 생산한다. 발생기는 객체와 함께 잘 작동하고 그것이 데이터 흐름을 생성하기 쉽게 만들어준다. 

발생기를 선언하는 데에는 특수한 문법이 사용된다. - 발생기 함수다.

function* generateSomething() {
    yield 10;
    yield 20;
    yield 30;
    return 40;
}

next() 는 발생기의 주요 메소드이다. next() 가 호출되면 가장 가까운 산출식이 나올 때까지 코드를 실행하기 시작한다. 값이 부재할 수도 있는데 그 경우 undefined 로 표출된다. 산출식에 도달하면 함수 실행은 중단되고 그에 상응하는 값을 외부 코드에 돌려준다.

let generator = generateSomething();
let first = generator.next();

34. 브라우저에 데이터를 저장하는 메소드에는 무엇들이 있는가?

브라우저에 데이터를 저장하는 메소드에는 몇 가지가 있다.

  • LocalStorage 와 SessionStorage: 키-값 쌍을 브라우저에 저장한다. 이것들에 저장된 데이터는 베이지가 새로고침된 후에도 유지된다. 두 저장 방식 다 키와 값에 스트링 타입만 사용할 수 있어서 객체는 JSON.stringify()를 사용하여 변환되어야 한다.
  • Cookie: 브라우저에 저장된 작은 데이터 스트링. 쿠키는 주로 Set-Cookie 헤더를 사용하여 웹 서버에 설정된다. 그러면 브라우저는 쿠키 헤더를 사용하여 거의 모든 동일 도메인 요청에 자동적으로 그것을 추가한다. 쿠키 하나는 4kb 까지 데이터를 보유할 수 있다. 브라우저에 따라 사이트 마다 20 개 이상의 쿠키가 허용된다.
  • IndexedDB:  localStorage 보다 더 강력한 내장 데이터베이스. 다수의 키 타입이 이용 가능하고 값은 거의 아무거나 될 수 있는 키-값 쌍의 저장방식이다. IndexedDB 는 신뢰성 있는 거래, 키 범위 질의어 (query) 및 인덱스를 지원하고, localStorage 보다 더 많은 데이터를 저장하도록 허용한다. IndexedDB 는 오프라인 애플리케이션을 위하 고안되었으며 서비스 워커 및 다른 기술들과 합체될 수 있다.

35. sessionStorage 와 localStorage 의 차이점은 무엇인가?

sessionStorage 와 localStorage 는 객체를 키-값 형식으로 브라우저에 저장하도록 허용한다. 

주요한 차이점들은:

  • localStorage 는 10MB 까지 데이터를 저장할 수 있는 반면 sessionStorage는 5MB까지 할 수 있다.
  • 브라우저 탭이 닫히면 localStorage 안의 데이터는 삭제되지 않지만 sessionStorage의 데이터는 삭제된ㄷ.
  • localStorage의 데이터는 어떤 윈도우에서도 접근 가능하지만 sessionStorage의 데이터는 동일한 윈도우의 브라우저에서만 접근 가능한다.

36. 정규 표현 (regular expression) 이란 무엇인가?

정규 표현이란 특수한 규칙과 패턴으로 정의된 스트링이다. 이것은 스트링 내부의 복잡한 구조를 감지하고 작업할 수 있게 해주는 강력한 도구다.

let str = "We will, we will rock you"

console.log(str.match(/we/gi)) // ['We', 'we']

 

37. WeakSet 과 WeakMap 이 무엇이고 Map 이나 Set 과는 어떻게 다른가?

WeakMap 과 Map 의 첫번째 차이는 WeakMap 안의 키들은 원시적인 값이 아닌 반드시 객체여야 한다는 것이다.

두번째 차이점은 데이터 구조의 메모리 저장이다. 자바스크립트 엔진은 값들에 도달할 수 있는 한 메모리 내에 그 값들을 보유한다. 즉, 그것들을 이요할 수 있다.

보통 객체 속성들, 배열 요소들, 또는 다른 데이터 구조들은 데이터 구조가 존재하는 한 것들에 다른 참조할 것들이 없더라도 메모리 안에서 닿을 수 있고 그 안에 보유된다.  

WeakMap 과 WeakSet의 경우에는 다르게 작동한다. 한 객체가 닿을 수 없게 되는 순간 그것은 데이터 구조에서 제거된다.

 

38. 왜 동일한 필드를 가진 두 개의 객체를 비교하면 거짓을 돌려주는가?

const test1 = { value: 3 }
const test2 = { value: 3 }
console.log(test1 == test2) // false

객체들은 메모리 구역에 대한 참조를 기반으로 하여 비교된다. 자바스크립트에서 test1 과 test2 객체는 비록 같은 필드를 가지고 있다고 해도 서로 다르다. 객체는 오직 동일한 객체일 때에만 동일하다고 여겨진다.

 

39. 왜 우리는 원시 타입에서 메소드를 부를 수 있는가?

자바스크립트는 원시 데이터 타입들 - 스트링, 숫자, 등 - 과 마치 객체처럼 작업하는 것을 허용한다. 원시 데이터타입들은 메소드들을 가지고 있다.

이 기능을 이용 가능하게 하기 위해 각 원시 데이터 타입은 자신을 포장하는 객체를 가지고 있다: String, Number, Boolean, 그리고 Symbol 이 그것들이다. 이 포장 객체들 덕분에 원시 데이터 타입들도 toLowerCase() 나 toUpperCase() 와 같은 여러 메소드들의 세트를 가지고 있다. 

 

40. 한 객체가 어느 클래스로부터 생성되었는지 어떻게 확인하는가?

하나의 객체가 어느 클래스에서 생성되었는지는 상속인지 아닌지를 고려하는 instanceof 연산자를 사용하여 확인할 수 있다. 

class Person {}
const person = new Person()
console.log(person instanceof Person) // true

 

41. 사이트에서 얼마만큼의 시간을 보냈는지 10초마다 초 단위로 기록하는 코드를 작성하시오.

let time = 0
setInterval(() => {
    time += 10
    console.log(time)
}, 10000)

 

42. 순수 (pure) 함수란 무엇인가?

순수 함수란 두 가지 조건을 충족시키는 함수다.

  1. 그 함수가 동일한 세트의 아큐먼트들과 함께 호출될 때마다 동일한 결과를 돌려준다.
  2. 부작용이 없다. 즉, 그 함수 바깥의 변수들을 변형시키지 않는다.
function calculate(num) {
    return calculate * 0.05;
}
console.log(calculate(15)) //calculate() function will always return the same result if we pass the same parameter

 

43. 상위 (higher-order) 함수란 무엇인가?

const nums1 = [1, 2, 3]
const nums2 = nums1.map(function(num) {
    return num * 2;
})
console.log(nums2) // [2, 4, 6]

상위 함수란 또 다른 함수를 아규먼트로 받아 어떤 함수를 결과로 돌려주는 함수다.

 

44. 회신을 사용하여 비동기 코드로 작업할 수 있는데 왜 프로미스가 필요한가?

회신 함수를 사용하여 서버로부터 어떤 데이터를 비동기적으로 가져오기를 원한다면 다음과 같은 결과를 낸다:

func((x) => {
  anotherFunc(x, (y) => {
    andAnotherFunc(i, (j) => {
      // some code
    })
  })
})

이것은 각 회신 함수가 또 다른 회신 함수에 내포되어 있고 내부의 회신 함수는 각자의 부모 함수에 의존하므로 회신 지옥이라 불린다. 

 

프로미스를 사용하면 위의 코드를 이렇게 작성할 수 있다:

func()
.then((x) => {
  return anotherFunc(x)
})
.then((y) => {
  return andAnotherFunc(y)
})
.then((i) => {
  return i
})

프로미스를 사용하면 실행 순서가 명확하고 코드를 더 읽기 쉽게 만들어 준다.

 

45. 묶음 (bind) 메소드를 실행하는 코드를 직접 작성하시오.

클로져와 apply() 메소드를 사용하여 함수를 맥락에 한 데 묶을 수 있다.

function bind(context, func) {
  return function(...args) {
    func.apply(context, args)
  }
}

 

46. 더하기, 빼기, 곱하기, 나누기, 그리고 get 메소드와 함께 계산기를 만드시오. 이 함수는 반드시 임의 연쇄를 통해 작동해야 함.

function calculator() {
  let result = 0;

  function plus(val) {
    result += val;
    return this;
  }

  function minus(val) {
    result -= val;
    return this;
  }

  function divide(val) {
    result /= val;
    return this;
  }

  function multiply(val) {
    result *= val;
    return this;
  }

  function get() {
    console.log(result);
    return this;
  }

  return { plus, minus, divide, multiply, get };
}

let calc = calculator();
calc.plus(2).minus(1).plus(19).divide(2).multiply(3).get(); // 30

 

47. 숫자들의 배열을 받아 임의적인 순서로 분류하는 randomSort 함수를 작성하시오.

sort() 메소드와 Math.random() 메소드를 사용해도 됨.

function randomSort(array) {
  return array.sort(() => {
    return 0.5 - Math.random();
  });
}
const arr = [2, 1, 3, -2, 9]
console.log(randomSort(arr)) // [-2, 2, 1, 3, 9]
console.log(randomSort(arr)) // [2, 1, -2, 9, 3]
console.log(randomSort(arr)) // [-2, 1, 9, 2, 3]
console.log(randomSort(arr)) // [1, -2, 2, 3, 9]

 

48. 숫자들의 2차 배열을 받아 각 내포 배열로부터 가장 큰 숫자를 제거하는 deleteGreatedValue 함수를 작성하시오.

모든 내포된 배열을 돌면서 각 내포된 배열 내의 가장 큰 값을 얻어 그것을 삭제해야 한다.

function deleteGreatestValue(array) {
  for (let i = 0; i < array.length; i++) {
    const max = Math.max(...array[i]);
    const maxIndex = array[i].indexOf(max);
    array[i].splice(maxIndex, 1);
  }
  return array;
}

const arr = [[1, 4, 4], [2, 6, 3], [9, 2, 7]]
console.log(deleteGreatestValue(arr)) // [[1, 4], [2, 3], [2, 7]]

 

49. names[i] == heights[i] 인 이름들의 스트링 배열과 키들의 숫자 배열을 받는 sortPeople 함수를 작성하시오. 이름 배열은 키 배열에 근거하여 분류되어야 함.

function sortPeople(names, heights) {
  const array = [];
  for (let [i, name] of names.entries()) {
    array.push([name, heights[i]]);
  }
  return array.sort((a, b) => b[1] - a[1]).map(([name]) => name);
}
const names = ['John', 'Maria', 'Alexa', 'Robert']
const heights = [180, 160, 165, 187]
console.log(sortPeople(names, heights)) // ['Robert', 'John', 'Alexa', 'Maria']

 

50. nums 라는 숫자 배열을 받아 그 숫자들로 부터 가능한 모든 종류의 배열을 돌려주는 부분집합 함수를 작성하시오.

function subsets(nums) {
  let result = [[]];

  for (let num of nums) { // Iterate through each number in the nums array
    const currentSize = result.length; // Get the current size of result to use it in the loop.

    for (let i = 0; i < currentSize; i++) {
      let subArray = [...result[i], num]; // Create a new subarray by adding the current number to the result[i] element.
      result.push(subArray);
    }
  }

  return result; // Return all possible variations of arrays from the numbers.
}

 

51. 연결된 리스트를 어떻게 거꾸로 만드는가?

연결된 리스트를 입력값으로 받아 그 리스트를 역으로 돌려주는 reverseLinkedList 함수를 만들자.

 

접근방법:

  1. 이 함수는 결과 값을 null로 초기화 하고, 그 결과 값은 거꾸로된 리스트를 보유할 것이다.
  2. 이 함수는 뿌리값을 머리값으로 초기화 하고, 그것이 리스트의 시작점을 가리킬 것이다.
  3. 이 함수는 while 고리로 진입하여 뿌리가 리스트의 끝을 가리키는 null 이 될 때 까지 연속해서 돈다.
  4. 그 고리 안에서 그것은 결과가 이미 요소들을 가지고 있는지를 확인한다. 만약 그렇다면 root.val 이라는 현재 값으로 새로운 리스트 노드를 생성하고 다음 노드 결과를 가리킨다. 그리고나서 이 새로운 노드로 결과 값을 업데이트 한다.
  5. 만약 겨로가값이 아직 아무 요소도 가지고 있지 않다면 새로운 root.val 과 null 이라는 현재 값으로 새로운 리스트 노드를 생성하며 다음 노드를 가리킨다. 그리고 나서 새로운 노드로 결과값을 업데이트한다.
  6. 결과값을 업데이트 한 후 root.next 를 뿌리에 할당함으로써 리스트 안의 다음 요소로 넘어간다.
  7. while 고리가 끝나면 결과값에 저장된 거꾸로된 리스트를 돌려준다.

요약하면, 이 함수는 머리에서 꼬리까지 각 노드를 통과해 돌면서 연결된 리스트를 거꾸로 만들면서 각 값에 대한 새로운 리스트 노드를 만들고, 그에 맞춰 가리키는 지점도 업데이트 한다.

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */

function reverseLinkedList(node) {
  let result = null; // Initialize the result variable with null, it will hold the reversed list.
  let root = head; // Initialize the root variable with head, pointing to the start of the list.

  // While root is not null (until we reach the end of the list)
  while (root) {
    if (result) { // If result already has elements
      result = new ListNode(root.val, result); // Create a new list node with the current value root.val and a pointer to the next node result. Update result.
    } else { // If result doesn't have any elements yet...
      result = new ListNode(root.val, null); // Create a new list node with the current value root.val and null as the pointer to the next node. Update result.
    }
    root = root.next; // Move to the next element in the list.
  }
  return result; // Return the reversed list.
}

 

52. 연결된 리스트를 어떻게 분류하는가?

연결된 리스트를 입력값으로 받아 그 리스트의 분류된 버전을 돌려주는 sortList 함수를 만들자.

 

접근방법: 

  1. 주어진 연결된 리스트가 비어 있는지 아닌지를 확인한다.
  2. 그 연결된 리스트를 가로지르며 각 노드 값을 하나의 배열에 저장한다.
  3. 내장된 sort() 메소드를 이용하여 그 배열을 분류한다.
  4. 그 분류된 배열을 사용하여 새로운 연결된 리스트를 생성시킨다.
  5. 생성된 연결된 리스트의 머리를 돌려준다.
**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
function sortList (head) {
  if (!head) {
    return null;
  }
  let root = head;
  let arr = [];

  while(root){
    arr.push(root.val);
    root = root.next;
  }
  arr.sort((a, b) => a - b);

  let node = new ListNode(arr[0]);
  head = node;

  let temp = head;

  for(let i = 1; i < arr.length; i++){
    let node = new ListNode(arr[i]);
    temp.next = node;
    temp = temp.next;       
  }
  return head;
};