JavaScript에서 불변성이 중요한 이유는 무엇입니까?


205

현재 React JSReact Native 프레임 워크 에서 작업하고 있습니다. 절반의 길에서 나는 Facebook의 Flux 및 Redux 구현에 대해 읽을 때 Immutability 또는 Immutable-JS 라이브러리를 발견했습니다 .

문제는 왜 불변성이 그렇게 중요한가? 객체를 돌연변이시키는 데 무엇이 잘못 되었습니까? 간단하지 않습니까?

예를 들어, 시작 화면이 뉴스 헤드 라인의 목록보기 인 간단한 뉴스 리더 앱을 고려하십시오 .

처음 에 값을 가진 객체 배열을 설정하면 조작 할 수 없습니다. 그것이 불변 원리가 말한 것입니다. (잘못되면 바로 잡으십시오.) 그러나 업데이트해야 할 새 뉴스 개체가 있으면 어떻게합니까? 일반적인 경우에는 객체를 배열에 추가했을 수 있습니다. 이 경우 어떻게 달성합니까? 상점을 삭제하고 다시 만드시겠습니까? 배열에 객체를 추가하는 것이 비용이 덜 드는 작업입니까?



2
불변의 데이터 구조와 순수한 기능은 참조 투명성을 유도하여 프로그램의 동작에 대해 추론하기가 훨씬 쉽습니다. 기능적인 데이터 구조를 사용할 때 무료로 역 추적 할 수 있습니다.
WorBlux

Redux 관점 @bozzmob를 제공했습니다.
prosti

1
JS가 그것과 관련이 있다고 생각하는 대신 기능 패러다임의 개념으로서 일반적으로 불임에 대해 배우는 것이 유용 할 수 있습니다. 반응은 기능적 프로그래밍 팬들에 의해 작성되었습니다. 당신은 그들이 이해하기 위해 알고있는 것을 알아야합니다.
Gherman

답변:


196

나는 최근에 같은 주제를 연구하고 있습니다. 귀하의 질문에 답변하고 지금까지 배운 내용을 공유하기 위해 최선을 다하겠습니다.

문제는 왜 불변성이 그렇게 중요한가? 객체를 돌연변이시키는 데 무엇이 잘못 되었습니까? 간단하지 않습니까?

기본적으로 불변성은 예측 가능성, 성능을 간접적으로 증가시키고 돌연변이 추적을 허용한다는 사실에 달려 있습니다.

예측 가능성

돌연변이는 변화를 숨겨 (예기치 않은) 부작용을 일으켜 불쾌한 버그를 일으킬 수 있습니다. 불변성을 적용하면 응용 프로그램 아키텍처와 정신 모델을 단순하게 유지할 수 있으므로 응용 프로그램에 대한 추론이 쉬워집니다.

공연

불변 객체에 값을 추가한다는 것은 기존 값을 복사해야하는 곳에 새로운 인스턴스를 생성해야하고 메모리를 소모하는 새로운 객체에 새로운 값을 추가해야한다는 것을 의미하지만 불변 객체는 메모리를 줄이기 위해 구조적 공유를 사용할 수 있습니다 간접비.

모든 업데이트는 새로운 값을 반환하지만 내부 구조는 공유되어 메모리 사용량 (및 GC 스 래싱)을 크게 줄입니다. 즉, 1000 개의 요소가있는 벡터에 추가하면 실제로 1001 개의 요소로 새 벡터를 만들지 않습니다. 내부적으로 몇 개의 작은 개체 만 할당되어있을 가능성이 큽니다.

이에 대한 자세한 내용은 여기를 참조 하십시오 .

돌연변이 추적

메모리 사용 감소 외에도 불변성을 통해 참조 및 값 평등을 사용하여 응용 프로그램을 최적화 할 수 있습니다. 이렇게하면 변경된 것이 있는지 쉽게 알 수 있습니다. 예를 들어 반응 구성 요소의 상태 변경. shouldComponentUpdate상태 객체를 비교하고 불필요한 렌더링을 방지하여 상태가 동일한 지 확인 하는 데 사용할 수 있습니다 . 이에 대한 자세한 내용은 여기를 참조 하십시오 .

추가 자료 :

처음에 값을 가진 객체 배열을 설정하면. 조작 할 수 없습니다. 그것이 불변성 원리가 말하는 것입니다. (내가 틀렸다면 정정하십시오). 그러나 업데이트해야 할 새 뉴스 개체가 있으면 어떻게합니까? 일반적인 경우에는 객체를 배열에 추가했을 수 있습니다. 이 경우 어떻게 달성합니까? 상점을 삭제하고 다시 만드시겠습니까? 배열에 객체를 추가하는 것이 비용이 덜 드는 작업입니까?

네 맞습니다. 애플리케이션에서 이것을 구현하는 방법에 대해 혼란 스러우면 핵심 개념에 익숙해지기 위해 redux 가이를 수행 하는 방법을 살펴 보는 것이 좋습니다 .

나는 불변성을 포용하기 때문에 Redux를 예로 사용하고 싶습니다. 여기에는 하나의 불변 상태 트리 (이라고 함 store)가 있으며, 모든 상태 변경은 명시 적으로 조치와 함께 이전 상태를 수락하고 애플리케이션의 다음 상태를 반환하는 감속기에 의해 처리되는 조치를 디스패치함으로써 명시 적으로 나타납니다. . 핵심 원칙에 대한 자세한 내용은 여기를 참조하십시오 .

egghead.io 에는 redux의 저자 인 Dan Abramov가 이러한 원칙을 다음과 같이 설명 하는 훌륭한 redux 과정 이 있습니다 (시나리오에 더 적합하도록 코드를 약간 수정했습니다).

import React from 'react';
import ReactDOM from 'react-dom';

// Reducer.
const news = (state=[], action) => {
  switch(action.type) {
    case 'ADD_NEWS_ITEM': {
      return [ ...state, action.newsItem ];
    }
    default: {
        return state;
    }
  }
};

