함수형 프로그래머의 JavaScript 소개 (작곡 소프트웨어)

스모크 아트 큐브 스모크 — MattysFlicks — (CC BY 2.0)
참고 :이 기능은 JavaScriptES6 +에서 기능 프로그래밍 및 컴포지션 소프트웨어 기술을 처음부터 배우는“소프트웨어 구성”시리즈 (현재 책!)의 일부입니다. 계속 지켜봐 주시기 바랍니다. 앞으로 더 많은 것들이 있습니다!
도서 구매 | 색인 | <이전 | 다음>

JavaScript 또는 ES6 +에 익숙하지 않은 사용자를위한 간단한 소개입니다. 초보자이든 숙련 된 JavaScript 개발자 든 새로운 것을 배울 수 있습니다. 다음은 표면을 긁어 내고 흥분하게 만드는 것입니다. 더 알고 싶다면 더 깊이 탐구하면됩니다. 더 많은 것이 있습니다.

코딩하는 법을 배우는 가장 좋은 방법은 코딩하는 것입니다. CodePen 또는 Babel REPL과 같은 대화식 JavaScript 프로그래밍 환경을 사용하는 것이 좋습니다.

또는 노드 또는 브라우저 콘솔 REPL을 사용하여 벗어날 수 있습니다.

표현과 가치

표현식은 값으로 평가되는 코드 덩어리입니다.

다음은 JavaScript에서 유효한 모든 표현식입니다.

7;
7 + 1; // 8
7 * 2; // 14
'여보세요'; // 여보세요

표현식의 값에는 이름이 주어질 수 있습니다. 이렇게하면식이 먼저 평가되고 결과 값이 이름에 할당됩니다. 이를 위해 const 키워드를 사용합니다. 유일한 방법은 아니지만 가장 많이 사용하는 방법이므로 지금은 const를 사용합니다.

const hello = 'Hello';
여보세요; // 여보세요

var, let 및 const

JavaScript는 var와 let이라는 두 가지 변수 선언 키워드를 더 지원합니다. 나는 그것들을 선택 순서로 생각하고 싶다. 기본적으로 가장 엄격한 선언 인 const를 선택합니다. const 키워드로 선언 된 변수는 재 할당 할 수 없습니다. 최종 값은 선언시 지정해야합니다. 이것은 딱딱하게 들릴 수 있지만 제한은 좋은 것입니다. "이 이름에 할당 된 값은 변경되지 않습니다"라는 신호입니다. 전체 기능이나 블록 범위를 읽을 필요없이 이름의 의미를 즉시 이해하는 데 도움이됩니다.

때로는 변수를 다시 할당하는 것이 유용합니다. 예를 들어,보다 기능적인 접근 방식이 아닌 수동적이고 명령적인 반복을 사용하는 경우 let이 할당 된 카운터를 반복 할 수 있습니다.

var는 변수에 대해 가장 적게 알려주기 때문에 가장 약한 신호입니다. ES6을 사용하기 시작한 이후로 실제 소프트웨어 프로젝트에서 의도적으로 var를 선언 한 적이 없습니다.

let 또는 const로 변수를 선언하면 변수를 다시 선언하면 오류가 발생합니다. REPL (읽기, 평가, 인쇄 루프) 환경에서보다 실험적인 유연성을 선호하는 경우 const 대신 var를 사용하여 변수를 선언 할 수 있습니다. var 재 선언이 허용됩니다.

이 텍스트는 실제 프로그램의 const를 기본 설정하는 습관을 갖기 위해 const를 사용하지만 대화 형 실험의 목적으로 var을 자유롭게 대체 할 수 있습니다.

종류

지금까지 숫자와 문자열이라는 두 가지 유형이 있습니다. JavaScript에는 부울 (true 또는 false), 배열, 객체 등이 있습니다. 나중에 다른 유형을 살펴 보겠습니다.

배열은 정렬 된 값 목록입니다. 많은 아이템을 담을 수있는 상자로 생각하십시오. 배열 리터럴 표기법은 다음과 같습니다.

[1, 2, 3];

물론, 그것은 이름을 붙일 수있는 표현입니다 :

const arr = [1, 2, 3];

JavaScript의 객체는 키 : 값 쌍의 모음입니다. 또한 문자 표기법이 있습니다.

{
  핵심 가치'
}

물론 이름에 객체를 할당 할 수 있습니다.

const foo = {
  바 : 'bar'
}

동일한 이름의 객체 속성 키에 기존 변수를 할당하려면 바로 가기가 있습니다. 키와 값을 모두 제공하는 대신 변수 이름을 입력하면됩니다.

