箭头函数和普通函数的差异

2,725 阅读4分钟

最近被问到一道题,问箭头函数有什么特性,当时心想,这个我熟啊,随口就来了没有自己的 this 啊,和最近的 function 定义的函数相同啊,其实不止这个,有很多,我们虽然经常用,往往会忽略。所以今天就一起来了解下箭头函数。

总结版本

节约时间可以看这里!!和传统 JS 函数的不同点

  • 不能通过 new 关键字调用, 没有原型
  • 不可以改变 this 绑定,由外层非箭头函数决定,所以使用 call, apply, bind 也不会影响
  • 不支持 arguments,所以根据作用域链,会拿到外层函数的 arguments
  • 不支持重复命名参数
  • 隐式返回

箭头函数是 ES6 引入的语法,和之前使用function 来定义的函数不同,箭头函数更加简洁,简单到甚至只需要一个() =>

举个例子

(a, b) => a + b
// 等价于
funtion(a, b){
  return a + b
}

所以你看,它们两兄弟长得就不像,自然是有很多差异,来一起看看它的差异

隐式返回

看我们刚才那个例子,为啥我们在箭头函数中不用使用 return 就能返回 a + b 的值呢?

其实函数内部一直都有一个隐藏的返回值,使用 function 如果什么都不定义,其实默认就会返回 undefined,我理解这是终止函数的执行,用代码来表示:

function noReturn() {
  console.log('aaa')
  return // 这个不需要手动写,不写默认就是
}

也就是说如果你在函数中没有需要 return 的,就会自动给你加上一个

那箭头函数函数有什么不同呢?

其实很明显就能看出来,箭头函数又给你省了一个 return,适合这种函数体内只有一行代码的情况,多行还是需要写的

不过要注意,如果 return 的是一个值,不需要 {} 就可以

但是有一个注意的点,如果返回的是一个字面量需要加 (),看下面的例子

const add = (a, b) => a + b
const obj = () => ({ a: 1 })

This

嗯,this 就是我印象最深的了。

我们知道由于 JS 的特殊关系,this 是在运行时确定的,这往往会在代码中产生一些迷惑的行为。

我们在 function函数里面访问 this 或者在嵌套函数里面访问 this 结果不太一样。this 在普通函数内总结为以下行为

  1. 在调用函数时使用 new 关键字,函数内的 this 是一个全新的对象。
  2. 如果 apply、call 或 bind 方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象。
  3. 当函数作为对象里的方法被调用时,函数内的 this 是调用该函数的对象。
  4. 如果调用函数不符合上述规则,那么 this 的值指向全局对象(global object)。
  5. 浏览器环境下 this 的值指向 window 对象,但是在严格模式下'use strict',this 的值为 undefined。

优先级按序号从高到低,this 比较复杂,这是另一个话题了,这里不展开讲了,我们主要来看箭头函数的不同

无论如何执行或在何处执行,箭头函数内部的 this 值始终等于离它最近的外部函数的 this 值。换句话说,箭头函数可按词法解析 this,箭头函数没有定义自己的执行上下文。

这样的好处是啥呢,来举个例子

const obj = {
	value: 2,
	callback: function(arr) {
		var self = this
		function showThis() {
			console.log(self.value)
		}
    showThis()
	}
}

const obj2 = {
	value: 2,
	callback: function(arr) {
		showThis = () => {
			console.log(this.value)
		}
		showThis()
	}
}
obj.callback() // 2
obj2.callback() // 2

这样就改善了我们之前在嵌套函数里面使用外面的 this 需要先定义变量的情况。

在类里面也经常使用,熟悉 React 的同学都知道,之前如果在类里面使用方法我们需要先 bind 一下 this, 但箭头函数都给我们解决了。

所以箭头函数的 this 是编写时确定的,所以使用 call, apply, bind 也不能改变

arguments

我们知道常规函数中,有一个 arguments 的类数组对象,包含了函数的所有参数,经常使用在我们没有具体定义参数的情况下,而箭头函数是不支持该对象的,所以如果你在箭头函数里面使用,按照作用域链的查找规则,会一直查询到最近的非箭头函数,然后实际使用的是非箭头函数里面的 arguments

为此,ES6 也提供了这种不定参数情况下的参数如果操作,看下面的例子

const add = (...args) => {
	return args.reduce((cur, i) => cur + i, 0)
}
add(5, 10, 15)

...args 会把不定参数转换成一个数组,但使用这个也要注意,只能使用一次且放在所有参数末尾

不能使用 new & 没有原型

因为不能使用 new,所以箭头函数也没有原型,ES6 新增的 new.target 也没有,自然而然也不能通过 super 来访问原型属性

var Foo = () => {};
console.log(Foo.prototype); // undefined

嗯,这就是箭头函数和普通函数的区别了,感觉又打开了新世界