// Store.
const createStore = (reducer) => {
  let state;
  let listeners = [];

  const subscribe = (listener) => {
    listeners.push(listener);

    return () => {
      listeners = listeners.filter(cb => cb !== listener);
    };
  };

  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach( cb => cb() );
  };

  dispatch({});

  return { subscribe, getState, dispatch };
};

// Initialize store with reducer.
const store = createStore(news);

// Component.
const News = React.createClass({
  onAddNewsItem() {
    const { newsTitle } = this.refs;

    store.dispatch({
      type: 'ADD_NEWS_ITEM',
      newsItem: { title: newsTitle.value }
    });
  },

  render() {
    const { news } = this.props;

    return (
      <div>
        <input ref="newsTitle" />
        <button onClick={ this.onAddNewsItem }>add</button>
        <ul>
          { news.map( ({ title }) => <li>{ title }</li>) }
        </ul>
      </div>
    );
  }
});

// Handler that will execute when the store dispatches.
const render = () => {
  ReactDOM.render(
    <News news={ store.getState() } />,
    document.getElementById('news')
  );
};

// Entry point.
store.subscribe(render);
render();

또한,이 비디오는 다음에 대해 불변성을 달성하는 방법을 자세히 보여줍니다.


1
의견을 보내 주셔서 감사합니다! 내 의도는 개념을 설명하고 Objects가 변경되지 않았으며 반드시 완전히 구현하는 방법을 보여주지는 않는다는 것을 명시 적으로 보여주기위한 것이었다. 그러나 내 예제는 약간 혼란 스러울 수 있으므로 조금 업데이트하겠습니다.
danillouz

2
@bozzmob 천만에요! 정확하지 않습니다. 감속기에서 불변성을 직접 적용해야합니다. 즉, 동영상에서 설명 된 전략을 따르거나 immutablejs와 같은 라이브러리를 사용할 수 있습니다. 자세한 정보는 여기여기를 참조하십시오 .
danillouz

1
@naomik ES6 const는 불변성에 관한 것이 아닙니다. Mathias Bynens는 그것에 관한 훌륭한 블로그 기사를 썼습니다 .
Lea Rosema

1
링크를 공유해 주셔서 감사합니다. 나는 그것이 중요한 구별에 동의합니다. ^ _ ^
감사합니다

4
"돌연변이로 인해 변경이 숨겨져 예기치 않은 부작용이 발생하여 불쾌한 버그가 발생할 수 있습니다. 불변성을 적용하면 응용 프로그램 아키텍처와 정신 모델을 단순하게 유지하여 응용 프로그램에 대한 추론을 쉽게 할 수 있습니다." 이것은 JavaScript와 관련하여 전혀 사실이 아니기 때문입니다.
Pavle Lekic

142

불변성의 모순 된 견해

TL / DR : 불변성은 JavaScript의 필요성보다 패션 트렌드입니다. React를 사용하는 경우 상태 관리에서 혼란스러운 디자인 선택 에 대한 깔끔한 해결 방법을 제공합니다 . 그러나 대부분의 다른 상황에서는 실제 클라이언트 요구를 충족시키는 것보다 이력서 를 채우는 데 더 많은 역할을하면서 도입 된 복잡성에 비해 충분한 가치를 추가하지 않습니다 .

긴 대답 : 아래를 읽으십시오.

왜 자바 스크립트에서 불변성이 그렇게 중요하거나 필요한가?

글쎄, 당신이 물어봐서 기뻐요!

얼마 전에 Dan Abramov 라는 재능있는 사람 은 순수한 기능과 불변성을 사용하는 Redux 라는 자바 스크립트 상태 관리 라이브러리를 작성했습니다 . 또한 아이디어를 이해하고 판매하기 쉽게 만드는 멋진 동영상 을 만들었습니다.

타이밍이 완벽했습니다. Angular 의 참신함 은 희미 해졌고 JavaScript 세계는 적절한 수준의 시원함을 가진 최신 것을 고칠 준비가되었습니다.이 라이브러리는 혁신적 일뿐 만 아니라 다른 Silicon Valley 강국에 의해 통제되고 있는 React 와 완벽하게 슬롯되었습니다 .

슬프게도 패션은 JavaScript 세계에서 지배합니다. 이제 아브라모프는 반신으로 찬사를 받고 있으며, 필사자 들만이 불변성Dao에 자신을 맡겨야합니다.

객체를 돌연변이시키는 데 무엇이 잘못 되었습니까?

아무것도!

실제로 프로그래머는 변형 할 객체가있는 한 객체를 변형했습니다. 다시 말해 50 년 이상의 응용 프로그램 개발입니다.

왜 문제가 복잡합니까? 당신이 물건 cat을 가지고 죽을 때, 당신은 정말로 cat변화를 추적하기 위해 1 초가 필요 합니까? 대부분의 사람들은 그냥 말하고 cat.isDead = true끝낼 것입니다.

(개체를 돌연변이시키는) 것이 일을 단순하게하지 않습니까?

예! .. 물론 그렇습니다!

특히 JavaScript에서는 실제로 데이터베이스와 같이 다른 곳에서 유지 관리되는 일부 상태의 뷰를 렌더링하는 데 가장 유용합니다.

업데이트해야 할 새 뉴스 개체가 있으면 어떻게합니까? ...이 경우 어떻게 달성합니까? 상점을 삭제하고 다시 만드시겠습니까? 배열에 객체를 추가하는 것이 비용이 덜 드는 작업입니까?

글쎄, 전통적인 접근 방식으로 News객체를 업데이트하여 객체의 메모리 내 표현이 변경되고 사용자에게 표시되는보기가 변경 될 수 있습니다 ...

아니면 ...

섹시한 FP / Immutability 접근 방식을 시도하고 모든 변경 사항을 추적하는 배열에News 객체 의 변경 사항을 추가하여 배열 을 반복하고 올바른 상태 표현이 무엇인지 파악할 수 있습니다 (푸우!).