const a = 'a';
const oldA = {a : a}; // 길고 중복 된 방법
const oA = {a}; // 단 것을 짧게!

재미로 다시 해봅시다.

const b = 'b';
const oB = {b};

객체를 새로운 객체로 쉽게 구성 할 수 있습니다.

const c = {... oA, ... oB}; // {a : 'a', b : 'b'}

이 점들은 객체 확산 연산자입니다. oA의 속성을 반복하여 새 객체에 할당 한 다음 oB에 대해 동일한 작업을 수행하여 새 객체에 이미 존재하는 모든 키를 재정의합니다. 이 글을 쓰는 시점에서 객체 확산은 모든 인기있는 브라우저에서 아직 사용 가능하지 않을 수있는 새로운 실험적 기능이지만 작동하지 않는 경우 Object.assign () 대신 사용할 수 있습니다.

const d = Object.assign ({}, oA, oB); // {a : 'a', b : 'b'}

Object.assign () 예제에서 조금만 더 입력하면 많은 객체를 작성하는 경우 일부 입력을 저장할 수도 있습니다. Object.assign ()을 사용할 때는 대상 객체를 첫 번째 매개 변수로 전달해야합니다. 속성을 복사 할 개체입니다. 대상 객체를 잊어 버리고 생략하면 첫 번째 인수에 전달한 객체가 변경됩니다.

내 경험상 새 객체를 만드는 대신 기존 객체를 변경하는 것은 일반적으로 버그입니다. 최소한 오류가 발생하기 쉽습니다. Object.assign ()에주의하십시오.

파괴

객체와 배열 모두 파괴를 지원합니다. 즉, 객체에서 값을 추출하여 명명 된 변수에 할당 할 수 있습니다.

const [t, u] = [ 'a', 'b'];
티; // 'a'
유; // 'b'
const blep = {
  블롭 : '블롭'
};

// 다음은 다음과 같습니다.
// const blop = blep.blop;
const {blop} = blep;
블롭; // '블롭'

위의 배열 예와 같이 한 번에 여러 할당으로 구조를 해제 할 수 있습니다. 다음은 많은 Redux 프로젝트에서 볼 수있는 내용입니다.

const {type, payload} = action;

감속기의 맥락에서 사용되는 방법은 다음과 같습니다 (나중에 해당 주제에 대해 자세히 설명).

const myReducer = (상태 = {}, 액션 = {}) => {
  const {type, payload} = action;
  스위치 (유형) {
    case 'FOO': return Object.assign ({}, 상태, 페이로드);
    기본값 : 반환 상태;
  }
};

새 바인딩에 다른 이름을 사용하지 않으려면 새 이름을 지정할 수 있습니다.

const {blop : bloop} = blep;
블룹; // '블롭'

읽기 : blep.blop을 bloop로 지정하십시오.

비교와 지역

엄격한 항등 연산자와 값을 비교할 수 있습니다 (“트리플 같음”이라고도 함).

3 + 1 === 4; // 참

조잡한 평등 연산자도 있습니다. 공식적으로“동일”연산자라고합니다. 비공식적으로, "double equals". Double equals에는 유효한 유스 케이스가 2 개 있지만 대신 === 연산자를 기본값으로 사용하는 것이 거의 항상 좋습니다.

다른 비교 연산자는 다음과 같습니다.

  • >보다 큼
  • <보다 작음
  • > = 이상
  • <= 이하
  • ! = 같지 않다
  • ! == 엄격하지 않음
  • && 논리 및
  • || 논리 또는

삼항식은 비교자를 사용하여 질문을 할 수있는 표현이며 표현이 진실인지 아닌지에 따라 다른 답변으로 평가됩니다.

14-7 === 7? '네!' : '네.'; // 응!

기능

JavaScript에는 함수 표현식이 있으며 이름에 지정할 수 있습니다.

const double = x => x * 2;

이것은 수학 함수 f (x) = 2x와 같은 의미입니다. 큰 소리로 말하면,이 함수는 x의 f는 2x와 같습니다. 이 함수는 특정 값 x에 적용 할 때만 흥미 롭습니다. 다른 방정식에서이 함수를 사용하려면 f와 같은 f (2)를 쓰십시오.

다시 말해, f (2) = 4입니다. 수학 함수는 입력에서 출력으로의 매핑으로 생각할 수 있습니다. 이 경우 f (x)는 x에 대한 입력 값을 입력 값의 곱과 2에 해당하는 해당 출력 값에 매핑합니다.

JavaScript에서 함수 표현식의 값은 함수 자체입니다.

더블; // [함수 : 더블]

