写点什么

ES6 新特性详解 - 箭头函数

作者:yuanyxh
  • 2024-09-14
    中国香港
  • 本文字数:2896 字

    阅读完需:约 10 分钟

ES6 新特性详解 - 箭头函数

简述

理解并讲述 ES6 新的定义函数的方式:Arrow Function,内容有误请指正。

语法

箭头函数是 ES6 中提出的新的定义函数的方式,语法较之传统函数声明与函数表达式更简洁,箭头函数只能以表达式的形式定义。


// () 参数列表// => 胖箭头// { ... } 函数体const add = (a, b) => {  return a + b;};
console.log(add(1, 2)); // 3
复制代码


箭头函数在有且仅有一个参数的情况下,能够省略 ()


// 省略 ()const print = (charts) => {  console.log(charts);};
print('Hello JavaScript!'); // Hello JavaScript!
复制代码


此外,如果函数体内只有一个表达式,可以省略包裹函数体的 {},且表达式的结果会被当作函数的返回值


// 函数体内只有一个表达式,省略 {}const isString = (_unknown) => typeof _unknown === 'string';
console.log(isString('Learn JavaScript')); // trueconsole.log(isString({})); // false
复制代码


当省略花括号 {} 且函数返回一个对象时,应使用 () 包裹


// 返回对象const getPersonObj = (name, age) => ({  // 使用 () 包裹  name: name,  age: age,  sayHi() {    console.log('hello, my name is ' + this.name + '.');  }});
const me = getPersonObj('yuanyxh', 22);me.sayHi(); // hello, my name is yuanyxh.
复制代码


这是因为 JS 引擎在大部分场景下遇到 { 会将它看作是代码块 { ... } 的左半部分,而非对象。

特性

箭头函数除了语法更简洁之外,还存在一些程序猿应该知晓的特性,只有了解这些特性才能更好的发挥出箭头函数的作用。


箭头函数没有原型对象,即 prototype 属性,也意味着它不能被当作构造函数,被 new 调用


// 声明箭头函数const ArrowFun = () => {};
// 没有原型对象console.log(ArrowFun.prototype); // undefined
// 无法以 new 调用new ArrowFun(); // TypeError
复制代码


箭头函数的 this 永远指向最近的上下文的 this,且无法被 callbindapply 改变


// 全局作用域下const arrow_global = () => console.log(this);arrow_global(); // window
// 函数作用域下function getThisArgs() { const arrow_func = () => console.log(this); arrow_func(); // { name: 'yuanyxh', age: 22 }}// getThisArgs 绑定 thisgetThisArgs.call({ name: 'yuanyxh', age: 22});
复制代码


const owner = {  count: 6,  add(num) {    const inner = { count: 0 };    const fn = (v) => v + this.count;    return fn.call(inner, num); // 修改 this 指向  }};console.log(owner.add(2)); // 8 -> 修改 this 指向无效!!!
复制代码


箭头函数没有自己的 argumentssupernew.target,若最近的上下文拥有这些 标识 则引用上下文的,否则抛出错误


// 全局作用域下,箭头函数没有自己的 argumentsconst arrow_global = () => console.log(arguments);arrow(); // ReferenceError
// 函数作用域下,箭头函数引用了 owner 的 argumentsfunction owner() { const args = arguments; const inner = () => { console.log(args === arguments); // true (() => console.log(args === arguments))(); // true }; inner();}owner();
复制代码


superES6 的另一个新特性,配合 ES6 class 使用,用于在子类引用父类构造器或父类方法


// 父类class Person {  sayHi() {    console.log('hi');  }}// 子类class Son extends Person {  constructor() {    super();  }  sayHi() {    // 箭头函数引用最近上下文的 super    const arrow = () => super.sayHi();    arrow();  }}// 构造实例new Son().sayHi(); // hi
复制代码


new.target 指向 new 调用的目标构造函数或类,当以普通方式调用时,new.target 值为 undefined


// 全局作用域下,箭头函数无法使用 new.targetconst arrow = () => console.log(new.target);arrow(); // SyntaxError
// 函数作用域下function CheckNewTarget() { // 箭头函数引用 CheckNewTarget 的 new.target const arrow_func = () => console.log(new.target === CheckNewTarget); arrow_func();}new CheckNewTarget(); // true
复制代码

实践

JS 中的 this 是动态绑定的,在不同场景下会出现 this 指向与预料不同的情况,如


假设有一个按钮,点击出现倒计时并禁用


// 获取按钮const btn = document.querySelector('.btn');// 添加事件btn.addEventListener('click', function () {  let i = 10,    timer = null;  this.disabled = true;  // 每隔一秒执行回调  timer = setInterval(function () {    i--;    this.innerHTML = '还有' + i + '秒';
if (i <= 0) { clearInterval(timer); this.disabled = false; this.innerHTML = '点击倒计时'; } }, 1000);});
复制代码


运行以上代码,会发现点击后除了按钮被禁用外,按钮文本并没有变化,甚至在严格模式下还会报错,这是因为传递给 setInterval 的函数内部 this 指向 window,在严格模式下为 undefined


const obj = {  sayHi() {    console.log(this);  }  asyncSayHi() {    setTimeout(this.sayHi, 1000);  }};
obj.sayHi(); // objobj.asyncSayHi(); // window
复制代码


可以看到 sayHi 方法经由 obj 调用,this 指向 obj,而传递给 setTimeoutsayHi 方法在定时结束后是直接调用的,并没有绑定到谁身上。


在箭头函数出现之前,开发人员经常使用一个临时变量来存储 this 的值


// 获取按钮const btn = document.querySelector('.btn');// 添加事件btn.addEventListener('click', function () {  let i = 10,    timer = null;
// 存储 this const _self = this;
this.disabled = true; // 每隔一秒执行回调 timer = setInterval(function () { i--;
// 使用临时变量修改状态 _self.innerHTML = '还有' + i + '秒';
if (i <= 0) { // 清除定时器并重置状态 clearInterval(timer); _self.innerHTML = '点击倒计时'; _self.disabled = false; } }, 1000);});
复制代码


而使用箭头函数则不必再分配一个变量,因为箭头函数的 this 永远指向最近的上下文的 this


// 获取按钮const btn = document.querySelector('.btn');// 添加事件btn.addEventListener('click', function () {  let i = 10,    timer = null;
// const _self = this;
this.disabled = true; // 每隔一秒执行回调 timer = setInterval(() => { i--;
// _self.innerHTML = '还有' + i + '秒';
// this 指向正常 this.innerHTML = '还有' + i + '秒';
if (i <= 0) { // 清除定时器并重置状态 clearInterval(timer); // _self.innerHTML = '点击倒计时'; // _self.disabled = false;
this.innerHTML = '点击倒计时'; this.disabled = false; } }, 1000);});
复制代码

结语

本文介绍了箭头函数的语法与相关特性。虽然箭头函数语法更简洁,但不应该滥用,不应该为了节省几句代码而忽视了箭头函数的特性。

参考资料

  • 《你不知道的 JavaScript》

  • 《JavaScript 高级程序设计》

  • MDN#JavaScript


发布于: 刚刚阅读数: 4
用户头像

yuanyxh

关注

站在巨人的肩膀上 2023-08-19 加入

web development

评论

发布
暂无评论
ES6 新特性详解 - 箭头函数_js_yuanyxh_InfoQ写作社区