JavaScript 인터뷰 마스터하기 : 함수형 프로그래밍이란 무엇입니까?

구조 신디사이저-오리 하우스 (CC BY 2.0)
“Master the JavaScript Interview”는 중급에서 상급 JavaScript 위치에 지원할 때 발생할 수있는 일반적인 질문에 대한 응시자를 준비하기 위해 설계된 일련의 게시물입니다. 실제 인터뷰에서 자주 사용하는 질문입니다.

함수형 프로그래밍은 JavaScript 세계에서 정말 인기있는 주제가되었습니다. 몇 년 전만해도 함수형 프로그래밍이 무엇인지 아는 JavaScript 프로그래머는 거의 없었지만 지난 3 년 동안 내가 본 모든 대규모 응용 프로그램 코드베이스는 함수형 프로그래밍 아이디어를 많이 사용합니다.

기능 프로그래밍 (종종 약칭 FP)은 순수한 기능을 구성하고 공유 상태, 변경 가능한 데이터 및 부작용을 피함으로써 소프트웨어를 구축하는 프로세스입니다. 함수형 프로그래밍은 명령이 아닌 선언적이며 응용 프로그램 상태는 순수한 함수를 통해 흐릅니다. 응용 프로그램 상태가 일반적으로 객체의 메소드와 공유되고 같은 위치에있는 객체 지향 프로그래밍과 대조됩니다.

함수형 프로그래밍은 프로그래밍 패러다임이며, 이는 기본적으로 정의 된 몇 가지 원칙 (위에 나열된)을 기반으로 소프트웨어 구성을 생각하는 방식임을 의미합니다. 프로그래밍 패러다임의 다른 예로는 객체 지향 프로그래밍 및 절차 적 프로그래밍이 있습니다.

함수형 코드는 명령형 또는 객체 지향 코드보다 더 간결하고 예측 가능하며 테스트하기 쉬운 경향이 있지만, 코드 및 이와 관련된 일반적인 패턴에 익숙하지 않은 경우 함수형 코드가 훨씬 더 조밀 해 보일 수 있습니다. 관련 문헌은 신규 이민자에게 뚫을 수 없습니다.

기능적 프로그래밍 용어를 인터넷 검색을 시작하면 초보자에게 매우 위협적인 학문적 용어의 벽돌 벽에 빨리 부딪 칠 것입니다. 학습 곡선이 있다고 말하는 것은 심각한 과소 평가입니다. 그러나 한동안 자바 스크립트로 프로그래밍했다면 실제 소프트웨어에서 많은 기능적 프로그래밍 개념과 유틸리티를 사용했을 가능성이 높습니다.

모든 새로운 단어가 당신을 놀라게하지 마십시오. 소리보다 훨씬 쉽습니다.

가장 어려운 부분은 익숙하지 않은 모든 어휘에 머리를 감는 것입니다. 함수형 프로그래밍의 의미를 파악하기 전에 모든 것을 이해해야 할 무고 해 보이는 정의에는 많은 아이디어가 있습니다.

  • 순수한 기능
  • 기능 구성
  • 공유 상태를 피하십시오
  • 돌연변이 상태를 피하십시오
  • 부작용을 피하십시오

다시 말해, 실제로 함수형 프로그래밍의 의미를 알고 싶다면 이러한 핵심 개념을 이해해야합니다.

순수한 함수는 다음과 같은 함수입니다.

  • 동일한 입력이 주어지면 항상 동일한 출력을 반환하고
  • 부작용이 없습니다

순수 함수에는 참조 투명도를 포함하여 함수형 프로그래밍에 중요한 많은 속성이 있습니다 (프로그램의 의미를 변경하지 않고 함수 호출을 결과 값으로 대체 할 수 있음). 자세한 내용은“순수한 기능이란 무엇입니까?”를 읽으십시오.

함수 구성은 새로운 함수를 생성하거나 계산을 수행하기 위해 둘 이상의 함수를 결합하는 프로세스입니다. 예를 들어, 조성 f. g (점은 "로 구성됨"을 의미)는 JavaScript에서 f (g (x))와 같습니다. 함수 구성 이해는 함수형 프로그래밍을 사용하여 소프트웨어를 구성하는 방법을 이해하는 데 중요한 단계입니다. 자세한 내용은“함수 구성이란 무엇입니까?”를 읽으십시오.