나는 여기에 무엇이 있는지 배우려고합니다. 나를 계몽하십시오 :)

패션이왔다 갔다. 고양이를 피부에 바르는 방법에는 여러 가지가 있습니다.

끊임없이 변화하는 프로그래밍 패러다임의 혼란을 겪게되어 유감입니다. 그러나 안녕하세요, 클럽에 오신 것을 환영합니다 !!

이제 불변성과 관련하여 기억해야 할 몇 가지 중요한 사항이 있으며, 순진한 사람 만이 할 수있는 열렬한 강도로 이러한 것들을 얻을 수 있습니다.

1) 다중 스레드 환경에서 경쟁 조건 을 피하기 위해 불변성은 훌륭 합니다 .

다중 스레드 환경 (C ++, Java 및 C #과 같은)은 둘 이상의 스레드가 객체를 변경하려고 할 때 객체를 잠그는 관행에 유죄입니다. 이것은 성능에는 좋지 않지만 데이터 손상의 대안보다 낫습니다. 그러나 모든 것을 불변으로 만드는 것만 큼 좋지는 않습니다 (주님 Haskell을 칭찬하십시오!).

하지만 그렇습니다! JavaScript에서는 항상 단일 스레드에서 작동합니다 . 웹 워커조차 (각각 별도의 컨텍스트 내에서 실행 ). 따라서 실행 컨텍스트 내 에서 스레드 관련 경쟁 조건을 가질 수 없으므로 (모든 사랑스러운 전역 변수 및 클로저) 불변성을 선호하는 요점은 창 밖으로 나옵니다.

(가, 그런 말을 데 입니다 메인 스레드에서 개체 하구에 대한 어떤 기대가없는 것이 오 웹 노동자, 순수한 기능을 사용하는 장점은.)

2) 불변성은 앱 상태에서 경쟁 조건을 피할 수 있습니다.

그리고 여기에 문제의 진정한 요점이 있습니다. 대부분의 (React) 개발자는 불변성과 FP가 어떻게 든 마법을 사용하여 응용 프로그램의 상태를 예측할 수 있다고 말할 것입니다.

물론 이것이 데이터베이스의 경쟁 조건을 피하고 모든 브라우저의 모든 사용자 를 조정 해야하며 WebSockets 와 같은 백엔드 푸시 기술이 필요 하다는 것을 의미하지는 않습니다 ( 아래에 더 자세히 설명) 앱을 실행하는 모든 사람에게 변경 사항을 브로드 캐스트합니다.

또한 JavaScript에서 응용 프로그램 상태를 예측할 수있게하기 위해 불변성이 필요한 일부 고유 한 문제가 있다는 의미는 아닙니다. React 이전에 프런트 엔드 응용 프로그램을 코딩 한 개발자라면이 사실을 알 수 있습니다.

이 혼란스러운 주장은 단순히 React사용 하면 응용 프로그램 상태가 경쟁 조건에 더 취약 해지지 만 불변성으로 인해 고통을 덜 수 있음을 의미합니다. 왜? React는 특별하기 때문입니다. 일관성있는 상태 관리가 2 위를 차지하는 고도로 최적화 된 렌더링 라이브러리설계 되었으므로 구성 요소 상태는 제어 할 수없는 비동기 이벤트 체인 (일명 "일방향 데이터 바인딩")을 통해 관리됩니다. 상태를 직접 변경하지 않는 것을 기억 하고 당신에게 의지 하십시오 ...

이러한 맥락에서, 불변성의 필요성이 JavaScript와 거의 관련이 없으며 React의 경쟁 조건과 많은 관련이 있음을 쉽게 알 수 있습니다. 응용 프로그램에 많은 상호 의존적 변경이 있고 무엇을 알아낼 수있는 쉬운 방법이 없다면 귀하의 주가 현재 있고 혼란 스러울 것이므로 불변성을 사용하여 모든 역사적 변화를 추적하는 것이 완벽 합니다.

3) 경쟁 조건은 엄청나게 나쁘다.

글쎄, 당신이 React를 사용한다면 그럴 수 있습니다. 그러나 다른 프레임 워크를 선택하면 드물게 나타납니다.

게다가, 당신은 일반적으로 다루어야 할 훨씬 더 큰 문제 가 있습니다 ... 의존성 지옥 같은 문제. 부풀린 코드베이스처럼. CSS 가로 드되지 않는 것처럼. 느린 빌드 프로세스 또는 반복을 거의 불가능하게하는 모 놀리 식 백엔드에 빠지는 것처럼. 미숙 한 개발자들과 마찬가지로 무슨 일이 일어나고 있는지 이해하지 못하고 엉망으로 만듭니다.

당신은 알고있다. 현실. 근데 누가 신경써?

4) 불변성은 참조 유형 을 사용하여 모든 상태 변경 추적의 성능 영향을 줄입니다.

진지하게, 상태가 바뀔 때마다 물건을 복사하려는 경우 현명한 지 확인하는 것이 좋습니다.

5) 불변성은 물건을 취소 할 수 있습니다 .

왜냐하면 .. 이것은 프로젝트 관리자가 요구하는 최고의 기능입니다. 맞습니까?

6) 불변 상태는 WebSocket과 함께 멋진 잠재력을 가지고 있습니다.

마지막으로, 상태 델타의 누적은 WebSocket과 함께 매우 매력적인 사례를 만들어 불변의 이벤트 흐름으로 상태를 쉽게 사용할 수 있습니다 ...

페니가이 개념을 포기하면 ( 최신 견해를 나타내는 조잡한 기록이 아니라 사건의 흐름 이된다) 불변의 세계는 거주하기에 마법의 장소가된다. 시간초월 하는 이벤트 기반의 경이와 가능성 의 땅 . 그리고 완료되면 바로이 확실히 실시간 EASI 애플 리케이션을 만들 수 있습니다 달성하기를, 당신은 단지 그들이 할 수 있도록 관심있는 모든 사람에게 사건의 흐름 방송을 자신의 표현을 구축 본의와 공동 흐름에 자신의 변화를 다시 쓰기.

