深入理解JavaScript中的箭头函数

(编辑:jimmy 日期: 2024/10/6 浏览:2)

从一开始箭头就是 JavaScript 的一部分,在第一个 JavaScript 中就建议将内联的脚本代码包裹在 HTML 的注释中,这可以防止那些不支持 JavaScript 的浏览器错误滴将你的代码显示为明文。你也许写过下面这样的代码:

<script language="javascript">
<!--
  document.bgColor = "brown"; // red
// -->
</script>
 
<script language="javascript">
<!--
  document.bgColor = "brown"; // red
// -->
</script>

古老的浏览器将看到两个不被支持的标签和一段注释,只有支持 JavaScript 的新浏览器才会将其解析为 JavaScript 代码。

为了支持这个古怪的特性,浏览器的 JavaScript 引擎把 <!-- 作为一个单行注释的开始,这不是开玩笑的,这一直都是这门语言的一部分,并且至今还能用,不仅仅在 <script> 标签内的首行,而是在 JavaScript 代码的任何部位都可用,它甚至还能在 Node 中使用。

凑巧的是,这种风格的注释在 ES6 中首次被标准化。但这并不是我们将谈论的箭头。

--> 也表示一个单行注释,与 HTML 不同的是,在 HTML 中,--> 之前的部分是注释内容,而在 JavaScript 中,在 --> 之后的行才是注释。

只有当 --> 出现在一行的开始时,才表示该箭头是一个注释,因为在其他情况下,--> 是一个操作符(goes to)。

function countdown(n) {
 while (n-->0) // "n goes to zero"
  alert(n);
 blastoff();
}
 
function countdown(n) {
 while (n-->0) // "n goes to zero"
  alert(n);
 blastoff();
}

上面代码是真实能运行的。循环运行直到 n 为 0,这并不是 ES6 的新特性,但结合我们熟悉的特性,这具有很强的误导性。你能搞明白上面代码的运行情况吗?你可以在 Stack Overflow 上找到相应的解答。

当然还有一个箭头,那就是小于等于操作符 <=,也许你还可以找到使用箭头的地方,但我们还是停下来,看一个我们从没见过的箭头:

  •     <!-- 单行注释
  •     --> goes to 操作符
  •     <= 小于等于操作符
  •     => "htmlcode">
    $("#confetti-btn").click(
     
    $("#confetti-btn").click(
    

    jQuery 的 .click() 方法需要一个函数作为参数,我们可以很方便地就地创建一个函数:

    $("#confetti-btn").click(function (event) {
     playTrumpet();
     fireConfettiCannon();
    });
    
     
    $("#confetti-btn").click(function (event) {
     playTrumpet();
     fireConfettiCannon();
    });
    
    

    现在对我们来说,编写这样的代码是最自然的事了。但是在 JavaScript 流行起来之前,这种风格的代码看起来还是有些奇怪,因为在其他语言中都没有这样的特性。在 1958 年,Lisp 就有了函数表达式,也叫 lambda 函数,而在存在多年的 C++、Python、C# 和 Java 中没有该特性。

    现在,这四门语言都有了 lambda 表达式,而且新出现的语言都普遍内置了 lambda 表达式。如今 JavaScript 也支持该特性了,这必须感谢那些重度依赖 lambda 表达式的库的开发者,这推动了该特性被广泛采纳。

    与其他几门语言相比,JavaScript 的语法略显冗长:

    // A very simple function in six languages.
    function (a) { return a > 0; } // JS
    [](int a) { return a > 0; } // C++
    (lambda (a) (> a 0)) ;; Lisp
    lambda a: a > 0 # Python
    a => a > 0 // C#
    a -> a > 0 // Java
     
    // A very simple function in six languages.
    function (a) { return a > 0; } // JS
    [](int a) { return a > 0; } // C++
    (lambda (a) (> a 0)) ;; Lisp
    lambda a: a > 0 # Python
    a => a > 0 // C#
    a -> a > 0 // Java
    

    箭头函数

    ES6 引入了一种新的语法来编写函数:

    // ES5
    var selected = allJobs.filter(function (job) {
     return job.isSelected();
    });
    
    // ES6
    var selected = allJobs.filter(job => job.isSelected());
    
     
    // ES5
    var selected = allJobs.filter(function (job) {
     return job.isSelected();
    });
     
    // ES6
    var selected = allJobs.filter(job => job.isSelected());
    
    

    当你需要只有一个参数的函数,箭头函数的语法可以简化为 Identifier => Expression,直接省略了 function 和 return 关键字,连括号和结尾的分号也同时省略了。

    编写一个有多个(或没有参数,或 Rest 参数和参数默认值,或解构参数)参数的函数,你需要用括号将参数括起来:

    // ES5
    var total = values.reduce(function (a, b) {
     return a + b;
    }, 0);
    
    // ES6
    var total = values.reduce((a, b) => a + b, 0);
     
    // ES5
    var total = values.reduce(function (a, b) {
     return a + b;
    }, 0);
     
    // ES6
    var total = values.reduce((a, b) => a + b, 0);
    
    

    箭头函数还可以与一些工具函数库完美地配合使用,比如 Underscore.js 和 Immutable,事实上,Immutable 文档中的例子全部都是使用 ES6 编写,其中有很多已经使用到了箭头函数。

    函数体除了使用一个表达式外,箭头函数还可以包含一个语句块,回忆之前我们提到过的例子:

    // ES5
    $("#confetti-btn").click(function (event) {
     playTrumpet();
     fireConfettiCannon();
    });
     
    // ES5
    $("#confetti-btn").click(function (event) {
     playTrumpet();
     fireConfettiCannon();
    });
    

    下面是采用箭头函数的写法:

    // ES6
    $("#confetti-btn").click(event => {
     playTrumpet();
     fireConfettiCannon();
    });
     
    // ES6
    $("#confetti-btn").click(event => {
     playTrumpet();
     fireConfettiCannon();
    });
    

    需要注意的是,使用语句块的箭头函数不会自动返回一个值,必须显式地使用 return 来返回一个值。

    还有一个忠告,当使用箭头函数来返回一个对象时,始终使用括号将返回的对象括起来:

    // create a new empty object for each puppy to play with
    var chewToys = puppies.map(puppy => {});  // BUG!
    var chewToys = puppies.map(puppy => ({})); // ok
    
     
    // create a new empty object for each puppy to play with
    var chewToys = puppies.map(puppy => {});  // BUG!
    var chewToys = puppies.map(puppy => ({})); // ok
    
    

    因为空对象 {} 与空语句块 {} 看上去一模一样,ES6 将始终把紧跟在 => 后面的 { 当作语句块的开始,而不是一个对象的开始,那么 puppy => {} 就被解析为一个没有函数体的箭头函数,而且返回值为 undefined。

一句话新闻

Windows上运行安卓你用过了吗
在去年的5月23日,借助Intel Bridge Technology以及Intel Celadon两项技术的驱动,Intel为PC用户带来了Android On Windows(AOW)平台,并携手国内软件公司腾讯共同推出了腾讯应用宝电脑版,将Windows与安卓两大生态进行了融合,PC的使用体验随即被带入到了一个全新的阶段。