Skip to main content

Arrow Function_ES6 Notes 6

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

The extreme conciseness of lambda expressions is very tempting, defining functions is like writing mathematical formulas, languages supporting functional programming should be like this

Preface

(This was supposed to be last week's content, forgot to write it yesterday, quickly and secretly make it up)

Recently there's been a lot going on, although sort is done, still slightly busy and chaotic. But anyway, the plan is there, eventually will complete things one by one.

Enough nonsense, hope mom's health gets better soon~

I. Arrow Function Introduction

Arrow function, is the lambda expression in C#, reportedly Java8 also added it to the luxury lunch. But anyway, JS is absorbing excellent features from other languages (such as yield, class, default parameters, etc.), regardless of whether these features are good or bad, this thing itself is extremely good (at least the tool we're using is a vibrant one)

It's just Java uses -> arrow, C# uses the same arrow as JS: =>, this arrow is called "lambda operator", jargon reads as "goes to"

Lambda expression (arrow function) is reportedly the most concise method to define functions, syntactically there's almost no redundant components. Because of JS's weak typing characteristics, JS's lambda expressions are more concise than those in C# and Java (less parameter type declarations)

In one sentence, arrow functions are lambda expressions, providing a more concise function definition method

II. Syntax

arg => returnVal syntax is the most concise way to create functions, defines a function with formal parameter arg, return value returnVal

Other syntax as follows in table:

Arrow Function Syntax
SyntaxEquivalent CodeMeaning
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. The third column's "meaning" refers to mathematical function meaning, lambda expressions were originally created by mathematicians

Simple examples as follows:

// Simple example, simplify anonymous function definition
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}`);
    }
});

More complex example:

// Complex example
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);

Rewrite above example with arrow functions:

// Rewrite with arrow functions
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);

Eliminated necessary nonsense like that = this, actually just need to follow one principle to eliminate all that = this, see 3. About this in notes below

III. Features and Notes

1. Parameter List and Return Value Syntax

With 1 parameter, directly write parameter name on left, with 0 or multiple parameters, parameter list needs to be wrapped with ()

When function body has only 1 statement, right side value automatically becomes function return value, when function body has more than 1 statement, function body needs to be wrapped with {}, and need to manually return

P.S. Of course, might easily think of indiscriminately using () => {} as arrow function's opening move, but don't recommend doing this, because next point says { is ambiguous, might bring trouble

2. Ambiguous Characters

{ is the only 1 ambiguous character, so when returning object literals need to wrap with (), otherwise will be parsed as block statements

For example:

var f1 = () => {};
f1();   // Returns undefined
// Equivalent to
// var f1 = function() {};

var f2 = () => ({});
f2();   // Returns empty object {}
// Equivalent to
// var f2 = function() {return {};};

3. About this

Arrow functions will inherit this from outer scope, to avoid that = this, need to follow: except for direct function property values on objects use function syntax, all other functions use arrow functions

This rule is easy to understand, examples as follows:

// Scenario 1
function MyType() {}
MyType.prototype.fn = function() {/*define arrow function*/};  // this in arrow function points to MyType type instance

// Scenario 2
var obj = {};
obj.fn = function() {/*define arrow function*/};   // this in arrow function points to obj

Difference is in function properties defined with function keyword, that function's this points to the object this function property belongs to (anonymous function's this points to global object or undefined). Simply put, function can define a new this, while arrow functions cannot, it can only borrow a this from outer layer. So, when need new this to appear use function to define functions, when want to continue using outer layer's this use arrow functions

4. About arguments Object

Arrow functions don't have arguments object, because standard encourages using [default parameters, rest parameters](/articles/默认参数和不定参数-es6 笔记 4/), [parameter destructuring](/articles/destructuring(解构赋值)-es6 笔记 5/)

For example:

// Normal function
(function(a) {console.log(`a = ${a}, arguments[0] = ${arguments[0]}`)})(1);
// log print: a = 1, arguments[0] = 1

// Arrow function equivalent to above
(a => console.log(`a = ${a}, arguments[0] = ${arguments[0]}`))(1);
// log print: Uncaught ReferenceError: arguments is not defined

This has nothing to do with whether function is anonymous or not, rule is arrow functions cannot access arguments object (undefined), as follows:

// Non-anonymous function
var f = a => console.log(`a = ${a}, arguments[0] = ${arguments[0]}`);
f(2);
// log print: Uncaught ReferenceError: arguments is not defined

IV. Off-topic

Just for ES6 arrow functions, above content is enough to freely master it, below we talk about something else (interesting things)

1. All Arrows Supported in JS

Up to ES6, arrows currently supported in js
ArrowMeaning
<!--Single line comment
-->At line start represents single line comment, at other positions represents "tends to" (n --> 0 equivalent to n-- > 0)
<=Comparison operator, less than or equal to
=>Arrow function

Don't be surprised seeing two single line comment syntaxes, historical reasons, but currently all browsers support. Not very useful, cold knowledge

2. Lambda Calculus and Church Numerals

In lambda calculus the only basic data type is function, Church numerals use higher-order functions to represent common basic data types (integers, boolean values, key-value pairs, lists, etc.)

Natural numbers are all numbers, Church numerals are all functions, Church numeral's n is n-order function, f^n(inc, base) === f(inc, f(inc, ...f(inc, base))), all Church numerals are functions with 2 parameters

How to use functions to represent natural numbers? Content is relatively large, here give a natural number set small example:

// Define natural number set
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;
    }
}

Natural number set represented with Church numerals as follows:

// 0, 1, 2
var zero = (inc, base) => base;
var one = (inc, base) => inc(base);
var two = (inc, base) => inc(inc(base));

// Define successor function f^n -> f^(n+1)
// succ = ln.lf.lx.f (n f x)
var succ = n => (inc, base) => inc(n(inc, base));

// Define Church numeral set
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

Thinking carefully will discover world is truly wonderful, this works?? If interested please see author's Demo (implemented subtraction, multiplication and subtraction)

3. Y Combinator and Functional Programming

Y combinator can implement anonymous recursive functions

Y combinator is a function, as follows:

var Y = F => G(G), var G = slef => F(self(self))

Among them there's a fixed point concept. Fixed point: If F(f) = f, then f is fixed point, in Y combinator, G(G) is fixed point

Suppose currently have a recursive function for calculating factorial:

var fact = n => n === 0 ? 1 : n * fact(n - 1);

This is obviously not an anonymous recursion, fact is function name, recursively call it to implement factorial calculation. So how to implement an anonymous recursive function? Is this possible?

Just use Y combinator, as follows:

// Define Y combinator
// var Y = F => G(G);
// var G = self => F(self(self));
var Y = F =>
    ((g => g(g))
        (g =>
            (F((...x) =>
                g(g)(...x)))));

// Implement anonymous recursion for factorial
var yFact = Y(f =>
    n => n === 0 ? 1 : n * f(n - 1));
console.log(yFact(5));  // 120

Wonderful right? In functional programming there are various similar wonderful transformations, regardless of FP's understanding cost, execution efficiency, these transformations themselves are some interesting things worth studying, give thinking a bit more space, let cpu run

V. Summary

The extreme conciseness of lambda expressions is very tempting, defining functions is like writing mathematical formulas, languages supporting functional programming should be like this

References

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment