|>
) (이 문서에서는 주제 참조에 대한 자리 표시자 토큰으로 %
사용합니다. 이것이 최종 선택이 아닐 가능성이 거의 확실 합니다. 자세한 내용은 토큰 bikeshed 토론을 참조하세요.)
State of JS 2020 설문조사에서 "현재 JavaScript에서 무엇이 빠져 있다고 생각하시나요?"에 대한 네 번째 상위 답변입니다 . 파이프 운영자 였습니다. 왜?
JavaScript의 값 에 대해 연속 작업 (예: 함수 호출)을 수행할 때 현재 두 가지 기본 스타일이 있습니다.
즉, three(two(one(value)))
대 value.one().two().three()
입니다. 그러나 이러한 스타일은 가독성, 유창성 및 적용 가능성에서 크게 다릅니다.
첫 번째 스타일인 중첩 은 일반적으로 적용 가능합니다. 이는 함수 호출, 산술, 배열/객체 리터럴, await
및 yield
등 모든 일련의 작업에 작동합니다.
그러나 중첩이 깊어지면 읽기가 어렵 습니다. 실행 흐름은 일반 코드를 왼쪽 에서 오른쪽으로 읽는 것이 아니라 오른쪽에서 왼쪽으로 이동합니다. 일부 수준에 여러 개의 인수가 있는 경우 읽기도 앞뒤로 튕겨 나옵니다. 즉, 우리의 눈은 함수 이름을 찾으려면 왼쪽으로 점프 해야 하고, 추가 인수를 찾으려면 오른쪽으로 점프 해야 합니다. 또한 나중에 코드를 편집하는 것은 어려울 수 있습니다. 중첩된 많은 괄호 중에서 새 인수를 삽입할 올바른 위치를 찾아야 합니다.
React의 실제 코드를 생각해 보세요.
console . log (
chalk . dim (
`$ ${ Object . keys ( envars )
. map ( envar =>
` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' )
} ` ,
'node' ,
args . join ( ' ' ) ) ) ;
이 실제 코드는 깊게 중첩된 표현식 으로 구성됩니다. 데이터의 흐름을 읽으려면 인간의 눈이 먼저 다음을 수행해야 합니다.
초기 데이터 (가장 안쪽 표현식, envars
)를 찾습니다.
그런 다음 각 데이터 변환에 대해 안쪽에서 바깥쪽으로 반복적으로 스캔합니다. 각 변환은 쉽게 놓칠 수 있는 왼쪽의 접두사 연산자 또는 오른쪽의 접미사 연산자입니다.
Object.keys()
(왼쪽),.map()
(오른쪽),.join()
(오른쪽),chalk.dim()
(왼쪽), 그런 다음console.log()
(왼쪽).많은 표현식(일부는 접두사 연산자를 사용하고 일부는 후위 연산자를 사용하며 일부는 절환 연산자를 사용함)을 깊게 중첩한 결과 각 표현식 의 선두를 찾으려면 왼쪽과 오른쪽을 모두 확인해야 합니다.
두 번째 스타일인 메소드 체이닝 은 값에 해당 클래스의 메소드 로 지정된 함수가 있는 경우 에만 사용할 수 있습니다. 이는 적용 가능성을 제한합니다 . 그러나 적용 하면 접미사 구조 덕분에 일반적으로 더 사용하기 쉽고 읽고 쓰기가 더 쉽습니다 . 코드 실행은 왼쪽에서 오른쪽으로 흐릅니다. 깊게 중첩된 표현들이 엉키지 않게 풀 립니다. 함수 호출에 대한 모든 인수는 함수 이름으로 그룹화 됩니다. 나중에 더 많은 메서드 호출을 삽입하거나 삭제하기 위해 코드를 편집하는 것은 간단합니다. 왜냐하면 커서를 한 곳에 놓은 다음 연속된 문자를 하나 입력하거나 삭제하기만 하면 되기 때문입니다.
실제로, 메소드 체이닝의 이점은 너무 매력적 이어서 일부 인기 있는 라이브러리는 더 많은 메소드 체이닝을 허용하기 위해 코드 구조를 특별히 왜곡합니다. 가장 눈에 띄는 예는 여전히 세계에서 가장 인기 있는 JS 라이브러리 로 남아 있는 jQuery 입니다. jQuery의 핵심 디자인은 수십 개의 메소드가 포함된 단일 객체로, 모두 동일한 객체 유형을 반환하므로 계속해서 연결할 수 있습니다. 이러한 프로그래밍 스타일에는 Fluent Interfaces 라는 이름도 있습니다.
안타깝게도 메소드 체이닝 만으로는 JavaScript의 다른 구문 (함수 호출, 산술, 배열/객체 리터럴, await
및 yield
등)을 수용할 수 없습니다. 이런 식으로 메소드 체이닝의 적용 가능성 은 여전히 제한적 입니다.
파이프 연산자는 메소드 체인 의 편리함 과 용이함을 표현식 중첩 의 폭넓은 적용 가능성 과 결합하려고 시도합니다.
모든 파이프 연산자의 일반적인 구조는 value |>
e1 |>
e2 |>
e3 입니다. 여기서 e1 , e2 , e3 은 모두 연속된 값을 매개변수로 사용하는 표현식입니다. 그런 다음 |>
연산자는 왼쪽에서 오른쪽으로 value
"파이프"하기 위해 어느 정도 마법을 수행합니다.
React에서 깊게 중첩된 실제 코드를 이어가면 다음과 같습니다.
console . log (
chalk . dim (
`$ ${ Object . keys ( envars )
. map ( envar =>
` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' )
} ` ,
'node' ,
args . join ( ' ' ) ) ) ;
...파이프 연산자와 이전 작업 값을 대신하는 자리 표시자 토큰( %
)을 사용하여 이를 풀 수 있습니다.
Object . keys ( envars )
. map ( envar => ` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' )
| > `$ ${ % } `
| > chalk . dim ( % , 'node' , args . join ( ' ' ) )
| > console . log ( % ) ;
이제 인간 독자는 초기 데이터 (가장 안쪽에 있는 표현인 envars
)를 신속하게 찾은 다음 데이터의 각 변환을 왼쪽에서 오른쪽으로 선형적으로 읽을 수 있습니다.
임시 변수를 사용하는 것이 깊게 중첩된 코드를 풀 수 있는 유일한 방법이라고 주장할 수도 있습니다. 모든 단계의 변수 이름을 명시적으로 지정하면 메서드 체이닝과 유사한 일이 발생하며 코드를 읽고 쓰는 것과 유사한 이점을 얻을 수 있습니다.
예를 들어 이전에 React에서 수정된 실제 예제를 사용하면 다음과 같습니다.
Object . keys ( envars )
. map ( envar => ` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' )
| > `$ ${ % } `
| > chalk . dim ( % , 'node' , args . join ( ' ' ) )
| > console . log ( % ) ;
...임시 변수를 사용하는 버전은 다음과 같습니다.
const envarString = Object . keys ( envars )
. map ( envar => ` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' ) ;
const consoleText = `$ ${ envarString } ` ;
const coloredConsoleText = chalk . dim ( consoleText , 'node' , args . join ( ' ' ) ) ;
console . log ( coloredConsoleText ) ;
하지만 현실 세계에서 임시 변수의 줄이 아닌 서로의 코드에서 항상 깊이 중첩된 표현식을 만나는 이유가 있습니다. 그리고 jQuery, Mocha 등의 메소드 체인 기반 유창한 인터페이스가 여전히 인기 있는 이유가 있습니다.
일련의 긴 임시 일회용 변수를 사용하여 코드를 작성하는 것은 종종 너무 지루하고 장황합니다 . 인간이 읽기 에는 지루하고 시각적으로 시끄러울 수도 있습니다.
이름 지정 이 프로그래밍에서 가장 어려운 작업 중 하나라면 프로그래머는 변수의 이점이 상대적으로 작다고 인식하면 필연적으로 변수 이름 지정을 피할 것입니다.
짧은 이름을 가진 단일 변경 가능 변수를 사용하면 임시 변수의 장황함이 줄어들어 파이프 연산자와 비슷한 결과를 얻을 수 있다고 주장할 수도 있습니다.
예를 들어 이전에 React에서 수정한 실제 예제를 다음과 같이 다시 작성할 수 있습니다.
let _ ;
_ = Object . keys ( envars )
. map ( envar => ` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' ) ;
_ = `$ ${ _ } ` ;
_ = chalk . dim ( _ , 'node' , args . join ( ' ' ) ) ;
_ = console . log ( _ ) ;
그러나 이와 같은 코드는 실제 코드에서는 일반적이지 않습니다 . 그 이유 중 하나는 변경 가능한 변수가 예기치 않게 변경되어 찾기 어려운 조용한 버그가 발생할 수 있기 때문입니다. 예를 들어 변수가 클로저에서 실수로 참조될 수 있습니다. 또는 표현식 내에서 실수로 재할당될 수도 있습니다.
// setup
function one ( ) { return 1 ; }
function double ( x ) { return x * 2 ; }
let _ ;
_ = one ( ) ; // _ is now 1.
_ = double ( _ ) ; // _ is now 2.
_ = Promise . resolve ( ) . then ( ( ) =>
// This does *not* print 2!
// It prints 1, because `_` is reassigned downstream.
console . log ( _ ) ) ;
// _ becomes 1 before the promise callback.
_ = one ( _ ) ;
이 문제는 파이프 연산자에서는 발생하지 않습니다. 토픽 토큰은 재할당할 수 없으며 각 단계 외부의 코드는 해당 바인딩을 변경할 수 없습니다.
let _ ;
_ = one ( )
| > double ( % )
| > Promise . resolve ( ) . then ( ( ) =>
// This prints 2, as intended.
console . log ( % ) ) ;
_ = one ( ) ;
이러한 이유로 변경 가능한 변수가 있는 코드는 읽기가 더 어렵습니다. 특정 지점에서 변수가 무엇을 나타내는지 확인하려면 이전 범위 전체 에서 해당 변수가 다시 할당된 위치를 검색해야 합니다.
반면에 파이프라인의 주제 참조는 어휘 범위가 제한되어 있으며 바인딩은 해당 범위 내에서 변경할 수 없습니다. 실수로 재할당될 수 없으며 클로저에서 안전하게 사용할 수 있습니다.
각 파이프라인 단계마다 주제 값도 변경되지만 이를 이해하기 위해 파이프라인의 이전 단계만 스캔하므로 읽기 쉬운 코드가 생성됩니다.
일련의 할당문(변경 가능하거나 변경 불가능한 임시 변수 포함)에 비해 파이프 연산자의 또 다른 이점은 표현식 이라는 점입니다.
파이프 표현식은 직접 반환되거나, 변수에 할당되거나, JSX 표현식과 같은 컨텍스트에서 사용될 수 있는 표현식입니다.
반면에 임시 변수를 사용하려면 일련의 명령문이 필요합니다.
파이프라인 | 임시 변수 |
---|---|
const envVarFormat = vars =>
Object . keys ( vars )
. map ( var => ` ${ var } = ${ vars [ var ] } ` )
. join ( ' ' )
| > chalk . dim ( % , 'node' , args . join ( ' ' ) ) ; | const envVarFormat = ( vars ) => {
let _ = Object . keys ( vars ) ;
_ = _ . map ( var => ` ${ var } = ${ vars [ var ] } ` ) ;
_ = _ . join ( ' ' ) ;
return chalk . dim ( _ , 'node' , args . join ( ' ' ) ) ;
} |
// This example uses JSX.
return (
< ul >
{
values
| > Object . keys ( % )
| > [ ... Array . from ( new Set ( % ) ) ]
| > % . map ( envar => (
< li onClick = {
( ) => doStuff ( values )
} > { envar } < / li >
) )
}
< / ul >
) ; | // This example uses JSX.
let _ = values ;
_ = Object . keys ( _ ) ;
_ = [ ... Array . from ( new Set ( _ ) ) ] ;
_ = _ . map ( envar => (
< li onClick = {
( ) => doStuff ( values )
} > { envar } < / li >
) ) ;
return (
< ul > { _ } < / ul >
) ; |
파이프 운영자를 위한 두 가지 경쟁 제안이 있었습니다: 해킹 파이프(Hack Pipe)와 F# 파이프. (그 전에는 처음 두 제안의 "스마트 혼합"에 대한 세 번째 제안이 있었지만 해당 구문이 제안 중 하나의 상위 집합이기 때문에 철회되었습니다.)
두 파이프 제안은 |>
사용할 때 코드 철자를 지정할 때 "마법"이 무엇인지에 대해 약간 다릅니다.
두 제안 모두 기존 언어 개념을 재사용합니다 . 핵 파이프는 표현식 개념을 기반으로 하는 반면 F# 파이프는 단항 함수 개념을 기반으로 합니다.
파이핑 표현식 과 파이핑 단항 함수는 그에 따라 작고 거의 대칭적인 트레이드오프를 갖습니다.
Hack 언어 의 파이프 구문에서 파이프의 오른쪽은 왼쪽 표현식을 평가한 결과에 바인딩된 자리 표시자와 함께 평가되는 특수 자리 표시자를 포함하는 표현식 입니다. 즉, 세 가지 함수를 통해 value |> one(%) |> two(%) |> three(%)
value
로 작성합니다.
장점: 오른쪽은 모든 표현식이 될 수 있고 자리 표시자는 일반 변수 식별자가 갈 수 있는 어디든 갈 수 있으므로 특별한 규칙 없이 원하는 코드로 파이프할 수 있습니다.
value |> foo(%)
,value |> foo(1, %)
,value |> %.foo()
,value |> % + 1
,value |> [%, 0]
,value |> {foo: %}
,value |> `${%}`
,value |> new Foo(%)
,value |> await %
.value |> (yield %)
,value |> import(%)
함수형 키워드 호출용, 단점: 단항 함수를 통한 파이핑은 F# 파이프보다 Hack 파이프에서 약간 더 장황합니다 . 여기에는 Ramda와 같은 함수 처리 라이브러리 에서 생성된 단항 함수뿐만 아니라 해당 인수에 대해 복잡한 구조 분해를 수행하는 단항 화살표 함수도 포함됩니다. 해킹 파이프는 명시적인 함수 호출 접미사 (%)
를 사용하면 약간 더 장황해집니다.
(파이프 본문 내에서 변수 할당/구조 분해를 수행할 수 있으므로 표현식이 진행되면 토픽 값의 복잡한 구조 분해가 더 쉬워집니다.)
F# 언어 의 파이프 구문에서 파이프의 오른쪽은 단항 함수로 평가 되어야 하는 표현식이며, 그런 다음 왼쪽의 값을 유일한 인수 로 사용하여 암묵적으로 호출 됩니다. 즉, 세 가지 함수를 통해 value
파이프하기 위해 value |> one |> two |> three
씁니다. left |> right
right(left)
가 됩니다. 이를 암묵적 프로그래밍 또는 포인트 프리 스타일이라고 합니다.
예를 들어 이전에 React에서 수정된 실제 예제를 사용하면 다음과 같습니다.
Object . keys ( envars )
. map ( envar => ` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' )
| > `$ ${ % } `
| > chalk . dim ( % , 'node' , args . join ( ' ' ) )
| > console . log ( % ) ;
...Hack 파이프 대신 F# 파이프를 사용하는 버전은 다음과 같습니다.
Object . keys ( envars )
. map ( envar => ` ${ envar } = ${ envars [ envar ] } ` )
. join ( ' ' )
| > x => `$ ${ x } `
| > x => chalk . dim ( x , 'node' , args . join ( ' ' ) )
| > console . log ;
장점: 오른쪽이 단항 함수로 해결 되어야 한다는 제한으로 인해 수행하려는 작업이 단항 함수 호출 인 경우 매우 간결한 파이프를 작성할 수 있습니다.
value |> foo
. 여기에는 Ramda와 같은 함수 처리 라이브러리 에서 생성된 단항 함수와 해당 인수에 대해 복잡한 구조 분해를 수행하는 단항 화살표 함수가 포함됩니다. F# 파이프는 암시적 함수 호출(no (%)
)을 사용하면 약간 덜 장황 해집니다.
단점: 제한 사항은 다른 구문 으로 수행되는 모든 작업을 단항 화살표 함수 로 래핑 하여 약간 더 장황 하게 만들어야 함을 의미합니다.
value |> x=> x.foo()
,value |> x=> x + 1
,value |> x=> [x, 0]
,value |> x=> ({foo: x})
객체 리터럴의 경우,value |> x=> `${x}`
,value |> x=> new Foo(x)
,value |> x=> import(x)
함수형 키워드 호출용,명명된 함수를 호출하는 경우에도 두 개 이상의 인수를 전달해야 할 경우 래핑이 필요합니다.
value |> x=> foo(1, x)
. 단점: await
및 yield
작업은 포함하는 함수 로 범위가 지정 되므로 단항 함수만으로는 처리할 수 없습니다 . 이를 파이프 표현식에 통합하려면 await
및 yield
특수 구문 사례 로 처리해야 합니다.
value |> await
,value |> yield
. Hack 파이프와 F# 파이프는 각각 서로 다른 표현식에 약간의 구문 세금을 부과합니다.
해킹 파이프에는 단항 함수 호출 에만 약간의 세금이 부과됩니다.
F# 파이프는 단항 함수 호출을 제외한 모든 표현식에 약간의 부담을 줍니다.
두 제안 모두 에서 과세 표현당 구문 세금은 작습니다 ( 둘 다 (%)
및 x=>
는 3자에 불과 합니다). 그러나 세금에는 각각의 과세 표현의 보급률 이 곱해집니다 . 따라서 덜 일반적인 표현에 세금을 부과하고 더 일반적인 표현을 선호하여 최적화하는 것이 합리적일 수 있습니다.
단항 함수 호출은 일반적으로 단항 함수를 제외한 모든 표현식보다 덜 일반적 입니다. 특히 메소드 호출과 n-ary 함수 호출이 항상 인기가 있습니다. 일반적인 빈도에서 단항 함수 호출은 배열 리터럴 , 객체 리터럴 및 산술 연산 과 같은 다른 유비쿼터스 구문은 물론 이 두 가지 경우와 같거나 그 이상입니다. 이 설명에는 이러한 보급률의 차이에 대한 몇 가지 실제 사례가 포함되어 있습니다.
게다가 확장 호출 , do 표현식 , 레코드/튜플 리터럴 과 같은 몇 가지 다른 제안된 새로운 구문 도 미래 에 널리 보급 될 가능성이 높습니다. 마찬가지로, TC39가 연산자 오버로딩을 표준화한다면 산술 연산도 더욱 일반화 될 것입니다. 이러한 미래 구문의 표현을 풀면 F# 파이프에 비해 Hack 파이프가 더 유창해집니다.
단항 함수 호출(즉, 오른쪽의 단항 함수를 호출하는 (%)
)에 대한 Hack 파이프의 구문 세금은 특별한 경우가 아닙니다 . 파이프 없이 일반적으로 하는 방식 으로 단순히 일반 코드를 명시적으로 작성하는 것입니다.
반면, F# 파이프에서는 "단항 함수로 확인되는 코드"와 "다른 표현식" 을 구별 해야 하며 후자의 경우에는 화살표 함수 래퍼를 추가해야 한다는 점을 기억해야 합니다.
예를 들어 Hack Pipe의 경우 value |> someFunction + 1
은 잘못된 구문 이므로 조기에 실패합니다 . someFunction + 1
단항 함수로 평가되지 않는다는 점을 인식할 필요가 없습니다. 그러나 F# 파이프의 경우 value |> someFunction + 1
은 여전히 유효한 구문 입니다. someFunction + 1
호출할 수 없기 때문에 런타임 에 늦게 실패합니다 .
파이프 챔피언 그룹은 Stage 2용 F# 파이프를 TC39에 두 번 선보였습니다. 두 번 모두 2단계 진출에 실패 했다. 두 F# 파이프(및 PFA(부분 기능 응용 프로그램)) 모두 다양한 우려로 인해 다른 여러 TC39 대표자들로부터 강력한 반발을 받았습니다. 여기에는 다음이 포함됩니다.
await
에 대한 구문 문제.이번 반발은 파이프 챔피언 그룹 외부 에서 발생했습니다. 자세한 내용은 HISTORY.md를 참조하세요.
명명된 변수에 의존하지 않고 깊이 중첩된 표현식을 쉽게 선형화하려면 파이프 연산자가 없는 것보다 낫다는 것이 파이프 챔피언 그룹의 믿음입니다. 챔피언 그룹의 많은 구성원은 Hack 파이프가 F# 파이프보다 약간 낫다고 믿고 있으며, 챔피언 그룹의 일부 구성원은 F# 파이프가 Hack 파이프보다 약간 낫다고 믿습니다. 그러나 챔피언 그룹의 모든 사람들은 F# 파이프가 가까운 미래에 TC39를 통과하기에는 너무 많은 저항에 직면했다는 데 동의합니다.
강조하자면 Hack 파이프에서 F# 파이프로 다시 전환하려고 시도하면 TC39가 어떤 파이프에도 전혀 동의하지 않을 가능성이 높습니다. PFA 구문은 마찬가지로 TC39에서 힘든 싸움에 직면해 있습니다(HISTORY.md 참조). 파이프 챔피언 그룹의 많은 구성원들은 이것이 불행한 일이라고 생각하고 나중에 F# 파이프 분할 믹스와 PFA 구문을 위해 다시 싸울 의향이 있습니다. 그러나 파이프 챔피언 그룹 외부에는 일반적으로 Hack Pipe와 관계없이 암묵적 프로그래밍(및 PFA 구문) 장려에 반대하는 대표자(브라우저 엔진 구현자 포함)가 꽤 있습니다.
(공식 초안 사양이 제공됩니다.)
주제 참조 %
널 연산자 입니다. 이는 주제 값 에 대한 자리 표시자 역할을 하며 어휘 범위가 지정 되고 변경할 수 없습니다 .
%
최종 선택이 아닙니다 (주제 참조에 대한 정확한 토큰은 최종이 아닙니다 . %
대신 ^
또는 다른 많은 토큰일 수 있습니다. 3단계로 진행하기 전에 사용할 실제 토큰을 정할 계획입니다. 그러나 %
구문상 문제가 가장 적은 것으로 보입니다. 이는 또한 printf 형식 문자열 및 Clojure 의 #(%)
함수 리터럴 의 자리 표시자와 유사합니다.)
파이프 연산자 |>
파이프 표현식 ( 파이프라인 이라고도 함)을 형성하는 중위 연산자 입니다. 왼쪽( 파이프 헤드 또는 파이프 입력 )을 평가하고 결과 값( 주제 값 )을 주제 참조 에 불변적으로 바인딩한 다음 해당 바인딩을 사용하여 오른쪽( 파이프 본문 )을 평가합니다. 오른쪽의 결과 값은 전체 파이프 표현식의 최종 값( 파이프 출력 )이 됩니다.
파이프 연산자의 우선순위는 다음과 같습니다 .
=>
;=
, +=
등;yield
및 yield *
; 쉼표 연산자보다 더 엄격합니다 ,
다른 모든 연산자보다 느슨 합니다.
예를 들어 v => v |> % == null |> foo(%, 0)
v => (v |> (% == null) |> foo(%, 0))
으로 그룹화합니다.
이는 v => foo(v == null, 0)
과 동일합니다.
파이프 본문은 해당 주제 값을 최소한 한 번 사용해야 합니다 . 예를 들어 value |> foo + 1
본문에 주제 참조가 포함되어 있지 않기 때문에 잘못된 구문 입니다. 이 디자인은 파이프 표현식의 본문에서 주제 참조를 생략하는 것이 거의 확실하게 우발적인 프로그래머 오류이기 때문입니다.
마찬가지로 주제 참조는 파이프 본문에 포함되어야 합니다 . 파이프 본문 외부에서 주제 참조를 사용하는 것도 잘못된 구문 입니다.
혼란스러운 그룹화를 방지하기 위해 비슷한 우선 순위를 갖는 다른 연산자(예: 화살표 =>
, 삼항 조건 연산자 ?
:
, 할당 연산자 및 yield
연산자)를 파이프 헤드 또는 본문 으로 사용하는 것은 유효하지 않은 구문입니다. 이러한 연산자와 함께 |>
사용할 때 괄호를 사용하여 어떤 그룹화가 올바른지 명시적으로 나타내야 합니다. 예를 들어, a |> b ? % : c |> %.d
잘못된 구문입니다. a |> (b ? % : c) |> %.d
또는 a |> (b ? % : c |> %.d)
로 수정되어야 합니다.
마지막으로, 동적으로 컴파일된 코드(예: eval
또는 new Function
사용) 내의 주제 바인딩은 해당 코드 외부 에서 사용할 수 없습니다 . 예를 들어, v |> eval('% + 1')
eval
표현식이 런타임에 평가될 때 구문 오류를 발생시킵니다.
다른 특별한 규칙은 없습니다 .
이러한 규칙 의 자연스러운 결과는 파이프로 전달되는 데이터를 수정하지 않고 파이프 표현식 체인 중간에 부작용을 삽입해야 하는 경우 value |> (sideEffect(), %)
. 평소와 같이 쉼표 표현식은 오른쪽 %
로 평가되어 기본적으로 주제 값을 수정하지 않고 전달합니다. 이는 빠른 디버깅에 특히 유용합니다: value |> (console.log(%), %)
.
원래 예제의 유일한 변경 사항은 들여쓰기 및 주석 제거였습니다.
jquery/build/tasks/sourceMap.js에서:
// Status quo
var minLoc = Object . keys ( grunt . config ( "uglify.all.files" ) ) [ 0 ] ;
// With pipes
var minLoc = grunt . config ( 'uglify.all.files' ) | > Object . keys ( % ) [ 0 ] ;
node/deps/npm/lib/unpublish.js에서:
// Status quo
const json = await npmFetch . json ( npa ( pkgs [ 0 ] ) . escapedName , opts ) ;
// With pipes
const json = pkgs [ 0 ] | > npa ( % ) . escapedName | > await npmFetch . json ( % , opts ) ;
underscore.js에서:
// Status quo
return filter ( obj , negate ( cb ( predicate ) ) , context ) ;
// With pipes
return cb ( predicate ) | > _ . negate ( % ) | > _ . filter ( obj , % , context ) ;
ramda.js에서.