ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL 2021.7.26 : 자바스크립트 클래스의 this와 파이썬 클래스의 self 비교(feat. React)
    항해99 1기 수료후 TIL 2021. 7. 26. 12:11

    출처: 드림코딩엘리

    자바스크립트 클래스에서 this

    자바스크립트에서 this는 상황마다 가리키는 것이 달라진다. 클래스를 정의하는 경우에는 this는 클래스가 생성하는 인스턴스를 가리키고, 클래스 정의시 내부 로직에서 변수와 메서드를 선언하는 경우에는 this가 꼭 나와줘야 한다(= this 없이는 오류가 나온다). 

     

    파이썬에서 클래스를 정의할 때 나오는 self도 클래스로 만드는 인스턴스를 가리키는데, 이는 자바스크립트 클래스에서 this가 인스턴스를 가리키는 것과 같다. 그래서 파이썬을 배운 다음에 자바스크립트를 배우면 좀 쉽게 배울 수 있다(파이썬과 자바스크립트는 이름도 같고 사용법도 같은 배열 메서드가 있는 정도로 유사한 점이 많아서 파이썬을 배우고 자바스크립트를 배우면 좀 더 쉽게 자바스크립트에 입문할 수 있을 것이다).  

     

    그러면 백문이 불여일견, 자바스크립트 클래스의 this, 파이썬 클래스의 self를 비교해보자

    먼저 파이썬이다.

    class Cat:
        # 클래스 속성, 클래스 변수
        species = 'firstcat'
    
        # 생성자/초기화 인스턴스 속성
        # 자바스크립트의 constructor 함수와 같음
        # 자바스크립트 클래스의 this와 python class의 self는 같음
        def __init__(self, name, age):
            self.name = name
            self.age = age
          
        # 인스턴스 메서드 정의   
        def info(self):
            return '{} is {} years old'.format(self.name, self.age)
      
        def speak(self, sound):
            return '{} says {}'.format(self.name, sound)
    
    # instance 생성
    goldenhand = Cat('goldenhand', 4)
    
    # 메서드 호출
    print(goldenhand.info())  # goldenhand is 4 years old
    print(goldenhand.speak('냐아아앙'))  # goldenhand says 냐아아앙

    여기서 def __init__(self, ...) 함수는 자바스크립트의 contructor 함수와 같은 역할을 하는데, 인스턴스 객체의 내부 프로퍼티를 설정해주는 역할을 한다. 다만 자바스크립트 클래스와는 달리 메서드는 정의하지 않는것으로 보인다. 파이썬 클래스에서는 def __init__(self, ...) 함수 이후에 선언하는 함수들이 인스턴스의 메서드가 된다. 

    그리고 인스턴스의 메서드가 될 함수들에는 파라메터로 self가 꼭 들어가는데, 인스턴스가 생성되고 나서 메서드를 호출할 때는 인자로 self가 들어가지 않고 생략된다. 왜냐면 self는 인스턴스를 가리키므로 굳이 등장할 필요가 없기 때문이다(= 예를들어, instacnename.method() 이렇게 메서드를 호출하는 경우, 인스턴스는 이미 .(점) 앞에 instancename으로 나와 있다).

     

    다음으로 자바스크립트의 React의 클래스형 컴포넌트의 예다.

    import React from 'react';
    import BucketList from './BucketList';
    import styled from "styled-components";
    
    // class component 
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          list: ['영화관 가기', '매일 책읽기', '수영 배우기']
        };
        // ref 선언 - DOM 지정하기
        this.text = React.createRef();
      };
    
      addBucketList = () => {
        let list = this.state.list; // state 데이터는 변수에 할당 가능
        const newItem = this.text.current.value
    
        this.setState({list: [...list, newItem]});
      };
    
      // 콘솔로 존재여부 확인
      componentDidMount() {
        console.log(this.text);
        console.log(this.text.current);
      };
    
      render() {
        return (
          <div className='App'>
            <Container>
              <Title>내 버킷리스트</Title>
              <Line/>
              <BucketList list={this.state.list} />
            </Container>
    
            <InputPart>
              <input type='text' ref={this.text}></input>
              <button onClick={this.addBucketList}>추가하기</button>
            </InputPart>
          
          </div>
        );
      };
    };

    여기서는 보다시피 변수와 메서드가 호출되는 경우 어김없이 this가 나온다. class 선언 이후 처음으로 나오는 constructor 함수에는 파이썬 클래스의 def __init__() 함수처럼 인스턴스의 변수를 정의한다. 파이썬 클래스와 비교해보면 알겠지만, 파이썬에서는 self.name = name 이런식이고, 자바스크립트에서는 this.state = {list: [.....]} 이다. 그래서 파이썬과 자바스크립트에서 self와 this는 모두 인스턴스를 가리키는 용도이고, 문법도 동일함을 알 수 있다. 만약 this 없이 쓴다면(예를 들어 return문에 있는 this.state.list를 대신 state.list로 쓴다면) 오류가 나온다. 

     

    그리고 자바스크립트에서도 메서드는 따로 지정하고, 나중에 this와 붙여서 쓰는 것을 알 수 있다(예를들어서, addBucketList를 정의하고 이후 return문에서 this.addBucketList로 호출해서 쓴다). 파이썬과는 달리 파라메터로 this를 쓰지 않고 대신 메서드 내부에서 this를 붙여서 쓴다.

     

    사족

    코어 자바스크립트(정재남 지음) 책의 p65에 '함수와 객체의 구분이 느슨한 자바스크립트에서 this는 실질적으로 이들을 구분하는 거의 유일한 기능입니다'라고 설명하고 있다. 그래서인지 자바스크립트의 함수에서는 this가 나오지 않는다. 예를들어 React 함수형 컴포넌트 파일에는 this가 나오지 않는다.

    import React, { useState, useRef } from 'react';
    
    const InputSample = () => {
      const [inputs, setInputs] = useState({
        name: '',
        nickname: '',
      });
      const { name, nickname } = inputs;
      const nameInput = useRef();
    
      const onChange = (e) => {
        const { value, name } = e.target;
        setInputs({
          ...inputs,
          [name]: value,
        });
      };
    
      const onReset = () => {
        setInputs({
          name: '',
          nickname: '',
        });
        nameInput.current.focus();
      };
    
      return (
        <>
          <input
            name="name"
            placeholder='이름'
            onChange={onChange}
            value={name}
            ref={nameInput}
          />
          <input
            name="nickname"
            placeholder='닉네임'
            onChange={onChange}
            value={nickname}
          />
          <button onClick={onReset}>초기화</button>
          <div>
            <b>값: </b>
            {name} ({nickname})
          </div>
        </>
      )
    }
    
    export default InputSample;

    이것을 클래스형 컴포넌트로 변환한다면 this가 변수와 메서드를 사용하는 부분마다 나와줘야 할 것이다. 다만, 함수형 컴포넌트와 달리 클래스형 컴포넌트는 contructor함수로 state를 이렇게 this.state으로 선언해서 사용하는 것이 가능하다. 왜냐면 클래스형 컴포넌트는 어차피 컴포넌트를 인스턴스로 만들어 사용하는 것이고, 그 인스턴스만의 state를 선언해서 쓸 수 있기 때문이다. 함수에서 this는 전역객체를 바라보는 것이지 함수자체의 정보가 아니다(코어 자바스크립트 p72 참조). 그래서 함수형 컴포넌트는 state를 this.state로 선언해서 쓸 수 없다. 그래서 리액트에서는 state를 사용해야 하는 경우에는 함수형 대신 클래스형 컴포넌트로 state를 사용했다. 함수형 컴포넌트는 hooks가 나온 버전 이후에야 state를 선언해서 사용할 수 있게 되었다.

    댓글

금손이 프론트엔드 개발자가 되고자 오늘도 존버중