ES6的箭头函数

    xiaoxiao2023-11-07  154

    转载自:http://www.ayqy.net/blog/箭头函数_es6笔记6/

    箭头函数_ES6笔记6

    写在前面

    (这篇本来是上周的内容,昨天忘记写啦,赶紧偷偷补上)

    最近事情有点多,虽然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,而箭头函数不能,它只能从外层借一个this。所以,需要新this出现的时候用function定义函数,想沿用外层的this时就用箭头函数

    4.关于arguments对象

    箭头函数没有arguments对象,因为标准鼓励使用默认参数、可变参数、参数解构

    例如:

    // 一般函数 (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.lambda演算与邱奇数

    lambda演算中唯一基础数据类型是函数,邱奇数(church numerals)就是用高阶函数表示常见的基础数据类型(整数、布尔值、键值对、列表等等)

    自然数都是数字,邱奇数都是函数,邱奇数的n是n阶函数,f^n(inc, base) === f(inc, f(inc, ...f(inc, base))),所有邱奇数都是有2个参数的函数

    如何用函数表示自然数?内容比较多,这里给一个自然数集小例子:

    // 定义自然数集合 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组合子就是一个函数,如下:

    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);

    这显然不是一个匿名递归,fact是函数名,递归调用它实现计算阶乘。那么如何实现一个匿名的递归函数?这有可能吗?

    用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表达式的极致简洁很诱人,定义函数就像写数学公式一样,支持函数式编程的语言本该如此

    最新回复(0)