• 技术文章 >web前端 >前端问答

    箭头函数属于es6吗

    青灯夜游青灯夜游2023-01-18 19:53:49原创73

    箭头函数属于es6。箭头函数是ES6中引入的新特性,使用箭头“=>”定义函数,例“var f = v => v;”,等价于“var f = function (v) {return v;};”;如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分,例“var f = () => 5;”。

    本教程操作环境:windows7系统、ECMAScript 6版、Dell G3电脑。

    箭头函数

    箭头函数是ES6中引入的新特性,使用“箭头”(=>)定义函数。由于其简洁的语法和对this关键字的处理,箭头函数迅速成为开发者们最喜爱的功能。

    var f = v => v;
    // 等同于
    var f = function (v) {
      return v;
    };

    如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

    var f = () => 5;
    // 等同于
    var f = function () { return 5 };
    var sum = (num1, num2) => num1 + num2;
    // 等同于
    var sum = function(num1, num2) {
      return num1 + num2;
    };

    如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

    var sum = (num1, num2) => { return num1 + num2; }

    由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

    // 报错
    let getTempItem = id => { id: id, name: "Temp" };
    // 不报错
    let getTempItem = id => ({ id: id, name: "Temp" });

    下面是一种特殊情况,虽然可以运行,但会得到错误的结果。

    let foo = () => { a: 1 };
    foo() // undefined

    上面代码中,原始意图是返回一个对象{ a: 1 },但是由于引擎认为大括号是代码块,所以执行了一行语句a: 1。这时,a可以被解释为语句的标签,因此实际执行的语句是1;,然后函数就结束了,没有返回值。

    如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。

    let fn = () => void doesNotReturn();

    说明:

    箭头函数是函数式编程的一种体现,函数式编程将更多的关注点放在输入和输出的关系,省去了过程的一些因素,因此箭头函数中没有自己的this,arguments,new target(ES6)和 super(ES6)。箭头函数相当于匿名函数,因此不能使用new来作为构造函数使用。

    箭头函数中的this始终指向其父级作用域中的this。换句话说,箭头函数会捕获其所在的上下文的this值,作为自己的this值。任何方法都改变不了其指向,如call(), bind(), apply()。在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用,它与调用时的上下文无关。我们用代码来解释一下。

    隐式返回

    在函数体内只有一个表达式时,你可以让ES6的箭头语法更加简洁。你可以把所有内容放在一行,去掉大括号,并移除return关键字。

    你已经在上面的示例中看到了这些漂亮的一行代码是如何工作的。下面的orderByLikes()函数返回奈飞剧集对象的数组,按照最高点赞数排序:

    // using the JS sort() function to sort the titles in descending order 
    // according to the number of likes (more likes at the top, fewer at the bottom
    const orderByLikes = netflixSeries.sort((a, b) => b.likes - a.likes)
    
    // call the function 
    // output:the titles and the n. of likes in descending order
    console.log(orderByLikes)

    这种写法很酷,但是要注意代码的可读性。特别是在使用单行和无括号的ES6箭头语法对一堆箭头函数进行排序时。就像这个例子:

    const greeter = greeting => name => `${greeting}, ${name}!`

    那里发生了什么?尝试使用常规的函数语法:

    function greeter(greeting) {
      return function(name) {
        return `${greeting}, ${name}!` 
      }
    }

    现在,你可以快速看到外部函数greeter如何具有参数greeting,并返回一个匿名函数。这个内部函数又有一个叫做name的参数,并使用greetingname的值返回一个字符串。下面是调用函数的方式:

    const myGreet = greeter('Good morning')
    console.log( myGreet('Mary') )   
    
    // output: 
    "Good morning, Mary!"

    注意隐式返回错误

    当你的JavaScript箭头函数包含不止一个语句,你需要在大括号内包裹所有语句,并使用return关键字。

    在下面的代码中,该函数建立了一个包含几个Netflix剧集的标题和摘要的对象:

    const seriesList = netflixSeries.map( series => {
      const container = {}
      container.title = series.name 
      container.summary = series.summary
    
      // explicit return
      return container
    } )

    .map()函数中的箭头函数在一系列的语句中展开,在语句的最后返回一个对象。这使得在函数主体周围使用大括号是不可避免的。

    另外,由于正在使用花括号,隐式返回便不是一个选项。你必须显式使用return关键字。

    如果你的函数使用隐式返回来返回一个对象字面量,你需要使用圆括号来包裹该对象字面量。不这样做将导致错误,因为JavaScript引擎将对象字面量的大括号错误地解析为函数的大括号。正如你刚才注意到的,当你在一个箭头函数中使用大括号时,你不能省略return关键字。

    前面代码的较短版本演示了这种语法:

    // Uncaught SyntaxError: unexpected token: ':'
    const seriesList = netflixSeries.map(series => { title: series.name });
    
    // Works fine
    const seriesList = netflixSeries.map(series => ({ title: series.name }));

    无法命名箭头函数

    function关键字和参数列表之间没有名称标识的函数被称为匿名函数。下面是常规匿名函数表达式的样子:

    const anonymous = function() {
      return 'You can\'t identify me!' 
    }

    箭头函数都是匿名函数:

    const anonymousArrowFunc = () => 'You can\'t identify me!'

    从ES6开始,变量和方法可以通过匿名函数的语法位置,使用name属性来推断其名称。这使得在检查函数值或报告错误时有可能识别该函数。

    使用anonymousArrowFunc检查一下:

    console.log(anonymousArrowFunc.name)
    // output: "anonymousArrowFunc"

    需要注意的是,只有当匿名函数被分配给一个变量时,这个可以推断的name属性才会存在,正如上面的例子。如果你使用匿名函数作为回调函数,你就会失去这个有用的功能。在下面的演示中,.setInterval()方法中的匿名函数无法利用name属性:

    let counter = 5
    let countDown = setInterval(() => {
      console.log(counter)
      counter--
      if (counter === 0) {
        console.log("I have no name!!")
        clearInterval(countDown)
      }
    }, 1000)

    这还不是全部。这个推断的name属性仍然不能作为一个适当的标识符,你可以用它来指代函数本身--比如递归、解除绑定事件等。

    如何处理this关键字

    关于箭头函数,最重要的一点是它们处理this关键字的方式。特别是,箭头函数内的this关键字不会重新绑定。

    为了说明这意味着什么,请查看下面的演示。

    这里有一个按钮。点击按钮会触发一个从5到1的反向计数器,它显示在按钮本身。

    <button class="start-btn">Start Counter</button>
    
    ...
    
    const startBtn = document.querySelector(".start-btn");
    
    startBtn.addEventListener('click', function() {
      this.classList.add('counting')
      let counter = 5;
      const timer = setInterval(() => {
        this.textContent = counter 
        counter -- 
        if(counter < 0) {
          this.textContent = 'THE END!'
          this.classList.remove('counting')
          clearInterval(timer)
        }
      }, 1000) 
    })

    注意到.addEventListener()方法里面的事件处理器是一个常规的匿名函数表达式,而不是一个箭头函数。为什么呢?如果在函数内部打印this的值,你会看到它引用了监听器所连接的按钮元素,这正是我们所期望的,也是程序按计划工作所需要的:

    startBtn.addEventListener('click', function() {
      console.log(this)
      ...
    })

    下面是它在Firefox开发人员工具控制台中的样子:

    1.png

    然后,尝试使用箭头函数来替代常规函数,就像这样:

    startBtn.addEventListener('click', () => {
      console.log(this)
      ...
    })

    现在,this不再引用按钮元素。相反,它引用Window对象:

    2.png

    这意味着,如果你想要在按钮被点击之后,使用this来为按钮添加class,你的代码就无法正常工作:

    // change button's border's appearance
    this.classList.add('counting')

    下面是控制台中的错误信息:

    3.png

    当你在JavaScript中使用箭头函数,this关键字的值不会被重新绑定。它继承自父作用域(也称为词法作用域)。在这种特殊情况下,箭头函数被作为参数传递给startBtn.addEventListener()方法,该方法位于全局作用域中。因此,函数处理器中的this也被绑定到全局作用域中--也就是Window对象。

    因此,如果你想让this引用程序中的开始按钮,正确的做法是使用一个常规函数,而不是一个箭头函数。

    匿名箭头函数

    在上面的演示中,接下来要注意的是.setInterval()方法中的代码。在这里,你也会发现一个匿名函数,但这次是一个箭头函数。为什么?

    请注意,如果你使用常规函数,this值会是多少:

    const timer = setInterval(function() {
      console.log(this)
      ...
    }, 1000)

    button元素吗?并不是。这个值将会是Window对象!

    事实上,上下文已经发生了变化,因为现在this在一个非绑定的或全局的函数中,它被作为参数传递给.setInterval() 。因此,this关键字的值也发生了变化,因为它现在被绑定到全局作用域。

    在这种情况下,一个常见的hack手段是包括另一个变量来存储this关键字的值,这样它就会一直指向预期的元素--在这种情况下,就是button元素:

    const that = this
    const timer = setInterval(function() {
      console.log(that)
      ...
    }, 1000)

    你也可以使用.bind()来解决这个问题:

    const timer = setInterval(function() {
      console.log(this)
      ...
    }.bind(this), 1000)

    有了箭头函数,问题就彻底消失了。下面是使用箭头函数时this的值:

    const timer = setInterval( () => { 
      console.log(this)
      ...
    }, 1000)

    4.png

    这次,控制台打印了button,这就是我们想要的。事实上,程序要改变按钮的文本,所以它需要this来指代button元素:

    const timer = setInterval( () => { 
      console.log(this)
     // the button's text displays the timer value
      this.textContent = counter
    }, 1000)

    箭头函数没有自己的this上下文。它们从父级继承this的值,正是因为这个特点,在上面这种情况下就是很好的选择。

    不正常工作的情况

    箭头函数并不只是在JavaScript中编写函数的一种花里胡哨的新方法。它们有自己的局限性,这意味着在有些情况下你不想使用箭头函数。让我们看看更多的例子。

    箭头函数作为对象方法

    箭头函数作为对象上的方法不能很好地工作。

    考虑这个netflixSeries对象,上面有一些属性和一系列方法。调用console.log(netflixSeries.getLikes()) 应该会打印一条信息,说明当前喜欢的人数。console.log(netflixSeries.addLike())应该会增加一个喜欢的人数,然后在控制台上打印新值:

    const netflixSeries = {
      title: 'After Life', 
      firstRealease: 2019,
      likes: 5,
      getLikes: () => `${this.title} has ${this.likes} likes`,
      addLike: () => {  
        this.likes++
        return `Thank you for liking ${this.title}, which now has ${this.likes} likes`
      } 
    }

    相反,调用.getLikes()方法返回'undefined has NaN likes',调用.addLike()方法返回'Thank you for liking undefined, which now has NaN likes'。因此,this.titlethis.likes未能分别引用对象的属性titlelikes

    这次,问题出在箭头函数的词法作用域上。对象方法中的this引用的是父对象的范围,在本例中是Window对象,而不是父对象本身--也就是说,不是netflixSeries对象。

    当然,解决办法是使用常规函数:

    const netflixSeries = {
      title: 'After Life', 
      firstRealease: 2019,
      likes: 5,
      getLikes() {
        return `${this.title} has ${this.likes} likes`
      },
      addLike() { 
        this.likes++
        return `Thank you for liking ${this.title}, which now has ${this.likes} likes`
      } 
    }
    
    // call the methods 
    console.log(netflixSeries.getLikes())
    console.log(netflixSeries.addLike())
    
    // output: 
    After Life has 5 likes
    Thank you for liking After Life, which now has 6 likes

    箭头函数与第三方库

    另一个需要注意的问题是,第三方库通常会绑定方法调用,因此this值会指向一些有用的东西。

    比如说,在Jquery事件处理器内部,this将使你能够访问处理器所绑定的DOM元素:

    $('body').on('click', function() {
      console.log(this)
    })
    // <body>

    但是如果我们使用箭头函数,正如我们所看到的,它没有自己的this上下文,我们会得到意想不到的结果:

    $('body').on('click', () =>{
      console.log(this)
    })
    // Window

    下面是使用Vue的其他例子:

    new Vue({
      el: app,
      data: {
        message: 'Hello, World!'
      },
      created: function() {
        console.log(this.message);
      }
    })
    // Hello, World!

    created钩子内部,this被绑定到Vue实例上,因此会显示'Hello, World!'信息。

    然而如果我们使用箭头函数,this将会指向父作用域,上面没有message属性:

    new Vue({
      el: app,
      data: {
        message: 'Hello, World!'
      },
      created: () => {
        console.log(this.message);
      }
    })
    // undefined

    箭头函数没有arguments对象

    有时,你可能需要创建一个具有无限参数个数的函数。比如,假设你想创建一个函数,列出你最喜欢的奈飞剧集,并按照偏好排序。然而,你还不知道你要包括多少个剧集。JavaScript提供了arguments对象。这是一个类数组对象(不是完整的数组),在调用时存储传递给函数的值。

    尝试使用箭头函数实现此功能:

    const listYourFavNetflixSeries = () => {
      // we need to turn the arguments into a real array 
      // so we can use .map()
      const favSeries = Array.from(arguments) 
      return favSeries.map( (series, i) => {
        return `${series} is my #${i +1} favorite Netflix series`  
      } )
      console.log(arguments)
    }
    
    console.log(listYourFavNetflixSeries('Bridgerton', 'Ozark', 'After Life'))

    当你调用该函数时,你会得到以下错误:Uncaught ReferenceError: arguments is not defined。这意味着arguments对象在箭头函数中是不可用的。事实上,将箭头函数替换成常规函数就可以了:

    const listYourFavNetflixSeries = function() {
       const favSeries = Array.from(arguments) 
       return favSeries.map( (series, i) => {
         return `${series} is my #${i +1} favorite Netflix series`  
       } )
       console.log(arguments)
     }
    console.log(listYourFavNetflixSeries('Bridgerton', 'Ozark', 'After Life'))
    
    // output: 
    ["Bridgerton is my #1 favorite Netflix series",  "Ozark is my #2 favorite Netflix series",  "After Life is my #3 favorite Netflix series"]

    因此,如果你需要arguments对象,你不能使用箭头函数。

    但如果你真的想用一个箭头函数来复制同样的功能呢?你可以使用ES6剩余参数(...)。下面是你该如何重写你的函数:

    const listYourFavNetflixSeries = (...seriesList) => {
       return seriesList.map( (series, i) => {
         return `${series} is my #${i +1} favorite Netflix series`
       } )
     }

    总结

    通过使用箭头函数,你可以编写带有隐式返回的单行代码,以解决JavaScript中this关键字的绑定问题。箭头函数在数组方法中也很好用,如.map().sort().forEach().filter()、和.reduce()。但请记住:箭头函数并不能取代常规的JavaScript函数。记住,只有当箭形函数是正确的工具时,才能使用它。

    【相关推荐:javascript视频教程编程视频

    以上就是箭头函数属于es6吗的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:ES6 javascript 箭头函数
    上一篇:es6 import会变量提升吗 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • es6怎么判断字符串中是否有某个字符串• es6怎么判断是否在数组里• ie6能不能兼容es6• es6是基于哪种语言的语法• es6怎么判断一个变量是否为数组
    1/1

    PHP中文网