그러나 어떤 시점에서 당신은 깨어나 모든 경이와 마법이 무료로 오지 않는다는 것을 깨달았습니다. 열성적인 동료 들과는 달리, 이해 관계자 (즉, 돈을 지불하는 사람들)는 철학이나 패션, 그리고 그들이 팔 수있는 제품을 만들기 위해 지불하는 돈에 대해서는 거의 관심이 없습니다. 결론은 불변의 코드를 작성하기가 어렵고 깨지기 쉬우 며, 지원할 백엔드가 없다면 불변의 프론트 엔드를 갖는 것이 거의 없다는 것입니다. WebSockets와 같은 푸시 기술을 통해 이벤트를 게시하고 소비해야한다는 사실을 이해 관계자에게 마침내 설득 하면 프로덕션 환경에서 확장 해야 할 고통이 무엇인지 알게됩니다 .


이제 몇 가지 조언을 받아들이도록 선택해야합니다.

FP / Immutability를 사용하여 JavaScript를 작성하는 선택은 또한 응용 프로그램 코드 기반을 더 크고 복잡하고 관리하기 어렵게 만드는 선택입니다. 나는 당신이하고있는 일을 알지 못한다면 Redux 감속기 로이 접근법을 제한 할 것을 강력히 주장 할 것입니다 ... 그리고 계속 진행하고 불변성을 사용한다면 불변 상태를 전체 응용 프로그램 스택에 적용 하십시오. 그렇지 않으면 실제 가치를 놓치므로 클라이언트 측.

지금, 당신이 당신의 일에서 선택을 할 수있을만큼 운이 좋으면, 당신의 지혜를 시도하고 사용하고 지불하는 사람이 옳은 일을하십시오 . 당신의 경험, 장, 또는 당신의 주위에 무슨 일이 있었는지에 근거 할 수 있습니다. Resume Driven Development 또는 Hype Driven Development 접근법을 시도 할 수 있습니다 . 그들은 더 당신의 일이 될 수 있습니다.

즉, 일이 불변성에 대해 말했다 수는 있다는 것입니다 것입니다 다음 열풍 주위에 제공 적어도 때까지하는 당신이 이동 드리겠습니다 포인트, 동료와 당신이 유행합니다.


이제이 자기 치료 세션이 끝난 후이 기사를 내 블로그의 기사 => JavaScript의 불변성 : 대립적 견해로 추가했습니다 . 가슴에서 내리고 싶은 강한 감정이 있다면 자유롭게 답장하십시오.).


10
안녕하세요 스티븐 immutable.js와 redux를 고려할 때 이러한 모든 의심이있었습니다. 그러나 당신의 대답은 놀랍습니다! 의심의 여지가있는 모든 사항을 해결해 주셔서 감사합니다. 불변의 물체를 몇 달 동안 작업 한 후에도 훨씬 더 명확하고 더 좋습니다.
bozzmob

5
Flux / Redux와 함께 2 년 동안 React를 사용해 왔는데 더 많은 의견에 동의 할 수 없었습니다.
Pavle Lekic

6
불변성에 대한 견해가 팀 및 코드베이스 크기와 다소 밀접하게 관련되어 있다는 것이 의심스럽고, 주요 지지자가 실리콘 밸리 거인이라는 우연의 일치라고 생각하지 않습니다. 나는 불변성은 goto를 사용하지 않는 것이 유용한 훈련이라는 유용한 훈련입니다. 또는 단위 테스트. 또는 TDD. 또는 정적 유형 분석. 그렇다고해서 항상 (일부는하더라도) 항상 그렇게하는 것은 아닙니다. 나는 유용성이 과대 광고에 직교한다고 말하고 싶습니다. 유용하고 불필요한, 섹시하고 지루한 매트릭스에는 각각의 예가 많이 있습니다. "hyped" !== "bad"
Jared Smith

4
안녕 @ ftor, 좋은 지적, 다른 방향으로 너무 멀리 물건을 복용. 그러나 '자바 스크립트의 불변성'기사와 논쟁이 풍부하기 때문에 균형을 맞출 필요가 있다고 생각했습니다. 따라서 초보자는 어느 쪽이든 판단 할 수 있도록 반대 관점을 가지고 있습니다.
Steven de Salas

4
유익하고 훌륭하게 제목이 지정됩니다. 이 답변을 찾을 때까지 나는 비슷한 견해를 가진 유일한 사람이라고 생각했습니다. 나는 불변성의 가치를 알고 있지만, 나를 괴롭히는 것은 그것이 다른 모든 기술 억압 교리 가되었다는 것입니다 (예를 들어 KnockoutJS에서 구현 된 입력 형식에 매우 유용한 2-way 바인딩의 손상).
Tyblitz

53

문제는 왜 불변성이 그렇게 중요한가? 객체를 돌연변이시키는 데 무엇이 잘못 되었습니까? 간단하지 않습니까?

사실, 그 반대는 사실이다 : 가변성은 최소한 장기적으로는 더 복잡하게 만든다. 예. 원하는 곳에서 원하는대로 변경할 수 있기 때문에 초기 코딩이 더 쉬워 지지만 프로그램이 커질수록 문제가됩니다. 값이 변경되면 무엇이 바뀌 었습니까?

모든 것을 불변으로 만들면 더 이상 놀랍게도 데이터를 변경할 수 없습니다. 함수에 값을 전달하면 해당 함수에서 값을 변경할 수 없다는 것을 알고 있습니다.

간단히 말해 : 불변의 값을 사용하면 코드에 대해 추론하기가 매우 쉽습니다. 모든 사람이 고유 한 * 데이터 사본을 얻으므로 데이터를 혼동하지 않고 코드의 다른 부분을 깰 수 없습니다. 이것이 멀티 스레드 환경에서 얼마나 쉽게 작업 할 수 있는지 상상해보십시오!

