본문 바로가기
우아한 테크코스

생각해보기🤔 - 로또

by 해-온 2023. 6. 17.

 

🎱 JS의 class와 같은 기능을 class 문법 없이 구현할 수 있을까요?

생성자 함수와 프로토타입을 사용해 구현할 수 있다.

 

// 생성자 함수
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 메소드 추가
Person.prototype.sayHello = function () {
  console.log('안녕하세요! 제 이름은 ' + this.name + ' 입니다.');
};

// 인스턴스 생성
const person1 = new Person('철수', 25);
const person2 = new Person('영희', 23);

// 메소드 사용
person1.sayHello(); // 안녕하세요! 제 이름은 철수 입니다.
person2.sayHello(); // 안녕하세요! 제 이름은 영희 입니다.

 

🎱 prototype은 어디에 쓰나요?

프로토타입(prototype)은 JavaScript 객체에서 다른 객체로 메서드와 속성을 전달하거나 공유하는 기능을 제공한다.

프로토타입을 사용하면 여러 객체 사이에 중복되는 코드를 줄이고 메모리를 효율적으로 사용할 수 있다.

 

// 생성자 함수
function Animal(name) {
  this.name = name;
}

// 프로토타입에 기능 추가
Animal.prototype.eat = function () {
  console.log(this.name + ' 밥을 먹습니다.');
};

// 인스턴스 생성
const dog = new Animal('강아지');
const cat = new Animal('고양이');

// 메소드 사용
dog.eat(); // 강아지 밥을 먹습니다.
cat.eat(); // 고양이 밥을 먹습니다.

 

위의 예시를 보면, 각 동물 객체는 동일한 기능 "밥 먹기"를 가지고 있다.

이런 경우에 프로토타입을 사용하면 각 동물 객체에 "밥 먹기" 기능을 공유할 수 있다.

 

 

🎱 const newArray = [ ] 이라고만 했을 뿐인데, Array에서 제공해주는 메서드들을 쓸 수 있는 이유는 무엇인가요?

newArray = []를 선언함으로써 생성된 배열은 내장 객체인 Array의 인스턴스이다.

따라서 Array.prototype에 정의된 모든 메서드를 사용할 수 있다.

JavaScript의 내장 객체인 Array는 다양한 메서드(예: push, pop, map 등)를 포함하고 있다.

이 메서드들은 Array.prototype이라는 프로토타입 객체에 정의되어 있어, 모든 배열 인스턴스가 해당 메서드들을 공유하여 사용할 수 있다.

 

🎱 클로저란 무엇인가요?

함수와 그 함수가 선언된 렉시컬 환경(Lexical Environment)에 있는 변수들의 조합이다.

클로저를 이용하면 함수 밖에서 선언된 변수에 접근하여 값을 기억하는 것이 가능해진다.

 

function outer() {
  const outerVariable = 'I am outside!';

  function inner() {
    console.log(outerVariable);
  }

  return inner;
}

const innerFunction = outer(); // outer 함수 호출 결과로 inner 함수를 반환
innerFunction(); // 'I am outside!' 출력

 

위 예시에서 outer 함수는 innter 함수를 반환한다.

inner 함수는 outerVariable이라는 outer 함수의 지역 변수를 사용하고 있다.

outer 함수를 호출한 InnerFunction은 inner 함수의 참조를 가지고 있다.

이 때 inner 함수가 자신이 선언된 렉시컬 환경과 연결된 클로저를 형성한다.

따라서 innerFunction을 호출했을 때 outerVariable에 접근할 수 있다.

 

 

🎱 일반적으로 함수는 기억력이 없습니다. 클로저는 어떻게 상태를 가질 수 있는 걸까요?

클로저는 함수 내에서 선언된 함수와 그 함수가 속한 렉시컬 환경에 접근할 수 있기 때문에 상태를 유지할 수 있다.

렉시컬 환경은 실행 시점의 모든 스코프에서 변수와 함께 생성되는 구조로, 함수와 변수 사이의 연결을 저장한다.

이러한 렉시컬 환경 덕분에 클로저는 다른 함수의 변수에 접근할 수 있는 것이다.

즉, 클로저는 함수와 그 함수와 관련된 변수들 사이의 연결을 유지하여 함수가 기억력을 갖게 해 준다.

 

 

function createCounter() {
  let count = 0; // 이 변수는 createCounter의 렉시컬 환경에 속함

  function counter() {
    count++; // counter 함수는 렉시컬 환경의 count 변수에 접근함
    return count;
  }

  return counter;
}

const myCounter = createCounter(); // 'createCounter' 함수 호출 결과로 반환된 'counter' 함수의 참조를 얻음

console.log(myCounter()); // 1 출력
console.log(myCounter()); // 2 출력

 

 

🎱 클로저는 어떤 때에 활용할까요?

1. 데이터 은닉

클로저를 사용하면 함수 내의 변수를 외부에서 직접 접근할 수 없도록 하여 정보를 은닉할 수 있다.

은닉한 데이터는 해당 함수를 통해서만 수정되거나 읽힌다.

이를 통해 객체의 속성을 보호하고 코드의 안정성을 높일 수 있다.

 

2. 즉시 실행되는 함수 표현식

클로저를 사용하여 함수가 정의된 후 바로 한 번 실행되는 방식이다.

코드의 전역 범위를 오염시키지 않고 라이브러리나 모듈 등을 작성할 때 사용한다.

 

3. 함수형 프로그래밍

클로저를 사용하여 함수를 리턴하는 고차 함수를 만들거나 커링과 같은 함수형 프로그래밍 기술을 사용할 수 있다.

 

 

🎱 DOM 변경을 비싼 작업이라고 하는 이유는 무엇일까요?

웹 페이지는 DOM을 사용하여 HTML, XML 등의 문서를 트리 구조로 표현하고, 자바스크립트를 통해 페이지의 구성 요소를 조작한다.

DOM 변경이 반복적으로 발생하면 브라우저가 레이아웃을 계산하고 화면을 계속해서 다시 그리는 작업을 수행해야 한다.

만약 빈번한 DOM 변경이 있는 경우 웹 페이지가 느리게 로드되거나 반응이 느려질 수 있다.

 

따라서 DOM 변경을 최소화해 성능을 개선할 수 있다.

예를 들어 Virtual DOM을 사용하는 React의 경우 변경 사항을 먼저 메모리에 저장한 후, 한 번에 변경사항을 DOM에 반영한다.

 

 

🎱 DOM을 추가 / 삭제 / 수정하면 브라우저에서는 어떤 일이 일어나나요?

1. Reflow (레이아웃 재연산)

DOM 요소가 추가, 삭제, 변경되었을 때, 브라우저는 페이지의 레이아웃을 다시 계산해야 한다.

브라우저는 요소의 위치와 크기, 여백 등에 대한 정보를 다시 결정해야 한다.

 

2. Repaint (화면 다시 그리기)

페이지의 레이아웃이 변경되거나 요소의 스타일이 바뀌었을 때, 브라우저는 화면을 다시 그려야 한다.

브라우저는 변경된 요소들을 다시 렌더링 하고, 이를 화면에 반영해야 한다.

 

3. DOM 트리 업데이트

DOM을 추가, 수정, 삭제하는 경우 브라우저의 DOM 트리 구조도 갱신해야 한다.

 

 

🎱 이벤트 핸들러 함수를 만들 때 bind(this) vs 화살표 함수의 차이는 무엇인가요?

1. bind(this) 사용

  • bind(this)를 사용하면, 인스턴스 메서드에서 this를 해당 컴포넌트 인스턴스로 유지할 수 있다.
  • 클래스 메서드를 이벤트 핸들러에 연결하려면 생성자(constructor)에서 bind(this)를 호출해야 한다.
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(event) {
    // 여기서 'this'는 MyComponent 인스턴스를 참조
    console.log(this);
  }

  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

2. 화살표 함수

  • 화살표 함수는 자동으로 주변 스코프의 this 값을 상속한다. 따라서 별도로 bind(this)를 호출하지 않아도 된다.
  • 클래스 프로퍼티를 사용하여 메서드를 선언할 때, 생성자에서 별도로 bind(this)를 호출하지 않아도 된다.