.toString () 메소드를 사용하여 함수 정의를 볼 수 있습니다.

double.toString (); // 'x => x * 2'

일부 인수에 함수를 적용하려면 함수 호출로 함수를 호출해야합니다. 함수 호출은 함수를 인수에 적용하고 리턴 값으로 평가합니다.

(argument1, argument2, ... rest)를 사용하여 함수를 호출 할 수 있습니다. 예를 들어, 이중 함수를 호출하려면 괄호를 추가하고 값을 double로 전달하십시오.

이중 (2); // 4

일부 기능 언어와 달리 괄호는 의미가 있습니다. 그것들이 없으면 함수가 호출되지 않습니다.

더블 4; // SyntaxError : 예기치 않은 숫자

서명

함수에는 다음과 같은 서명이 있습니다.

  1. 선택적 기능 이름.
  2. 괄호 안의 매개 변수 유형 목록입니다. 파라미터는 선택적으로 명명 될 수있다.
  3. 반환 값의 타입.

JavaScript에서 유형 서명을 지정할 필요는 없습니다. JavaScript 엔진은 런타임에 유형을 파악합니다. 충분한 단서를 제공하면 데이터 흐름 분석을 사용하여 IDE (Integrated Development Environment) 및 Tern.js와 같은 개발자 도구에서 서명을 유추 할 수도 있습니다.

JavaScript에는 자체 함수 서명 표기법이 없으므로 몇 가지 경쟁 표준이 있습니다. JSDoc은 역사적으로 매우 인기가 있었지만 어색하게 장황하며 아무도 문서 주석을 코드로 최신 상태로 유지하지 않아도되므로 많은 JS 개발자가 가지고 있습니다. 사용을 중지했습니다.

TypeScript와 Flow는 현재 큰 경쟁자입니다. 그중 하나에 필요한 모든 것을 표현하는 방법을 잘 모르겠으므로 Rtype을 문서 목적으로 만 사용합니다. 어떤 사람들은 Haskell의 카레 전용 Hindley–Milner 유형으로 돌아갑니다. 문서화 목적으로 만 JavaScript에 대해 표준화 된 좋은 표기법 시스템을보고 싶지만 현재 솔루션 중 어떤 것도 현재 작업에 달려 있다고 생각하지 않습니다. 지금은 삐걱 거리고 사용하는 것과 약간 다른 이상한 형식의 서명을 유지하기 위해 최선을 다하십시오.

functionName (param1 : 유형, param2 : 유형) => 유형

double의 서명은 다음과 같습니다.

double (x : n) => 숫자

JavaScript가 서명에 주석을 달지 않아도된다는 사실에도 불구하고, 기능이 어떻게 사용되는지, 기능이 어떻게 구성되는지에 대해 효율적으로 의사 소통하기 위해서는 서명의 의미와 의미를 아는 것이 여전히 중요합니다. 대부분의 재사용 가능한 함수 구성 유틸리티는 동일한 유형 서명을 공유하는 함수를 전달해야합니다.

기본 매개 변수 값

JavaScript는 기본 매개 변수 값을 지원합니다. 다음 함수는 정의되지 않은 상태로 호출하거나 인수를 전혀 전달하지 않으면 ID 함수 (전달하는 것과 동일한 값을 반환하는 함수)처럼 작동합니다. 대신 0을 반환합니다.

const orZero = (n = 0) => n;

기본값을 설정하려면 위의 n = 0에서와 같이 함수 서명에서 = 연산자로 매개 변수에 기본값을 지정하면됩니다. 이러한 방식으로 기본값을 할당하면 Tern.js, Flow 또는 TypeScript와 같은 형식 유추 도구는 형식 주석을 명시 적으로 선언하지 않더라도 함수의 형식 서명을 자동으로 유추 할 수 있습니다.

결과적으로 편집기 또는 IDE에 올바른 플러그인이 설치되어 있으면 함수 호출을 입력 할 때 함수 서명이 인라인으로 표시 될 수 있습니다. 또한 통화 서명을 기반으로 한 눈에 기능을 사용하는 방법을 이해할 수 있습니다. 의미가있는 기본 할당을 사용하면 더 많은 자체 문서화 코드를 작성할 수 있습니다.

참고 : 기본값이있는 매개 변수는 함수의 .length 속성에 포함되지 않으므로 .length 값에 따라 자동 카레와 같은 유틸리티가 삭제됩니다. lodash / curry와 같은 일부 카레 유틸리티를 사용하면이 제한을 해결하기 위해 사용자 정의 arity를 ​​전달할 수 있습니다.

명명 된 인수

