본문으로 건너뛰기

화살표 함수_ES6 노트 6

무료2016-05-02#JS#js箭头函数#js lambda表达式#js拉姆达表达式#javascript lambda expression#邱奇数#Y组合子

lambda 표현식의 극한의 간결함은 매력적이며, 함수를 정의하는 것은 수학 공식을 쓰는 것과 같다. 함수형 프로그래밍을 지원하는 언어는 본래 이러해야 한다

서문에

(이 글은 원래 지난주 콘텐츠였는데, 어제 쓰는 것을 잊어버렸습니다. 몰래 보충합니다)

최근 일이 조금 많아, 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 중에서 서포트하는 모든 화살표

ES6 까지, 현재 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 표현식의 극한의 간결함은 매력적이며, 함수를 정의하는 것은 수학 공식을 쓰는 것과 같습니다. 함수형 프로그래밍을 지원하는 언어는 본래 이러해야 합니다

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성