공유 상태

공유 상태는 공유 범위에 있거나 범위간에 전달되는 개체의 속성으로 존재하는 모든 변수, 개체 또는 메모리 공간입니다. 공유 범위에는 전역 범위 또는 폐쇄 범위가 포함될 수 있습니다. 종종 객체 지향 프로그래밍에서 다른 객체에 속성을 추가하여 범위간에 객체를 공유합니다.

예를 들어, 컴퓨터 게임에는 마스터 게임 객체가있을 수 있으며 캐릭터와 게임 항목은 해당 객체가 소유 한 속성으로 저장됩니다. 함수형 프로그래밍은 공유 상태를 피합니다. 대신 불변의 데이터 구조와 순수한 계산에 의존하여 기존 데이터에서 새 데이터를 도출합니다. 기능성 소프트웨어가 애플리케이션 상태를 처리하는 방법에 대한 자세한 내용은“더 나은 Redux 아키텍처를위한 10 가지 팁”을 참조하십시오.

공유 상태의 문제점은 함수의 효과를 이해하려면 함수가 사용하거나 영향을주는 모든 공유 변수의 전체 히스토리를 알아야한다는 것입니다.

저장해야하는 사용자 개체가 있다고 가정합니다. saveUser () 함수는 서버의 API를 요청합니다. 그 동안 사용자는 updateAvatar ()로 프로필 사진을 변경하고 다른 saveUser () 요청을 트리거합니다. 저장시, 서버는 서버에서 발생하는 변경 사항 또는 다른 API 호출에 응답하여 메모리에있는 모든 것을 대체해야하는 표준 사용자 오브젝트를 다시 보냅니다.

불행하게도, 첫 번째 응답 이전에 두 번째 응답이 수신되므로 첫 번째 (현재 구식) 응답이 리턴되면 새 프로파일 사진이 메모리에서 지워지고 이전 프로파일로 바뀝니다. 이것은 공유 상태와 관련된 매우 일반적인 버그 인 경쟁 조건의 예입니다.

공유 상태와 관련된 또 다른 일반적인 문제는 함수가 호출되는 순서를 변경하면 공유 상태에서 작동하는 함수가 타이밍에 따라 연속적으로 실패 할 수 있다는 것입니다.

공유 상태를 피하면 함수 호출의 타이밍과 순서는 함수 호출 결과를 변경하지 않습니다. 순수한 함수를 사용하면 동일한 입력이 주어지면 항상 동일한 출력을 얻을 수 있습니다. 이는 함수 호출을 다른 함수 호출과 완전히 독립적으로 만들어 변경 및 리팩토링을 근본적으로 단순화 할 수 있습니다. 한 기능의 변경 또는 기능 호출의 타이밍이 프로그램의 다른 부분을 파열시키지 않습니다.

위의 예제에서 Object.assign ()을 사용하고 빈 객체를 첫 번째 매개 변수로 전달하여 x의 속성을 변경하지 않고 복사합니다. 이 경우 Object.assign ()을 사용하지 않고 처음부터 새 객체를 작성하는 것과 같았지만 돌연변이를 사용하는 대신 기존 상태의 사본을 작성하는 JavaScript의 일반적인 패턴입니다. 예.

이 예제에서 console.log () 문을 자세히 살펴보면 이미 언급 한 함수 구성을 확인할 수 있습니다. 앞에서 보았 듯이 함수 구성은 다음과 같습니다. f (g (x)). 이 경우 컴포지션에 대해 f () 및 g ()를 x1 () 및 x2 ()로 바꿉니다 : x1. x2.

물론 컴포지션의 순서를 변경하면 출력이 변경됩니다. 작업 순서는 여전히 중요합니다. f (g (x))가 항상 g (f (x))와 같지는 않지만 더 이상 문제가되지 않는 것은 함수 외부의 변수에 발생하는 것입니다. 이는 매우 중요합니다. 불완전한 함수를 사용하면 함수가 사용하거나 영향을 미치는 모든 변수의 전체 기록을 알지 못하면 함수의 기능을 완전히 이해할 수 없습니다.

함수 호출 타이밍 종속성을 제거하고 잠재적 인 버그의 전체 클래스를 제거합니다.

불변성

불변 개체는 만든 후에는 수정할 수없는 개체입니다. 반대로, 가변 객체는 객체를 만든 후에 수정할 수있는 객체입니다.

불변성은 함수형 프로그래밍의 핵심 개념으로, 프로그램이 없으면 프로그램의 데이터 흐름이 손실되기 때문입니다. 상태 기록이 취소되고 이상한 버그가 소프트웨어에 들어올 수 있습니다. 불변성의 중요성에 대한 자세한 내용은“불변의 Dao”를 참조하십시오.

JavaScript에서는 const와 불변성을 혼동하지 않는 것이 중요합니다. const는 생성 후 재 할당 할 수없는 변수 이름 바인딩을 만듭니다. const는 변경할 수없는 객체를 만들지 않습니다. 바인딩이 참조하는 객체는 변경할 수 없지만 여전히 객체의 속성을 변경할 수 있습니다. 즉 const로 만든 바인딩은 변경할 수없고 변경할 수 없습니다.

불변 개체는 전혀 변경할 수 없습니다. 개체를 깊게 얼려 값을 변경할 수 없습니다. JavaScript에는 객체를 한 수준 깊이 고정시키는 방법이 있습니다.

그러나 냉동 된 물체는 표면적으로 만 불변입니다. 예를 들어 다음 객체는 변경 가능합니다.

보시다시피 고정 된 객체의 최상위 기본 속성은 변경할 수 없지만 객체 (배열 등 포함)이기도 한 속성은 여전히 ​​변경할 수 있으므로 고정 된 객체도 걸을 수없는 한 변경할 수 없습니다. 전체 오브젝트 트리를 만들고 모든 오브젝트 속성을 고정합니다.

많은 기능적 프로그래밍 언어에는 효과적으로 깊은 고정 된 trie 데이터 구조 ( "트리"로 표시)라는 불변의 특수 데이터 구조가 있습니다. 즉, 객체 계층의 속성 수준에 관계없이 속성을 변경할 수 없습니다.

시도는 구조적 공유를 사용하여 운영자가 오브젝트를 복사 한 후에도 변경되지 않은 오브젝트의 모든 부분에 대한 참조 메모리 위치를 공유합니다. 이는 메모리를 덜 사용하고 일부 유형의 조작에서 성능을 크게 향상시킵니다.

예를 들어 비교를 위해 객체 트리의 루트에서 ID 비교를 사용할 수 있습니다. 동일성이 동일하다면 전체 트리를 걸어 다닐 필요가 없습니다.

JavaScript에는 Immutable.js 및 Mori를 포함하여 시도를 이용하는 여러 라이브러리가 있습니다.

나는 두 가지를 모두 실험했으며 상당한 양의 불변 상태가 필요한 대규모 프로젝트에서 Immutable.js를 사용하는 경향이 있습니다. 이에 대한 자세한 내용은“더 나은 Redux 아키텍처를위한 10 가지 팁”을 참조하십시오.

부작용

부작용은 반환 값 이외의 호출 된 함수 외부에서 관찰 할 수있는 응용 프로그램 상태 변경입니다. 부작용은 다음과 같습니다.

  • 외부 변수 또는 객체 속성 수정 (예 : 전역 변수 또는 부모 함수 범위 체인의 변수)
  • 콘솔에 로깅
  • 화면에 쓰기
  • 파일에 쓰기
  • 네트워크에 쓰기
  • 외부 프로세스 트리거
  • 부작용이있는 다른 함수 호출

기능적 프로그래밍에서는 부작용을 피할 수 있으므로 프로그램의 효과를 훨씬 쉽게 이해하고 테스트하기가 훨씬 쉽습니다.

Haskell 및 기타 기능 언어는 모나드를 사용하여 순수한 기능과 부작용을 분리하고 캡슐화합니다. 모나드의 주제는 책을 쓸만큼 깊기 때문에 나중에 저장하겠습니다.

지금 당장 알아야 할 것은 부작용이 소프트웨어의 나머지 부분과 격리되어야한다는 것입니다. 부작용을 나머지 프로그램 논리와 분리하면 소프트웨어를 훨씬 쉽게 확장, 리팩토링, 디버그, 테스트 및 유지 관리 할 수 ​​있습니다.