참고 1 : 수행중인 작업에 따라 불변성에 대한 잠재적 성능 비용이 있지만 Immutable.js와 같은 것들이 최대한 최적화됩니다.

참고 2 : 확실하지 않은 상황에서 Immutable.js와 ES6 const은 매우 다른 의미를 갖습니다 .

일반적인 경우에는 객체를 배열에 추가했을 수 있습니다. 이 경우 어떻게 달성합니까? 상점을 삭제하고 다시 만드시겠습니까? 배열에 객체를 추가하는 것이 비용이 덜 드는 작업입니까? 추신 : 예제가 불변성을 설명하는 올바른 방법이 아닌 경우 올바른 실제 예가 무엇인지 알려주십시오.

예, 귀하의 뉴스 사례는 완벽하고 귀하의 추론은 옳습니다. 기존 목록을 수정할 수 없으므로 새로운 목록을 작성해야합니다.

var originalItems = Immutable.List.of(1, 2, 3);
var newItems = originalItems.push(4, 5, 6);

1
나는이 답변에 동의하지 않지만 질문의 "실제적인 예에서 배우고 싶습니다"부분을 다루지 않습니다. 여러 영역에서 사용되는 뉴스 헤더 목록에 대한 단일 참조가 좋은 것이라고 주장 할 수 있습니다. "목록을 한 번만 업데이트하면 뉴스 목록을 참조하는 모든 내용이 무료로 업데이트됩니다."더 나은 답변은 그가 제시 한 것과 같은 일반적인 문제를 해결하고 불변성을 사용하는 유용한 대안을 제시한다고 생각합니다.
감사합니다.

1
답변이 도움이되었다 니 다행입니다. 새로운 질문과 관련하여 : 시스템을 추측하려고 시도하지 마십시오. js는 가능한 한 이전 구조를 재사용하려고 시도합니다. Immutable.js가 메모리에 대해 걱정하게하고 더 나은 결과가 나올 가능성이 높아집니다.
TwoStraws

6
Imagine how much easier this makes working in a multi-threaded environment!-> 다른 언어에서는 가능하지만 단일 스레드 JavaScript에서는 이점이 아닙니다.
Steven de Salas

1
@StevendeSalas는 JavaScript는 주로 비동기식이며 이벤트 중심이라는 점에 주목합니다. 경쟁 조건에 전혀 영향을받지는 않습니다.
Jared Smith

1
@JaredSmith 여전히 내 요점이 남아 있습니다. FP 및 불변성은 다중 스레드 환경에서 데이터 손상 및 / 또는 리소스 잠금을 피하기위한 강력한 패러다임이지만 단일 스레드이기 때문에 JavaScript에서는 그렇지 않습니다. 내가 거룩한 지혜의 덩어리를 놓치지 않는 한, 여기서 가장 중요한 단점은 경쟁 조건을 피하기 위해 코드를 더 복잡하고 느리게 만들 준비가되어 있는지 여부입니다. 생각한다.
Steven de Salas

37

다른 답변은 괜찮지 만 실제 사용 사례에 대한 귀하의 질문을 해결하기 위해 (다른 답변에 대한 의견에서) 잠시 동안 실행중인 코드를 벗어나서 바로 코 아래에서 유비쿼터스 답변을 볼 수 있습니다 : git . 커밋을 푸시 할 때마다 리포지토리의 데이터 를 덮어 쓰면 어떻게됩니까?

이제 우리는 불변 컬렉션이 직면하는 문제 중 하나 인 메모리 팽창에 직면 해 있습니다. Git은 변경 할 때마다 단순히 파일의 새로운 사본을 만들지 않을만큼 똑똑 하여 diff를 추적합니다 .

나는 git의 내부 작업에 대해 많이 알지 못하지만 참조하는 라이브러리의 구조와 유사한 전략을 사용한다고 가정 할 수 있습니다 : 구조 공유. 라이브러리는 시도 또는 다른 트리를 사용하여 다른 노드 만 추적합니다.

이 전략은 또한 로그 시간으로 작동하는 잘 알려진 트리 연산 알고리즘이 있기 때문에 메모리 내 데이터 구조에 대해 합리적으로 수행 됩니다.

또 다른 유스 케이스 : webapp에서 실행 취소 버튼을 원한다고 가정하십시오. 데이터를 변경할 수없는 표현으로 구현하는 것은 비교적 사소한 일입니다. 그러나 돌연변이에 의존한다면 세계 상태를 캐싱하고 원자 업데이트를하는 것에 대해 걱정해야한다는 의미입니다.

요컨대, 런타임 성능과 학습 곡선의 불변성에 대한 대가가 있습니다. 그러나 숙련 된 프로그래머라면 디버깅 시간이 코드 작성 시간보다 몇 배나 크다는 것을 알게 될 것입니다. 또한 런타임 성능에 대한 약간의 히트는 사용자가 견뎌 낼 필요가없는 상태 관련 버그로 인해 더 중요합니다.


1
내가 말하는 훌륭한 예입니다. 불변성에 대한 나의 이해는 이제 더 분명합니다. 감사합니다. 실제로 구현 중 하나는 UNDO 버튼입니다. D 그리고 당신은 나에게 아주 간단하게 만들었습니다.
bozzmob

3
git에서 의미가있는 패턴이 모든 곳에서 똑같은 의미를 갖는 것은 아닙니다. git에서는 실제로 저장된 모든 기록을 관리하고 다른 분기를 병합 할 수 있기를 원합니다. 프론트 엔드에서는 대부분의 상태 기록에 신경 쓰지 않으며 이러한 모든 복잡성이 필요하지 않습니다.
Ski

2
@ 스키 그것은 기본값이 아니기 때문에 복잡합니다. 나는 일반적으로 프로젝트에서 mori 또는 immutable.js를 사용하지 않습니다. 나는 항상 제 3 자 뎁을 취하는 것을 주저합니다. 그러나 이것이 기본 (la clojurescript)이거나 적어도 옵트 인 기본 옵션이있는 경우 항상 사용합니다 .clojure에서 프로그램을 작성할 때 모든 것을 원자에 즉시 넣지 않기 때문입니다.
Jared Smith

