서문에
(이 글은 원래 지난주 콘텐츠였는데, 어제 쓰는 것을 잊어버렸습니다. 몰래 보충합니다)
최근 일이 조금 많아, sort 는 통과했지만, 여전히 약간 분주합니다. 하지만不管怎样, 계획은 거기에 있고, 최종적으로는 하나씩 완료될 것입니다
쓸데없는 말은 여기까지 하고, 어머니의 몸이 빨리 좋아지기를 바랍니다~
##一.화살표 함수简介
화살표 함수 (arrow function) 는 C# 의 lambda 표현식입니다. Java8 도 그것을 호화로운 점심에 추가했다고 합니다. 하지만不管怎样, JS 는 다른 언어로부터 우수한 특성을 흡수하고 있습니다 (예를 들어 yield, class, 기본 파라미터 등). 이러한 특성의 좋고 나쁨은 묻지 않고, 이 일 자체가 매우 좋은 것입니다 (적어도 우리가 사용하고 있는 것은 활력에 찬 도구입니다)
Java 는 -> 화살표를 사용하고, C# 이 사용하는 화살표는 JS 와 같습니다: =>. 이 화살표는 "lambda 연산자"라고 불리며, 업계 용어로는 "goes to"라고 읽습니다
lambda 표현식 (화살표 함수) 은 함수를 정의하는 가장 간결한 방법이라고 합니다. 구문상 거의 중복된 성분이 없습니다. JS 의 약타입 특징 때문에, JS 중의 lambda 표현식은 C# 이나 Java 중의 것보다 더욱 간결합니다 (파라미터 타입 선언이 적음)
한마디로, 화살표 함수는 lambda 표현식이며, 더 간결한 function 정의 방식을 제공합니다
##二.구문
arg => returnVal 구문은 함수를 생성하는 가장 간결한 방식으로, 형参가 arg, 반환값이 returnVal 인 function 을 정의합니다
다른 구문은 아래 표와 같습니다:
| 구문 | 등가 코드 | 의미 |
|---|---|---|
x => f(x) | function(x) {
return f(x);
} | y=f(x) |
(x, y)=>x + y; | function(x, y) {
return x + y;
} | y=f(x,y)=x+y |
(x, y)=>{g(x); g(y); return h(x, y);}; | function(x, y) {
g(x);
g(y);
return h(x, y);
} | g(x), g(y) y=f(x,y)==============h(x,y) |
()=>({}); | function() {
return {};
} | y={}
|
P.S. 세 번째 열의 "의미" 는 수학 함수 의미를 가리키며, lambda 표현식은 원래 수학자가 만들어낸 것입니다
간단한 예는 다음과 같습니다:
// 간단한 예, 익명 함수 정의 간소화
var arr = [1, 3, 21, 12];
console.log(arr.map(x => 2 * x)); // [2, 6, 42, 24]
console.log(arr.sort((a, b) => a - b)); // [1, 3, 12, 21]
arr.forEach((item, index, arr) => {
if (index %2 == 0) {
console.log(item);
}
if (index == arr.length - 1) {
console.log(`last item is ${item}`);
}
});
복잡한 예:
// 복잡한 예
var app = {
cache: {},
ajax: function(url, callback) {
var self = this;
function req(url) {
var res = `data from ${url}`;
console.log(`ajax request ${url}`);
// cache res
self.cache[url] = res;
return res;
}
var data = req(url);
callback(data);
}
}
app.ajax('http://www.xxx.xx', function(data) {
console.log(`receive: ${data}`);
});
console.log(app.cache);
화살표 함수로 위 예를 개작:
// 화살표 함수로 개작
var es6App = {
cache: {},
ajax(url, callback) {
var req = url => {
var res = `data from ${url}`;
console.log(`ajax request ${url}`);
// cache res
this.cache[url] = res;
return res;
}
var data = req(url);
callback(data);
}
}
es6App.ajax('http://www.q.xx', function(data) {
console.log(`receive: ${data}`);
});
console.log(es6App.cache);
that = this 라는 필요한 쓸데없는 말을 제거했습니다. 실제로는 한 항목의 원칙을 준수하면 모든 that = this 를 제거할 수 있습니다.下文의 주의사항 중의 3.this 에 대하여 참조
##三.특징 및 주의사항
###1.파라미터 리스트와 반환값의 구문
1 개의 파라미터일 때, 왼쪽에 직접 파라미터 이름을 쓰고, 0 개 또는 여러 개의 파라미터일 때, 파라미터 리스트는 () 로 감싸야 합니다
함수 본체가 1 조의 문만일 때, 오른쪽 값은 자동으로 함수 반환값이 되고, 함수 본체가 1 조를 초과하는 문일 때, 함수 본체는 {} 로 감싸야 하며,かつ 수동으로 return 해야 합니다
P.S. 물론, 쉽게 생각할 수 있는 것은 청홍조백을 가리지 않고,() => {} 를 화살표 함수의起手式으로 하는 것이지만, 이렇게 하는 것은 권장하지 않습니다. 왜냐하면 다음 조에서 { 는 모호하다고述べた通り, 번거로움을 가져올 수 있기 때문입니다
###2.모호한 문자
{ 는유일1 개의 모호한 문자이므로, 오브젝트 리터럴을 반환할 때는 () 로 감싸야 합니다.否则블록 문으로 해석됩니다
예를 들어:
var f1 = () => {};
f1(); // undefined 를 반환
// 等価于
// var f1 = function() {};
var f2 = () => ({});
f2(); // 빈 오브젝트{}를 반환
// 等価于
// var f2 = function() {return {};};
###3.this 에 대하여
화살표 함수는외위 작용역에서 this 를 상속합니다. that = this 를 피하기 위해, 준수해야 합니다: 오브젝트 상의 직접 함수 속성값은 function 구문을 사용하는 외, 다른 함수는 모두 화살표 함수를 사용
이 규칙은 쉽게 이해할 수 있습니다. 예는 다음과 같습니다:
// 장면 1
function MyType() {}
MyType.prototype.fn = function() {/*화살표 함수 정의*/}; // 화살표 함수 중의 this 는 MyType 타입 인스턴스를 가리킴
// 장면 2
var obj = {};
obj.fn = function() {/*화살표 함수 정의*/}; // 화살표 함수 중의 this 는 obj 를 가리킴
차이는 function 키워드로 정의된 함수 속성 중에서, 해당 함수의 this 는 이 함수 속성이 소속된 오브젝트를 가리킵니다 (익명 함수의 this 는 global 오브젝트 또는 undefined 를 가리킴).说白了, function 은 새로운 this 를 정의할 수 있고, 화살표 함수는 할 수 없습니다. 그것은 외층에서 1 개의 this 를 빌릴 수밖에 없습니다. 따라서, 새로운 this 가 나타날 필요가 있을 때는 function 으로 함수를 정의하고, 외층의 this 를沿用하고 싶을 때는 화살표 함수를 사용합니다
###4.arguments 오브젝트에 대하여
화살표 함수에는 arguments 오브젝트가 없습니다. 왜냐하면 표준은 [기본 파라미터, 가변 파라미터](/articles/기본 파라미터와 부정 파라미터-es6 노트 4/), [파라미터 해체](/articles/destructuring(해체 대입)-es6 노트 5/) 의 사용을 장려하고 있기 때문입니다
예를 들어:
// 일반 함수
(function(a) {console.log(`a = ${a}, arguments[0] = ${arguments[0]}`)})(1);
// log print: a = 1, arguments[0] = 1
// 위와 등가인 화살표 함수
(a => console.log(`a = ${a}, arguments[0] = ${arguments[0]}`))(1);
// log print: Uncaught ReferenceError: arguments is not defined
이는 함수가 익명인지 여부와 무관하며, 규칙은 화살표 함수 중에서는 arguments 오브젝트에 액세스할 수 없습니다 (undefined). 다음과 같습니다:
// 비익명 함수
var f = a => console.log(`a = ${a}, arguments[0] = ${arguments[0]}`);
f(2);
// log print: Uncaught ReferenceError: arguments is not defined
##四.題外話
ES6 화살표 함수에 관해서는, 위의 콘텐츠로随心所欲에驾驭하는のに十分합니다.下面我们扯点别的(재미있는 것)
###1.JS 중에서 서포트하는 모든 화살표
| 화살표 | 의미 |
|---|---|
| <!-- | 단행 주석 |
| --> | 행두에서 단행 주석을 나타내며, 다른 위치에서는 "趋向于" 를 나타냄 (n --> 0 은 n-- > 0 과 등가) |
| <= | 비교 연산자, 小于等于 |
| => | 화살표 함수 |
2 개의 단행 주석 구문을 보고 놀라지 마십시오. 역사적 원인ですが, 현재 모든 브라우저가 서포트합니다. 쓸모없습니다. 냉지식이죠
###2.lambda 연산과 邱奇數
lambda 연산 중에서 유일한 기초 데이터 타입은 함수입니다. 邱奇數 (church numerals) 는 고계 함수를 사용하여 常見의 기초 데이터 타입 (정수, 불리언 값, 鍵値對, 리스트 등) 을 나타냅니다
자연수는 모두 숫자, 邱奇數는 모두 함수입니다. 邱奇數의 n 은 n 계 함수이며, f^n(inc, base) === f(inc, f(inc, ...f(inc, base))) 입니다. 모든 邱奇數는 2 개의 파라미터를 가진 함수입니다
어떻게 함수로 자연수를 나타내는가? 콘텐츠는 비교적 많지만, 여기에 자연수 집의 작은 예를 1 개 줍니다:
// 자연수 집합 정의
var number = (function*(inc, base) {
var n = zero;
while(true) {
yield n(inc, base);
n = succ(n);
}
})(inc, 0);
for (var n of number) {
console.log(n); // 0, 1, 2, 3, 4
if (n > 3) {
break;
}
}
邱奇數로 나타낸 자연수 집은 다음과 같습니다:
// 0, 1, 2
var zero = (inc, base) => base;
var one = (inc, base) => inc(base);
var two = (inc, base) => inc(inc(base));
// 後繼 함수 정의 f^n -> f^(n+1)
// succ = ln.lf.lx.f (n f x)
var succ = n => (inc, base) => inc(n(inc, base));
// 邱奇數 집합 정의
var church = (function*() {
var fn = zero;
while(true) {
yield fn;
fn = succ(fn);
}
})();
// test
var [, , , three, four, five, six, seven] = church;
console.log(three(inc, 0)); // 3
console.log(four(inc, 0)); // 4
console.log(five(inc, 0)); // 5
console.log(six(inc, 0)); // 6
console.log(seven(inc, 0)); // 7
잘 생각하면세계는 정말로 기묘하다는 것을 발견합니다. 이것도 가능??흥미가 있는 분은 필자의 Demo 를 참조하십시오 (감법, 승법과 감법을 실장했습니다)
###3.Y 組合子와 함수형 프로그래밍
Y 組合子는 익명 재귀 함수를 실장할 수 있습니다
Y 組合子는 1 개의 함수입니다. 다음과 같습니다:
var Y = F => G(G), var G = slef => F(self(self))
그 중에 不動点의 개념이 있습니다. 不動点: 若 F(f) = f 라면, f 는 不動点입니다. Y 組合子 중에서, G(G) 는 不動点입니다
가정 현재 계승을 구하는 재귀 함수가 있다고 합니다:
var fact = n => n === 0 ? 1 : n * fact(n - 1);
이는 분명히 1 개의 익명 재귀가 아닙니다. fact 는 함수명이며, 그것을 재귀 호출하여 계승을 계산합니다. 그렇다면 어떻게 1 개의 익명의 재귀 함수를 실장하는가? 이것은 가능할까요?
Y 組合子로一发하면 됩니다. 다음과 같습니다:
// Y 組合子 정의
// var Y = F => G(G);
// var G = self => F(self(self));
var Y = F =>
((g => g(g))
(g =>
(F((...x) =>
g(g)(...x)))));
// 익명 재귀로 계승 구하기 실장
var yFact = Y(f =>
n => n === 0 ? 1 : n * f(n - 1));
console.log(yFact(5)); // 120
기묘하죠?함수형 프로그래밍 중에는 각종의 유사한 기묘한 변환이 있습니다. FP 의 이해 코스트, 실행 효율은 묻지 않고, 이러한 변환 자체가 몇 개의 재미있는 연구할 가치가 있는 것입니다. 사고에 조금 더 공간을 주고, cpu 를 달리게 합시다
##五.まとめ
lambda 표현식의 극한의 간결함은 매력적이며, 함수를 정의하는 것은 수학 공식을 쓰는 것과 같습니다. 함수형 프로그래밍을 지원하는 언어는 본래 이러해야 합니다
참고 자료
-
APIO 강연고——함수형 프로그래밍:byvoid 선배의 강연고, 汗顔
-
『ES6 in Depth』:InfoQ 中文站에서 제공하는 무료 전자책
아직 댓글이 없습니다