class MyComponent extends React.Component {
  handleClick = (event) => {
    // 여기서 'this'는 MyComponent 인스턴스를 참조
    console.log(this);
  };

  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

 

🎱 함수가 실행될 때 this가 어떤 걸로 결정되는지 예상할 수 있나요?

this가 결정되는 방식은 함수가 어떻게 호출되었는지에 따라 달라진다.

 

1. 일반 함수 호출

function myFunction() {
  console.log(this);
}

myFunction(); // 'this'는 전역 객체를 참조 (브라우저에서는 Window 객체, Node.js에서는 Global 객체)


//실행 결과(Node.js)
<ref *1> Object [global] {
  global: [Circular *1],
  queueMicrotask: [Function: queueMicrotask],
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  structuredClone: [Getter/Setter],
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  atob: [Getter/Setter],
  btoa: [Getter/Setter],
  performance: [Getter/Setter],
  fetch: [AsyncFunction: fetch],
  crypto: [Getter]
}

2. 객체의 메서드 호출

const myObject = {
  myMethod() {
    console.log(this);
  },
};

myObject.myMethod(); // 'this'는 메서드를 소유한 객체인 'myObject'를 참조

//실행 결과
{ myMethod: [Function: myMethod] }

3. 생성자 함수 호출

function MyConstructor() {
  this.myProperty = "Hello";
  console.log(this);
}

const newInstance = new MyConstructor(); // 'this'는 새로 생성된 객체를 참조

//실행 결과
MyConstructor { myProperty: 'Hello' }

4. 명시적으로 this를 설정하는 함수 호출 (call, apply, bind)

function myFunction() {
  console.log(this);
}

myFunction.call({ customThis: true }); // 'this'는 전달한 객체({customThis: true})를 참조
myFunction.apply({ customThis: true }); // 'this'는 전달한 객체({customThis: true})를 참조

const boundFunction = myFunction.bind({ customThis: true });
boundFunction(); // 'this'는 전달한 객체({customThis: true})를 참조

//실행 결과
{ customThis: true }
{ customThis: true }
{ customThis: true }

5. 이벤트 리스너 내부에서의 호출

const myButton = document.getElementById("myButton");

myButton.addEventListener("click", function () {
  console.log(this); // 'this'는 이벤트가 발생한 DOM 요소를 참조 (여기서는 'myButton')
});

6. 화살표 함수

const myObject = {
  myFunction: () => {
    console.log(this);
  },
};

myObject.myFunction(); // 화살표 함수에서 'this'는 상위 스코프의 'this'를 상속 (주변에 있는 일반 함수가 아닌 경우 전역 객체)

//실행 결과 (Node.js)
{}

 

🎱 일반 함수와 화살표 함수는 무슨 차이가 있을까요?

1. 문법

일반 함수는 function 키워드를 사용하고, 화살표 함수는 => 문법인 화살표를 사용하여 선언한다.

 

2. this 바인딩

일반 함수는 this 값은 호출 컨텍스트에 따라 달라진다.

반면, 화살표 함수는 자체 this 바인딩이 생성되지 않고 상위 스코프의 this 값을 상속한다.

 

3. 생성자 사용 불가능

화살표 함수는 생성자로 사용할 수 없다.

따라서 new 키워드로 인스턴스를 생성할 수 없다.

 

function NormalFunction() {
  this.property = "Hello";
}

const instance1 = new NormalFunction(); // 동작

const ArrowFunction = () => {
  this.property = "Hello";
};

const instance2 = new ArrowFunction(); // TypeError: ArrowFunction is not a constructor

 

4. arguments 객체

일반 함수는 arguments라는 특별한 객체를 할당받아, 호출 시 전달된 인자를 참조할 수 있다.

반면, 화살표 함수의 경우 arguments 객체를 할당받지 않고, 상위 스코프의 arguments 객체를 참조한다.

 

function normalFunction() {
  console.log(arguments);
}

normalFunction(1, 2, 3); // Arguments(3) [1, 2, 3]

const arrowFunction = () => {
  console.log(arguments);
};

arrowFunction(1, 2, 3); // ReferenceError: arguments is not defined

 

 

 

 

 

댓글