조 암스트롱은 성능에 대해 걱정하지 말고 몇 년만 기다리면 무어의 법칙이이를 처리해 줄 것입니다.
ximo

1
@JaredSmith 당신이 옳습니다. 상황은 점점 작아지고 리소스가 제한적입니다. 그래도 JavaScript의 제한 요소인지 확실하지 않습니다. 성능을 향상시킬 수있는 새로운 방법을 계속 찾고 있습니다 (예 : Svelte). 그건 그렇고, 나는 당신의 다른 의견에 전적으로 동의합니다. 변경 불가능한 데이터 구조를 사용하는 데 따르는 복잡성 또는 어려움은 종종 개념에 대한 지원이 기본적으로 제공되지 않는 언어에 기인합니다. Clojure는 언어로 구워지기 때문에 불변성을 간단 하게 만듭니다 . 전 언어는 아이디어를 중심으로 설계되었습니다.
ximo

8

문제는 왜 불변성이 그렇게 중요한가? 객체를 돌연변이시키는 데 무엇이 잘못 되었습니까? 간단하지 않습니까?

가변성

기술적 인 관점에서 변경 가능성에있어 잘못된 것은 없습니다. 빠르며 메모리를 재사용하고 있습니다. 개발자는 처음부터 기억합니다. 가변성 사용에 문제가 있으며이 사용으로 인해 문제가 발생할 수 있습니다.

객체가 어떤 것과 공유되지 않으면, 예를 들어 함수의 범위 내에 존재하고 외부에 노출되지 않으면 불변성의 이점을보기가 어렵습니다. 실제로이 경우에는 불변 인 것이 이치에 맞지 않습니다. 불변의 의미는 무언가가 공유 될 때 시작됩니다.

돌연변이 성 두통

가변적 인 공유 구조는 많은 함정을 쉽게 만들 수 있습니다. 참조에 액세스 할 수있는 코드 부분의 변경 사항은이 참조를 볼 수있는 다른 부분에 영향을줍니다. 이러한 충격은 서로 다른 모듈을 인식하지 않아도 모든 부품을 연결합니다. 하나의 함수에서 돌연변이는 앱의 완전히 다른 부분에서 충돌을 일으킬 수 있습니다. 그러한 것은 나쁜 부작용입니다.

다음으로 돌연변이 문제는 종종 손상 상태입니다. 중간에 돌연변이 절차가 실패하고 일부 필드가 수정되고 일부는 수정되지 않은 경우 손상된 상태가 발생할 수 있습니다.

또한 돌연변이로 인해 변화를 추적하기가 어렵습니다. 간단한 참조 검사는 차이점을 보여주지 않고 어떤 심층 검사가 변경되었는지를 알고 있습니다. 또한 변경 사항을 모니터링하려면 관찰 가능한 일부 패턴을 도입해야합니다.

마지막으로, 돌연변이는 신뢰 부족의 원인입니다. 일부 구조가 변경 될 수있는 경우 값을 원하는지 확인하는 방법

const car = { brand: 'Ferrari' };
doSomething(car);
console.log(car); // { brand: 'Fiat' }

위의 예에서 볼 수 있듯이 변경 가능한 구조를 전달하면 항상 다른 구조를 가짐으로써 완료 할 수 있습니다. 함수 doSomething이 외부에서 제공된 속성을 변경합니다. 코드를 믿지 않아도 실제로 무엇을 가지고 있는지 알 수 없습니다. : 때문에 이러한 모든 문제가 발생하는 변경 가능한 구조는 메모리에 대한 포인터를 나타내는 있습니다.

불변성은 가치에 관한 것입니다

불변성은 동일한 객체, 구조에서 변경이 수행되지 않지만 변경이 새로운 것으로 표시됨을 의미합니다. 그리고 이것은 참조가 메모리 포인터뿐만 아니라 값을 나타 내기 때문입니다. 모든 변화는 새로운 가치를 창출하고 이전 가치를 건드리지 않습니다. 이러한 명확한 규칙은 신뢰와 코드 예측 성을 제공합니다. 함수는 돌연변이 대신 사용하기에 안전하며 자체 값을 가진 자체 버전을 처리합니다.

메모리 컨테이너 대신 값을 사용하면 모든 객체가 변경 불가능한 특정 값을 나타내므로 사용하는 것이 안전합니다.

불변의 구조는 값을 나타냅니다.

나는 중간 기사-https: //medium.com/@macsikora/the-state-of-immutability-169d2cd11310 에서 주제에 대해 더 많이 뛰어 들었습니다.


6

JavaScript에서 불변성이 중요한 이유는 무엇입니까?

불변성은 다른 상황에서 추적 될 수 있지만 가장 중요한 것은 응용 프로그램 상태 및 응용 프로그램 UI에 대해 추적하는 것입니다.

JavaScript Redux 패턴을 매우 트렌디하고 현대적인 접근 방식으로 생각할 것입니다.

UI의 경우 예측 가능 해야합니다 . 이 경우 예측 가능합니다 UI = f(application state).

JavaScript에서 애플리케이션은 reducer 함수를 사용하여 구현 된 조치를 통해 상태를 변경합니다 .

감속기 함수는 단순히 동작과 이전 상태를 가져 와서 이전 상태를 그대로 유지하면서 새 상태를 반환합니다.

new state  = r(current state, action)

여기에 이미지 설명을 입력하십시오

이점은 모든 상태 객체가 저장되므로 상태를 시간 이동하고 이후 모든 상태에서 앱을 렌더링 할 수 있다는 것입니다. UI = f(state)

따라서 쉽게 취소 / 재실행 할 수 있습니다.


이러한 모든 상태를 만드는 것은 여전히 ​​메모리 효율적 일 수 있으며 Git과의 유추는 훌륭하며 Linux OS에서도 비슷한 링크가 있습니다 (Iode 기반).


5

