모던 리액트 딥다이브 1장

Junho Yun·2024년 2월 6일
0
post-thumbnail

1장 리액트 개발을 위해 꼭 알아야할 자바스크립트

1.1 자바스크립트 동등 비교

Q. 리액트를 배우는데, 왜 자바스크립트의 동등 비교를 알아야할까?
A. 리액트에서 훅을 사용하다보면, 의존성 배열을 넣어주는 일이 있다. 이 의존성 배열의 동작 원리도 JS의 동등 비교가 포함된다.
또한, 리액트의 리랜더링 되는 조건 중 하나인 props의 변경을 감지 하는 원리 또한 동등 비교 로직이 포함되어 있다.

1.1.1 자바스크립트의 데이터 타입

  • 원시 타입 (7가지)
    boolean
    null -> typeof null === 'object' // true 바보 같은 JS의 실수
    undefined
    number
    string
    symbol
    bigint

  • 객체 타입
    object (배열, 함수, 정규식, 클래스 등)

개체 타입은 다른 말로 "참조 타입이라고도 한다" 그렇게 말하는 이유에 대해서 알아보자

1.1.2 값을 저장하는 방식의 차이

  • 원시타입은 불변 형태의 값을 메모리에 저장합니다.
  • 객체(참조) 타입은 불변이 아닌 변할 수 있는 (프로퍼티 삭제,추가,수정) 형태로 저장합니다.

[원시타입 예시]

let num1 = 42;
let num2 = num1; // num2에 num1의 값(42)를 넣어줍니다.
num1 = 100; // 그 후 처음 num1 변경 
console.log(num1); // Output: 100 
console.log(num2); // Output: 42 -> num2에는 불변의 값 자체를 저장했기에 영향이 없습니다.

[객체타입 예시]

let obj1 = { name: "John" }; // ex 101 주소에 name은 'John' 이다 
let obj2 = obj1; // 복사 => obj2는 obj1의 주소값을 저장합니다. (101 저장)
obj1.name = "Jane"; // 주소값 안에 있는 값을 변경합니다. -> 101 주소에 name은 'Jane'
console.log(obj1.name); // Output: "Jane" -> 101주소는 Jane
console.log(obj2.name); // Output: "Jane" -> 101주소는 Jane

1.1.3 자바스크립트의 또 다른 비교 공식, Object.is

  • == 과 Object.is의 차이점 => Object.is는 타입이 다르면 그냥 false 입니다
  • === 과 Object.is의 차이점 => Object.is가 조금 더 이해하기 쉽게 비교합니다.
-0 === +0 // true
Object.is(-0,+0) // false

그치만 객체비교에서는 별차이가 없습니다.

1.1.4 리액트에서의 동등 비교

리액트에서 동등비교를 하는 로직은 위에서 설명한 Object.is를 사용합니다. (with polyfill)

https://github.com/facebook/react/blob/main/packages/shared/objectIs.js

function is(x: any, y: any) {
  return (
    (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
  );
}

const objectIs: (x: any, y: any) => boolean =
  // $FlowFixMe[method-unbinding]
  typeof Object.is === 'function' ? Object.is : is;

export default objectIs;
import is from './objectIs';
import hasOwnProperty from './hasOwnProperty';

function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    const currentKey = keysA[i];
    if (
      !hasOwnProperty.call(objB, currentKey) ||
      // $FlowFixMe[incompatible-use] lost refinement of `objB`
      !is(objA[currentKey], objB[currentKey])
    ) {
      return false;
    }
  }

  return true;
}

export default shallowEqual;

1.2 함수

1.2.1 함수란

함수는 코드를 재사용할 수 있게 해주며, 코드의 구조를 분명하게 하여 가독성과 유지보수성을 향상시킵니다.

기본적으로 매개변수를 받아서, 특정 값을 return 해주는 방식입니다.

1.2.2 함수를 정의하는 4가지 방법

  1. 함수 선언문
function greet(name) {
  return `Hello, ${name}!`;
}
  1. 함수 표현식
const greet = function(name) {
  return `Hello, ${name}!`;
};
  1. Function 생성자 - 생략
  2. 화살표 함수
const greet = (name) => `Hello, ${name}!`;
  1. 즉시 실행 함수
(function() {
  console.log('This runs right away');
})();
  1. 생성자 함수 (Constructor Functions)
function createPerson(name) {
  return {
    name: name,
    greet: function() {
      return `Hello, ${this.name}!`;
    }
  };
}

const person1 = createPerson('Alice');

1.2.3 화살표 함수와 일반 함수 선언/표현식에서의 this 키워드 동작 차이

일반 함수에서 this의 값은 함수가 호출되는 방식에 의해 결정됩니다. 즉, 실행 컨텍스트(execution context)에 따라 this가 달라집니다.

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