이것이 대부분의 프론트 엔드 프레임 워크가 사용자가 느슨하게 연결된 별도의 모듈에서 상태 및 구성 요소 렌더링을 관리하도록 권장하는 이유입니다.

고차 함수를 통한 재사용 성

기능적 프로그래밍은 공통 기능 집합을 재사용하여 데이터를 처리하는 경향이 있습니다. 객체 지향 프로그래밍은 객체에서 메소드와 데이터를 함께 배치하는 경향이 있습니다. 배치 된 메소드는 작동하도록 설계된 데이터 유형 및 특정 오브젝트 인스턴스에 포함 된 데이터에서만 작동 할 수 있습니다.

함수형 프로그래밍에서 모든 유형의 데이터는 공정한 게임입니다. 동일한 map () 유틸리티는 주어진 데이터 유형을 적절하게 처리하는 인수로 함수를 사용하기 때문에 객체, 문자열, 숫자 또는 기타 데이터 유형을 매핑 할 수 있습니다. FP는 더 높은 차수의 함수를 사용하여 일반적인 유틸리티 속임수를 사용합니다.

JavaScript에는 일급 함수가 있습니다.이를 통해 함수를 데이터로 취급 할 수 있습니다. 변수에 할당하고, 다른 함수에 전달하고, 함수에서 반환하는 등

고차 함수는 함수를 인수로 사용하거나, 함수를 반환하거나, 또는 둘 다를 반환하는 함수입니다. 고차 함수는 종종 다음을 위해 사용됩니다.

  • 콜백 함수, 약속, 모나드 등을 사용하여 동작, 효과 또는 비동기 흐름 제어를 추상화하거나 격리합니다.
  • 다양한 데이터 유형에서 작동 할 수있는 유틸리티 작성
  • 재사용 또는 함수 구성을 위해 함수를 인수에 부분적으로 적용하거나 카레 함수를 작성
  • 함수 목록을 가져 와서 해당 입력 함수의 일부 구성을 반환

컨테이너, 펑터, 목록 및 스트림

펑 터는 매핑 할 수있는 것입니다. 다시 말해, 내부의 값에 함수를 적용하는 데 사용할 수있는 인터페이스가있는 컨테이너입니다. functor라는 단어가 보이면“mappable”이라고 생각해야합니다.

앞서 우리는 동일한 map () 유틸리티가 다양한 데이터 유형에 작용할 수 있다는 것을 배웠다. functor API로 작업하기 위해 매핑 작업을 들어 올림으로써 수행됩니다. map ()에서 사용하는 중요한 흐름 제어 작업은 해당 인터페이스를 이용합니다. Array.prototype.map ()의 경우 컨테이너는 배열이지만 매핑 API를 제공하는 한 다른 데이터 구조도 펑터가 될 수 있습니다.

Array.prototype.map ()을 사용하여 map ()을 모든 데이터 유형에서 사용할 수 있도록 매핑 유틸리티에서 데이터 유형을 추상화하는 방법을 살펴 보겠습니다. 전달 된 값에 2를 곱하는 간단한 double () 매핑을 만듭니다.

게임에서 목표를 달성하여 획득 한 포인트 수를 두 배로 늘리려면 어떻게해야합니까? 우리가해야 할 일은 map ()에 전달하는 double () 함수를 미묘하게 변경하는 것입니다. 모든 것이 여전히 작동합니다.

함수형 프로그래밍에서는 일반 유틸리티 함수를 사용하여 여러 가지 다른 데이터 유형을 조작하기 위해 펑터 및 고차 함수와 같은 추상화를 사용하는 개념이 중요합니다. 비슷한 개념이 모든 종류의 다른 방식으로 적용되는 것을 볼 수 있습니다.

"시간이 지남에 따라 표현 된 목록은 스트림입니다."

현재 이해해야 할 것은 배열과 펑터가 컨테이너의 컨테이너 및 값 개념이 적용되는 유일한 방법은 아니라는 것입니다. 예를 들어, 배열은 사물 목록 일뿐입니다. 시간이 지남에 따라 표현 된 목록은 스트림이므로 동일한 종류의 유틸리티를 적용하여 들어오는 이벤트 스트림을 처리 할 수 ​​있습니다. FP로 실제 소프트웨어를 구축 할 때 많이 볼 수 있습니다.