Javascript에서 불변성의 또 다른 이점은 일반적으로 디자인에 실질적인 이점이있는 Temporal Coupling을 줄인다는 것입니다. 두 가지 방법으로 객체의 인터페이스를 고려하십시오.

class Foo {

      baz() {
          // .... 
      }

      bar() {
          // ....
      }

}

const f = new Foo();

baz()호출이 bar()올바르게 작동 하려면 개체를 유효한 상태로 가져 오려면 호출 이 필요할 수 있습니다. 그러나 이것을 어떻게 알 수 있습니까?

f.baz();
f.bar(); // this is ok

f.bar();
f.baz(); // this blows up

그것을 알아 내려면 공개 인터페이스를 검사하는 것이 즉시 명백하지 않기 때문에 클래스 내부를 면밀히 조사해야합니다. 이 문제는 많은 변경 가능한 상태와 클래스가있는 큰 코드베이스에서 폭발 할 수 있습니다.

Foo불변 인 경우 는 더 이상 문제가되지 않습니다. 클래스의 내부 상태를 변경할 수 없으므로 호출 baz하거나 bar순서에 관계없이 가정하는 것이 안전합니다 .


4

옛날 옛적에 스레드 간 데이터 동기화에 문제가있었습니다. 이 문제는 큰 고통이었고 10 가지 이상의 해결책이있었습니다. 어떤 사람들은 그것을 근본적으로 해결하려고했습니다. 함수형 프로그래밍이 탄생 한 곳입니다. 그것은 마르크스주의와 같습니다. Dan Abramov가 단일 아이디어이기 때문에이 아이디어를 JS에 어떻게 판매했는지 이해할 수 없었습니다. 그는 천재입니다.

작은 예를들 수 있습니다. __attribute__((pure))gcc 에는 속성이 있습니다 . 컴파일러는 특별히 명확하지 않은 경우 함수가 순수한지 여부를 해결하려고 시도합니다. 상태가 변경 가능하더라도 함수는 순수 할 수 있습니다. 불변성은 100 가지 이상의 방법 중 하나 일뿐입니다. 실제로 함수의 95 %가 순수합니다.

실제로 심각한 이유가 없다면 불변성과 같은 제한을 사용해서는 안됩니다. 일부 상태를 "실행 취소"하려면 트랜잭션을 작성할 수 있습니다. 통신을 단순화하려면 변경 불가능한 데이터로 이벤트를 보낼 수 있습니다. 너하기에 달렸다.

나는이 메시지를 마르크스주의 포스트 공화국에서 쓰고있다. 나는 어떤 아이디어의 급진 화가 잘못된 길임을 확신합니다.


세 번째 단락은 매우 의미가 있습니다. 고마워 '일부 상태를'실행 취소 '하려면 거래를 만들 수 있습니다'!!
bozzmob

그런데 맑스주의와의 비교는 OOP에서도 가능하다. 자바를 기억하십니까? JavaScript에서 Java의 홀수 비트? 과대 광고는 결코 좋지 않으며 급 진화와 분극을 일으 킵니다. 역사적으로 OOP는 Facebook의 Redux 광고보다 훨씬 더 많은 광고를 보냈습니다. 그들은 확실히 최선을 다했지만.
ximo

4

다른 테이크 ...

내 다른 대답은 매우 실용적인 관점에서 질문을 다루며 여전히 좋아합니다. 나는 이것이 희망적으로 질문에 대답하지만 지루한 기존의 대답에는 맞지 않는 지루한 철학적 인 파문이기 때문에 이것을 하나의 부록이 아닌 다른 대답으로 추가하기로 결정했습니다.

TL; DR

작은 프로젝트에서도 불변성은 유용 할 수 있지만 그것이 존재하기 때문에 당신을위한 것이라고 가정하지 마십시오.

훨씬 더 긴 답변

참고 : 이 답변의 목적을 위해 나는 '수양'이라는 단어를 사용하여 일부 이익에 대한 자기 부인을 의미합니다.

이것은 "질문을 사용해야합니까? 왜 JavaScript에서 유형이 중요한가?"라는 다른 질문과 형태가 비슷합니다. 비슷한 대답이 있습니다. 다음 시나리오를 고려하십시오.

귀하는 약 5000 줄의 JavaScript / CSS / HTML 코드베이스를 단독으로 작성하고 유지 관리합니다. 반 기술 상사는 새로운 핫 타입으로 Typescript에 대한 내용을 읽은 후 우리가 그것을 옮기고 싶지만 결정을 내릴 것을 제안합니다. 그래서 당신은 그것에 대해 읽고, 놀아보십시오.

이제 선택할 수 있습니다. Typescript로 이동 하시겠습니까?

Typescript에는 인텔리전스, 오류 조기 발견, API 사전 지정, 리팩토링시 오류 수정, 테스트 횟수 감소 등 몇 가지 강력한 이점이 있습니다. Typescript에는 약간의 비용이 있습니다. 매우 자연스럽고 정확한 JavaScript 관용구는 특히 강력하지 않은 유형 시스템에서 모델링하기 까다로울 수 있습니다. 주석은 LoC를 늘리고 기존 코드베이스를 다시 작성하는 시간과 노력, 빌드 파이프 라인의 추가 단계 등 더 근본적으로, 그것은 당신의 코드가 더 가능성이 높다는 약속에 대한 대가로 가능한 올바른 JavaScript 프로그램 의 하위 세트 를 제공합니다. 정확할 . 임의로 제한적입니다. 그것은 요점입니다. 당신은 당신을 제한하는 몇 가지 규율을 부과합니다 (희망적으로 발에 총을 쏘는 것을 피하십시오).

질문으로 돌아가서, 위 단락의 맥락에서 표현됩니다 : 그만한 가치가 있습니까? 있습니까?

설명 된 시나리오에서 중소 규모의 JS 코드베이스에 대해 잘 알고 있다면 Typescript를 사용하는 선택이 실제보다 미학적이라고 주장합니다. 그리고 그것은 괜 찮 아 요 , 미학 에는 아무 문제 가 없습니다. 그들은 반드시 설득력있는 것은 아닙니다.

시나리오 B :

Foo Corp에서 업무를 변경하고 현재 업무용 프로그래머로 일하고 있습니다. 90000 LoC (및 계산) JavaScript / HTML / CSS 코드베이스에서 10 명의 팀과 협력하고 있습니다. , polyfills 세트, 다양한 플러그인, 상태 관리 시스템, ~ 20 타사 라이브러리, ~ 10 내부 라이브러리, 내부 스타일 가이드 규칙이있는 린터와 같은 편집기 플러그인 등과 반응합니다.

당신이 5k LoC 남자 / 여자 였을 때, 그것은 그다지 중요하지 않았습니다. 심지어 문서는 아니었다 것을 큰 거래 6 개월 후 코드의 특정 부분에도오고 다시 당신이 충분히 쉽게 알아낼 수있다. 그러나 이제는 훈련이 좋지만 필요한 것은 아닙니다 . 그 징계에는 타이프 스크립트가 필요하지 않지만 것입니다 가능성이 정적 분석의 형태뿐만 아니라 모든으로 코딩 분야 (문서, 스타일 가이드, 빌드 스크립트, 회귀 테스트, CI)의 다른 형태를 포함한다. 징계는 더 이상없는 고급 그것이이다 필요성 .

이 모든 것이 GOTO1978 년에 적용되었습니다 : C의 ​​엉뚱한 작은 블랙 잭 게임은 GOTOs와 스파게티 논리를 사용할 수 있었으며 , 그것을 통해 자신의 모험을 선택하는 것이 그다지 큰 것은 아니었지만 프로그램이 커지고 더 야심 차고 잘 훈련GOTO 되지 않은 사용은 지속될 수 없었다. 그리고이 모든 것은 오늘날 불변성에 적용됩니다.

정적 유형과 마찬가지로, 유지 관리 / 확장 엔지니어 팀과 함께 큰 코드베이스에서 작업하지 않는 경우 불변성을 사용하는 선택은 실용적보다 미학적입니다. 이점은 여전히 ​​있지만 아직 비용을 능가 할 수는 없습니다.

그러나 모든 유용한 학문과 마찬가지로 더 이상 선택 사항이 아닌 시점이 있습니다. 건강한 체중을 유지하려면 아이스크림과 관련된 징계가 선택 사항 일 수 있습니다. 그러나 내가 경쟁적인 선수가되고 싶다면, 아이스크림을 먹을지 여부는 내가 선택한 목표에 의해 결정됩니다. 소프트웨어로 세상을 바꾸고 싶다면 불변성은 자체 무게로 인해 붕괴되는 것을 피하는 데 필요할 수 있습니다.


1
+1 마음에 들어요. Jared 지점에 훨씬 더. 그러나 불변성으로 인해 팀 자체의 훈련 부족을 막을 수는 없습니다. 😉
Steven de Salas

@StevendeSalas는 징계의 한 형태입니다. 따라서 저는 이것이 다른 형태의 소프트웨어 엔지니어링 분야와 관련이 있다고 생각합니다. 대체물이 아니라 보완합니다. 그러나 귀하의 답변에 대한 의견에서 말했듯이, 동일한 거대 한 코드베이스에서 모든 기술자가 분쇄하는 기술 거인에 의해 추진되고 있다는 사실에 놀라지 않습니다. :) 그들이 얻을 수있는 모든 훈련이 필요합니다. 나는 대부분 객체를 변형시키지 않고 단지 나이기 때문에 어떤 형태의 시행도 사용하지 않습니다.
Jared Smith

0

나는 libs (redux, vuex 등 ...)와 같은 불변의 모든 저장소를 대체 할 수있는 변경 가능한 (또는 불변의) 상태에 대한 프레임 워크 불가지론 적 오픈 소스 (MIT) lib를 만들었습니다.

수행 할 작업이 너무 많고 (간단한 읽기 / 쓰기 작업을위한 많은 작업), 코드를 읽을 수 없었으며 큰 데이터 세트의 성능이 허용되지 않았기 때문에 (불변 상태는보기 흉한 상태였습니다.

deep-state-observer를 사용하면 점 표기법으로 하나의 노드 만 업데이트하고 와일드 카드를 사용할 수 있습니다. 또한 변경된 구체적인 값만 유지 한 상태 기록 (실행 취소 / 다시 실행 / 시간 여행)을 만들 수도 있습니다.{path:value} 메모리 = 메모리 사용량을 줄인 .

딥 스테이트 옵저버를 사용하면 사물을 미세 조정할 수 있으며 구성 요소 동작을 세부적으로 제어하여 성능을 크게 향상시킬 수 있습니다. 코드를 더 읽기 쉽고 리팩토링하는 것이 훨씬 쉽습니다. 경로 문자열을 검색하고 바꾸면됩니다 (코드 / 논리를 변경할 필요가 없음).


-1

나는 불변의 객체 인 주된 이유는 객체의 상태를 유효하게 유지하는 것이라고 생각합니다.

라는 객체가 있다고 가정합니다 arr. 이 개체는 모든 항목이 같은 문자 일 때 유효합니다.

// this function will change the letter in all the array
function fillWithZ(arr) {
    for (var i = 0; i < arr.length; ++i) {
        if (i === 4) // rare condition
            return arr; // some error here

        arr[i] = "Z";
    }

    return arr;
}

console.log(fillWithZ(["A","A","A"])) // ok, valid state
console.log(fillWithZ(["A","A","A","A","A","A"])) // bad, invalid state

경우 arr불변의 객체가 될, 우리는 편곡이 유효한 상태에 항상 확실 할 것이다.


arr전화 할 때마다 돌연변이가 발생 한다고 생각 합니다fillWithZ
rodrigo-silveira

immutable.js를 사용하면 객체를 변경할 때마다 새 객체 사본이 제공됩니다. 원래 개체는 그대로 유지
bedorlan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.