JavaScript之箭头函数的不适用场景

ES6 新增了箭头函数,使用起来非常简洁干练。 请看下面代码:

目录
  1. 与 this 相关场景
    1. 定义对象方法
    2. 定义构造函数
    3. 定义函数原型方法
    4. 定义动态上下文的回调函数
    5. 说明
  2. 追求过短代码
  3. 其他情况
  4. 总结

ES6 新增了箭头函数,使用起来非常简洁干练。

请看下面代码:

// 普通函数
const fn = function (arg1, arg2) {
  // ...code
};
 
// 箭头函数,括号内的是参数
const fn = (arg1, arg2) => {
  // ...code
};
 
// 箭头函数 无参数
setTimeOut(() => {
  // ...code
});

看了上面的例子,是不是觉得箭头函数很简便,少了很多代码,看起来也很舒适呢。特别是使用匿名函数时,非常清爽,是不是觉得以后要尽量使用箭头函数了。

但是,如果你是新手的话,在使用一段时候后你可能会发现,有时候箭头函数总是会出现一些莫名奇妙的错误,也不知道是什么原因。如果这时候把箭头函数换成普通的函数,错误又突然解决掉了。这到底是怎么回事呢?

这就要更深入的去了解下箭头函数了!箭头函数虽然简洁方便,但是不能滥用,特别是在与this相关的场景。

与 this 相关场景#

定义对象方法#

const hale = {
  name: "Hale",
  sayName: () => {
    console.log(this.name);
  },
};
hale.sayName(); // undefined

定义构造函数#

const Person = (name) => {
  this.name = name;
};
const hale = new Person("hale");
console.log(hale); // TypeError: Person is not a constructor

定义函数原型方法#

function Person(name) {
  this.name = name;
}
Person.prototype.sayName = () => {
  console.log(this.name);
};
const jack = new Person("jack");
jack.sayName(); // undefined

定义动态上下文的回调函数#

const p = document.querySelector("p");
p.addEventListener("click", () => {
  this.innerText = "hello world";
}); // 无反应

说明#

上面的几种场景都是与this相关的。

我们都知道函数(普通函数)里面的this的值是根据执行上下文来判断的, 是不固定的。但是,箭头函数内的this的值不一样,因为箭头函数没有自己的this,它里面的this指的就是外层代码块的this,也就是外层代码块的作用域,它是固定的。所以,不能使用箭头函数创建构造函数。

因为箭头函数没有this,也不能使用call(),apply(),bind()等改变this的指向。如果需要用到this指向本身函数或者对象的时候,就不能用箭头函数了。

当然,有时候也可以利用箭头函数的this指向外层代码块的情况来编写代码。比如 vue 组件的实例方法,使用定时器或者内层函数时,需要调用本身的属性和方法,就可以使用箭头函数和this了,这时候正好利用箭头函数的this指向外层代码块的情况。

export default {
  data() {
    return {
      name: 'hale',
      list: [],
      target: {}
    }
  },
  create() {
    this.$axios.get('api/data')
      .then(res => { // 箭头函数
      this.list = res.data
      // ...code
    })
  },
  methods: {
    method1() {
      setTimeOut(() => {  // 箭头函数
        const name = this.name
        // ...code
        this.method2(list)
      }, 1000)
    },
    method2(list) {
      list.forEach(item => { // 箭头函数
        // ...code
        this.target = item.xxx
      })
    }
  }
}

稍微了解过 Vue 的源码的同学都知道,Vue 其实就是一个构造函数。整个 Vue 项目的入口就是 Vue 构造函数new出来的实例对象。

这个实例对象里面有自带很多属性和方法。Vue 实例里面的this指的就是 vue 实例本身。

例如,setTimeOut方法调用了箭头函数,而且还使用了this,这里的this指向的是setTimeOut外层的作用域,即 method1 方法的作用域,也就是 method1 的this的执行一样,而 method1 是一个普通函数,它里面的this指向的是调用这个方法的实例对象,也就是 Vue 实例,所以可以拿到 Vue 实例中 data 的数据,以及调用实例本身的方法。

如果把setTimeOut里面的箭头函数改成普通函数function(){}, 再使用thisthis指向的就是setTimeOut本身,因为setTimeOut本身根本就没有data数据或者其他的方法,所以得到的只有undefined了。

所以,methods选项里面的方法,最好也不要使用箭头函数。如果使用了箭头函数,里面再调用this是无法访问 Vue 实例的属性和方法的,因为这时的this已经不是指向 Vue 实例了,而是指向 Vue 实例外层的作用域。

箭头函数用在什么场景中,主要还是需要理解箭头函数里面this的指向问题。

追求过短代码#

箭头函数虽然简洁方便,但是有时候过于简洁也不行,特别是在函数嵌套的情况下,会影响代码的可读性。

下面的例子中是两个参数相加的函数,如果只有一个参数就加上常量 5,使用箭头函数只有一行代码,可能比较难以理解,转换成普通函数就比较好理解了。

// 箭头函数
const add = (a, b) => (b === undefined ? (b) => b + 5 : a + b);
 
const addNum = add();
 
console.log(addNum(1)); // 6
console.log(add()(1)); // 6
console.log(add(4, 10)); // 14
 
// 普通函数
const add = function (a, b) {
  if (b === undefined) {
    return function (b) {
      return b + 5;
    };
  }
  return a + b;
};

滥用箭头函数使得代码过于简短,语义化不明,不利于阅读。毕竟代码主要还是给人看的,而不是给机器看的。

其他情况#

还有一些其他的特殊场景也不适合使用箭头函数。

  • 需要用到arguments对象时,因为箭头函数里面没有arguments这个对象。
  • Generator 函数, 因为箭头函数不能使用yield命令。

总结#

  • 箭头函数适合于无复杂逻辑或者无副作用的纯函数的场景。比如定时器setTimeOut,setInterval; 数组对象的方法forEach,filter,findIndex,map,reduce等。
  • 尽量不要在最外层的函数中使用箭头函数。因为箭头函数的this指向的是外层作用域,这个作用域可能是全局作用域。函数里面如果需要用到this的话,会污染全局作用域。这时候应该先使用普通函数,然后再根据实际情况使用箭头函数,记住箭头函数的this指向的就是外面普通函数的作用域。
  • 使用箭头函数不要追求过于简短的代码,还需要保证代码的可读性。