JavaScript 함수는 객체 리터럴을 인수로 사용하고 명명 된 인수와 동등한 것을 달성하기 위해 매개 변수 시그니처에 구조적 할당을 사용할 수 있습니다. 기본 매개 변수 기능을 사용하여 매개 변수에 기본값을 지정할 수도 있습니다.

const createUser = ({
  이름 = '익명',
  avatarThumbnail = '/avatars/anonymous.png'
}) => ({
  이름,
  아바타
});
const george = createUser ({
  이름 : 'George',
  avatarThumbnail : 'avatars / shades-emoji.png'
});
성 조지;
/ *
{
  이름 : 'George',
  avatarThumbnail : 'avatars / shades-emoji.png'
}
* /

휴식과 전파

JavaScript에서 함수의 일반적인 기능은 rest 연산자를 사용하여 함수 시그니처에 남아있는 인수 그룹을 모을 수있는 기능입니다. ...

예를 들어 다음 함수는 단순히 첫 번째 인수를 버리고 나머지를 배열로 반환합니다.

const aTail = (head, ... tail) => 꼬리;
a 꼬리 (1, 2, 3); // [2, 3]

나머지는 개별 요소를 배열로 모 읍니다. 확산은 반대입니다. 배열에서 개별 요소로 요소를 펼칩니다. 이걸 고려하세요:

const shiftToLast = (head, ... tail) => [... 꼬리, 머리];
shiftToLast (1, 2, 3); // [2, 3, 1]

JavaScript의 배열에는 확산 연산자를 사용할 때 호출되는 반복자가 있습니다. 배열의 각 항목에 대해 반복자는 값을 제공합니다. [... tail, head] 표현식에서 반복자는 각 요소를 꼬리 배열에서 주변 리터럴 표기법으로 작성된 새 배열로 순서대로 복사합니다. 헤드는 이미 개별 요소이므로 배열의 끝에 닿으면 끝납니다.

카레

커리 함수는 한 번에 여러 매개 변수를 취하는 함수입니다. 매개 변수를 가져 와서 모든 매개 변수가 제공 될 때까지 다음 매개 변수를 취하는 함수 등을 반환하는 함수입니다.이 시점에서 응용 프로그램이 완료되고 최종 값이 반환됩니다.

다른 기능을 반환하여 카레 및 부분 응용 프로그램을 활성화 할 수 있습니다.

const 고역 통과 = 컷오프 => n => n> = 컷오프;
const gt4 = 고역 통과 (4); // highpass ()는 새로운 함수를 반환

화살표 기능을 사용할 필요가 없습니다. JavaScript에는 함수 키워드도 있습니다. function 키워드는 훨씬 더 타이핑하기 때문에 화살표 함수를 사용하고 있습니다. 위의 highPass () 정의와 동일합니다.

const highpass = 함수 고역 통과 (컷오프) {
  리턴 함수 (n) {
    리턴 n> = 컷오프;
  };
};

JavaScript의 화살표는 대략 "기능"을 의미합니다. 사용하는 함수 종류 (=> 자체가없고 생성자로 사용할 수 없음)에 따라 함수 동작에 몇 가지 중요한 차이점이 있지만, 거기에 도달하면 그 차이에 도달하게됩니다. 지금, x => x가 보이면 "x를 가져 와서 x를 반환하는 함수"라고 생각하십시오. 따라서 const highpass = cutoff => n => n> = cutoff;를 읽을 수 있습니다. 같이:

"고역 통과 (highpass)는 컷오프를 수행하고 n을 취하여 n> = 컷오프의 결과를 반환하는 함수를 반환하는 함수입니다."

highpass ()는 함수를 반환하므로이 함수를 사용하여보다 전문화 된 함수를 만들 수 있습니다.

const gt4 = 고역 통과 (4);
gt4 (6); // 참
gt4 (3); // 거짓

Autocurry를 사용하면 기능을 자동으로 카레 할 수있어 유연성이 극대화됩니다. add3 () 함수가 있다고 가정 해보십시오.

const add3 = curry ((a, b, c) => a + b + c);

autocurry를 사용하면 여러 가지 방법으로 사용할 수 있으며 전달하는 인수 수에 따라 올바른 것을 반환합니다.

add3 (1, 2, 3); // 6
add3 (1, 2) (3); // 6
add3 (1) (2, 3); // 6
add3 (1) (2) (3); // 6

죄송합니다. Haskell 팬들에게는 JavaScript에 내장 된 자동 카레 메커니즘이 없지만 Lodash에서 가져올 수 있습니다.

npm 설치-lodash 저장

그런 다음 모듈에서 :

'lodash / curry'에서 수입 카레;