선언적 대 명령형

함수형 프로그래밍은 선언적 패러다임입니다. 즉, 흐름 제어를 명시 적으로 설명하지 않고 프로그램 논리가 표현됩니다.

명령형 프로그램은 원하는 결과 (흐름 제어 : 작업 방법)를 달성하는 데 사용되는 특정 단계를 설명하는 일련의 코드를 사용합니다.

선언적 프로그램은 흐름 제어 프로세스를 추상화하고 대신 데이터 흐름을 설명하는 코드 라인을 사용합니다. 어떻게 추상화 되는가.

예를 들어이 명령형 매핑은 숫자 배열을 가져 와서 각 숫자에 2를 곱한 새 배열을 반환합니다.

이 선언적 매핑은 동일한 작업을 수행하지만 기능적인 Array.prototype.map () 유틸리티를 사용하여 흐름 제어를 추상화하여 데이터 흐름을보다 명확하게 표현할 수 있습니다.

명령형 코드는 자주 문을 사용합니다. 명령문은 일부 작업을 수행하는 코드입니다. 일반적으로 사용되는 문의 예로는 for, if, switch, throw 등이 있습니다.

선언적 코드는 표현식에 더 의존합니다. 표현식은 어떤 값으로 평가되는 코드입니다. 표현식은 일반적으로 결과 값을 생성하기 위해 평가되는 함수 호출, 값 및 연산자의 조합입니다.

이들은 모두 표현의 예입니다.

2 * 2
doubleMap ([2, 3, 4])
Math.max (4, 3, 2)

일반적으로 코드에서는 표현식이 식별자에 할당되거나 함수에서 반환되거나 함수로 전달되는 것을 볼 수 있습니다. 할당, 반환 또는 전달하기 전에 먼저 식을 평가하고 결과 값을 사용합니다.

결론

기능적 프로그래밍 선호 :

  • 공유 상태 및 부작용 대신 순수한 기능
  • 변경 가능한 데이터에 대한 불변성
  • 명령형 흐름 제어를 통한 기능 구성
  • 배치 된 데이터에서만 작동하는 메소드 대신 많은 데이터 유형에 대해 고차 함수를 사용하는 많은 일반 재사용 가능 유틸리티
  • 명령형 코드가 아닌 선언적 (작업 방법이 아니라 수행 할 작업)
  • 문장에 대한 표현
  • 임시 다형성에 대한 컨테이너 및 고차 함수

숙제

이 핵심 기능 배열 추가 기능을 배우고 실습하십시오.

  • .지도()
  • .필터()
  • .줄이다()

map을 사용하여 다음 값 배열을 항목 이름 배열로 변환하십시오.

포인트를 3보다 크거나 같은 항목을 선택하려면 필터를 사용하십시오.

포인트를 합산하기 위해 reduce를 사용하십시오.

시리즈 둘러보기

  • 폐쇄 란 무엇입니까?
  • 계급과 프로토 타입 상속의 차이점은 무엇입니까?
  • 순수한 기능이란 무엇입니까?
  • 기능 구성이란 무엇입니까?
  • 기능적 프로그래밍이란 무엇입니까?
  • 약속이란 무엇입니까?
  • 소프트 스킬
이 게시물은“Composing Software”책에 포함되었습니다.
도서 구매 | 색인 | <이전 | 다음>
EricElliottJS.com에서 무료 레슨을 시작하십시오

Eric Elliott는 "Composing Software"및 "JavaScript Applications 프로그래밍"책의 저자입니다. EricElliottJS.com과 DevAnywhere.io의 공동 창립자로서 개발자에게 필수 소프트웨어 개발 기술을 가르칩니다. 그는 암호화 프로젝트를위한 개발 팀을 구성하고 조언하며 Adobe Systems, Zumba Fitness, The Wall StreetJournal, ESPN, BBC 및 Usher, Frank Ocean, Metallica 등을 포함한 최고의 레코딩 아티스트를위한 소프트웨어 경험에 기여했습니다.

그는 세상에서 가장 아름다운 여성과 함께 외진 생활을 즐깁니다.