const myObj = {
  method: function() {
    console.log(this);
  }
};

regularFunction(); // 전역 객체 또는 엄격 모드에서는 undefined
myObj.method(); // myObj를 가리킴

화살표 함수에서는 this가 다르게 작동합니다. 화살표 함수는 자신이 선언된 시점의 실행 컨텍스트의 this를 "캡처"합니다. 이는 화살표 함수가 this의 값을 자신이 생성될 때의 렉시컬 컨텍스트(lexical context)에서 가져오는 것을 의미합니다

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

const myObj2 = {
  method: arrowFunction
};

arrowFunction(); // 전역 컨텍스트의 this를 가리킴 (브라우저에서는 window 객체)
myObj2.method(); // 여전히 전역 컨텍스트의 this를 가리킴, 화살표 함수는 자신이 속한 객체의 this를 바인딩하지 않음

Q 비교 코드 문제

const myObject = {
  value: 'My Object',
  regularMethod: function() {
    console.log(this.value); // 'My Object' - 일반 함수는 호출하는 객체의 컨텍스트를 `this`로 가짐
    setTimeout(function() {
      console.log(this.value); // undefined - 일반 함수 내의 `this`는 전역 객체를 가리킴
    }, 1000);
  },
  arrowMethod: function() {
    setTimeout(() => {
      console.log(this.value); // 'My Object' - 화살표 함수는 자신을 둘러싼 렉시컬 컨텍스트의 `this`를 가짐
    }, 1000);
  }
};

myObject.regularMethod();
myObject.arrowMethod();

1.2.4 함수 호이스팅에 대해 설명하고, 함수 선언문과 함수 표현식에서 호이스팅이 어떻게 다르게 동작하는지 예시를 들어 설명해주세요.

함수 선언문의 호이스팅:
함수 선언문은 전체 함수가 호이스팅되므로, 함수 선언문으로 정의된 함수는 실제 코드상 위치에 관계없이 코드의 어느 곳에서든 호출할 수 있습니다.

console.log(greet('Alice')); // "Hello, Alice!"

function greet(name) {
  return `Hello, ${name}!`;
}

함수 표현식의 호이스팅:
함수 표현식(변수에 할당된 함수)은 변수 호이스팅의 규칙을 따릅니다. 변수 선언은 호이스팅되지만, 할당은 호이스팅되지 않습니다. 따라서 함수 표현식으로 정의된 함수는 해당 함수가 할당되기 전에는 호출할 수 없습니다.

console.log(greet('Alice')); // TypeError: greet is not a function

var greet = function(name) {
  return `Hello, ${name}!`;
};

1.2.5 함수 만들 때 기본 원칙 (항상 X)

  1. side-effect 최소화 (순수함수로 만들기)
    위의 내용은 useEffect를 최소화 해야한다는 React 이론의 베이스가된다
  2. 함수의 단위는 작게해라
    코드가 길고 짧고가 아닌 기능적으로 작아야 이해하기가 쉽고, 유지보수가 쉽다
  3. 이름은 최대한 정확하게

1.3 클래스

1.3.1 클래스란 무엇인가?

특정 객체를 반복해서 만들기 위한 기본 템플릿, 붕어빵(객체)를 만들기 위한 붕어빵 틀(클래스)라고 비유 하기도 한다.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

// 인스턴스 생성
const person1 = new Person('Alice', 30);
person1.greet(); 
// 출력: Hello, my name is Alice and I am 30 years old.
  • 확장시키기
class Employee extends Person {
  constructor(name, age, position) {
    super(name, age); // 부모 클래스의 constructor 호출
    this.position = position;
  }

  introduce() {
    console.log(`Hello, my name is ${this.name}, I am ${this.age} years old and I work as a ${this.position}.`);
  }
}

// Employee 인스턴스 생성
const employee1 = new Employee('Bob', 25, 'Software Engineer');
employee1.introduce(); // 출력: Hello, my name is Bob, I am 25 years old and I work as a Software Engineer.
employee1.greet(); // 부모 클래스의 메서드 호출 가능

getter와 setter

class Person {
  constructor(name, age) {
    this._name = name; 
    // _name은 내부 프로퍼티로, 직접적인 접근을 권장하지 않음
    this._age = age;
  }

  get name() {
    return this._name;
  }

  set name(newName) {
    this._name = newName;
  }
  // age에 대한 getter와 setter도 유사하게 구현할 수 있음
}

자바스크립트에서는 원래 프로토타입이라는 개념이 있고, 클래스 또한 이 개념을 바탕으로 작동합니다. 그리고 클래스로 구성된 코드는 같은 동작을 하는 함수로도 변경할 수 있으니 어느 하나가 맞다가 아닌 필요에 따라 사용할 것

profile
의미 없는 코드는 없다.

0개의 댓글