또는 다음 마법 주문을 사용할 수 있습니다.

// 작은 재귀 자동 카레
const 카레 = (
  f, arr = []
) => (... args) => (
  a => a.length === f.length?
    f (... a) :
    카레 (f, a)
) ([... arr, ... args]);

기능 구성

물론 기능을 구성 할 수 있습니다. 함수 구성은 한 함수의 반환 값을 인수로 다른 함수에 전달하는 프로세스입니다. 수학 표기법에서 :

f. 지

다음은 JavaScript로 해석됩니다.

f (g (x))

내부에서 다음과 같이 평가됩니다.

  1. x가 평가된다
  2. g ()는 x에 적용됩니다
  3. f ()는 g (x)의 반환 값에 적용됩니다

예를 들면 다음과 같습니다.

const inc = n => n + 1;
inc (double (2)); // 5

값 2는 double ()로 전달되어 4를 생성합니다. 4는 5로 평가되는 inc ()로 전달됩니다.

모든 표현식을 함수의 인수로 전달할 수 있습니다. 함수가 적용되기 전에식이 평가됩니다.

inc (double (2) * double (2)); // 17

double (2)는 4로 평가되므로 inc (16)로 평가되는 inc (4 * 4)로 읽은 다음 17로 평가할 수 있습니다.

기능 구성은 기능 프로그래밍의 핵심입니다. 나중에 더 자세히 설명하겠습니다.

배열

배열에는 내장 메소드가 있습니다. 메소드는 객체와 연관된 함수입니다. 일반적으로 연관된 객체의 속성입니다.

const arr = [1, 2, 3];
arr.map (더블); // [2, 4, 6]

이 경우 arr은 객체이고 .map ()은 값에 대한 함수가있는 객체의 속성입니다. 이 함수를 호출하면 함수는 인수에 적용되며이 매개 변수는 메소드 호출시 자동으로 설정되는 특수 매개 변수입니다. 이 값은 .map ()이 배열의 내용에 액세스하는 방법입니다.

우리는 double 함수를 호출하지 않고 값으로 map에 전달합니다. map은 함수를 인수로 받아서 배열의 각 항목에 적용하기 때문입니다. double ()에서 반환 한 값이 포함 된 새 배열을 반환합니다.

원래 arr 값은 변경되지 않습니다.

arr; // [1, 2, 3]

메소드 체인

메소드 호출을 연결할 수도 있습니다. 메소드 체인은 이름으로 리턴 값을 참조 할 필요없이 함수의 리턴 값에 대해 메소드를 직접 호출하는 프로세스입니다.

const arr = [1, 2, 3];
arr.map (double) .map (double); // [4, 8, 12]

술어는 부울 값 (true 또는 false)을 리턴하는 함수입니다. .filter () 메소드는 술어를 가져 와서 새리스트를 리턴하고, 술어를 전달하는 항목 만 선택하여 (새로 리턴) 새리스트에 포함시킵니다.

[2, 4, 6]. 필터 (gt4); // [4, 6]

종종 목록에서 항목을 선택한 다음 해당 항목을 새 목록에 매핑하려고합니다.

[2, 4, 6] .filter (gt4) .map (double); [8, 12]

참고 :이 텍스트의 뒷부분에서 변환기라는 것을 사용하여 동시에 선택하고 매핑하는보다 효율적인 방법을 볼 수 있지만 먼저 다른 방법을 탐색해야합니다.

결론

지금 머리가 돌고 있어도 걱정하지 마십시오. 우리는 더 많은 탐구와 배려가 필요한 많은 것들의 표면을 거의 긁지 않았습니다. 우리는 곧 돌아와서 이러한 주제 중 일부를 훨씬 더 깊이있게 살펴볼 것입니다.

도서 구매 | 색인 | <이전 | 다음>

EricElliottJS.com에서 자세히 알아보기

EricElliottJS.com 회원은 대화식 코드 문제가있는 비디오 강의를 이용할 수 있습니다. 회원이 아닌 경우 오늘 가입하십시오.

Eric Elliott는 분산 시스템 전문가이며“Composing Software”및“JavaScript Applications 프로그래밍”책의 저자입니다. DevAnywhere.io의 공동 창립자로서 개발자에게 원격으로 작업하고 업무 / 생활 균형을 수용하는 데 필요한 기술을 가르칩니다. 그는 암호화 프로젝트를위한 개발 팀을 구성하고 조언하며, Adobe Systems, Zumba Fitness, Wall Street Journal, ESPN, BBC 및 Usher, Frank Ocean, Metallica 등 최고의 레코딩 아티스트를위한 소프트웨어 경험에 기여했습니다.

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