本文深入解析JavaScript ES6中箭头函数的语法特性及其对this绑定的影响。通过对比常规函数与箭头函数的写法,详细阐述this在不同调用场景下的确定规则,包括普通调用、对象方法、构造函数及定时器中的表现,并解释箭头函数如何继承外层this且无法通过call修改。
前言
JavaScript在ES6规范中引入了箭头函数,相比传统函数定义方式,它不仅在语法上更为精简,更在this的绑定机制上做出了重要改进。由于this在JavaScript中常被视作复杂且易混淆的概念,许多资料对其解释存在差异,本文旨在厘清JS中函数定义与this值之间的关联。
一、JS中函数的写法
1.常规函数的写法
在ES6标准普及之前,JavaScript中的函数由function关键字、参数列表以及被大括号包裹的函数体构成。为了与后续介绍的箭头函数区分,我们将此类函数称为常规函数。常规函数支持声明式定义和赋值式定义两种形式。例如:
function test(name) { //声明式写法
console.log(name)
}
test('Jerry')
let test2 = function(name) { //赋值式写法
console.log(name)
}
test2('Tom')
2. 箭头函数的写法
ES6引入箭头函数后,函数定义变得更加简洁,但书写时需遵循特定规则。
规则一:箭头函数只能用赋值式写法,不能用声明式写法
例如:
const test = (name) => {
console.log(name)
}
test('Jerry')
规则二:如果参数只有一个,可以省略圆括号;如果没有参数或多个参数,则必须保留圆括号。
例如:
const test = name => {
console.log(name)
}
test('Jerry')
const test2 = (name1, name2) => {
console.log(name1 + ' and ' + name2)
}
test2('Tom', 'Jerry')
规则三:如果函数体仅包含一条语句,可以省略大括号。
例如:
const test = name => console.log(name)
规则四:如果省略了大括号,则无需显式编写return语句,箭头函数会自动返回该表达式的结果。
例如:
const add = (p1, p2) => p1 + p2
add(10, 25)
需牢记:函数体的大括号与return关键字必须同时存在或同时省略。
从上述示例可见,箭头函数简化了常规函数中的圆括号和大括号使用。除此之外,箭头函数对常规函数最大的优化在于this的处理方式。
二、理解常规函数中this
在探讨箭头函数对this的优化之前,需明确this的本质及其使用方式。this是调用call方法时传递的第一个参数,它可以在函数调用时动态修改;若函数未被调用,this的值是不确定的。
若未使用过call方法调用函数,上述定义可能难以理解。因此,需先理解函数调用的两种基本方式。
1. 纯粹的函数调用
第一种方式最为常见,示例如下:
function test(name) {
console.log(name)
console.log(this)
}
test('Jerry') //调用函数
这种调用方式虽常用,但实为简写形式,其完整写法如下:
function test(name) {
console.log(name)
console.log(this)
}
test.call(undefined, 'Tom')
注意上述调用函数时使用了call方法。call方法接收的第一个参数即为this,此处传递了undefined。那么,依据定义,函数执行后输出的this会是undefined吗?并非如此。
如果传递的context为null或undefined,则默认context为window对象(严格模式下默认context为undefined)。
因此,此处输出的this是Window对象。
2. 对象中函数的调用
直接查看示例:
const obj = {
name: 'Jerry',
greet: function() {
console.log(this.name)
}
}
obj.greet() //第一种调用方法
obj.greet.call(obj) //第二种调用方法
示例中的第一种调用方式仅是第二种方式的语法糖,第二种才是完整的调用方法,其优势在于可以手动指定this。
手动指定this的示例:
const obj = {
name: 'Jerry',
greet: function() {
console.log(this.name)
}
}
obj.greet.call({name: 'Spike'}) //打出来的是 Spike
从上述示例可见,greet函数执行时,this已被手动修改。
3. 构造函数中this
构造函数中的this较为特殊,每个构造函数在new操作后都会返回一个对象,该对象即为this,也就是context上下文。
示例:
function Test() {
this.name = 'Tom'
}
let p = new Test()
console.log(typeof p) //object
console.log(p.name) // Tom
4. window.setTimeout()和window.setInterval()中函数的调用
window.setTimeout()和window.setInterval()中的函数this较为特殊,内部的this默认指向window对象。
简单总结:函数完整的调用方式使用call方法,包括test.call(context, name)和obj.greet.call(context,name),这里的context即为函数调用时的上下文,也就是this,只不过这个this是可以通过call方法来修改的;构造函数稍微特殊一点,它的this直接指向new之后返回的对象;window.setTimeout()和window.setInterval()默认的是this是window对象。
三、理解箭头函数中的this
前文关于this的论述较多,this是函数用call方法调用时传递的第一个参数,而且它还可以手动更改,这样要确定this的值就太麻烦了。不过,箭头函数的出现给我们确定this帮了一些忙。
1. 箭头函数的特性一:默认绑定外层this
前文提到:this的值是可以用call方法修改的,而且只有在调用的时候我们才能确定this的值。而当我们使用箭头函数的时候,箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的。
不使用箭头函数例子:
const obj = {
a: function() { console.log(this) }
}
obj.a() //打出的是obj对象
使用箭头函数的例子:
const obj = {
a: () => {
console.log(this)
}
}
obj.a() //打出来的是window
在使用箭头函数的例子里,因为箭头函数默认不会使用自己的this,而是会和外层的this保持一致,最外层的this就是window对象。
2. 箭头函数的特性二:不能用call方法修改里面的this
这个也很好理解,我们之前一直在说,函数的this可以用call方法来手动指定,而为了减少this的复杂性,箭头函数无法用call方法来指定this。
例子:
const obj = {
a: () => {
console.log(this)
}
}
obj.a.call('123') //打出来的结果依然是window对象
因为前文提到window.setTimeout()中函数里的this默认是window,我们也可以通过箭头函数使它的this和外层的this保持一致:
window.setTimeout()的例子:
const obj = {
a: function() {
console.log(this)
window.setTimeout(() => {
console.log(this)
}, 1000)
}
}
obj.a.call(obj) //第一个this是obj对象,第二个this还是obj对象
想必大家明白了,函数obj.a没有使用箭头函数,因为它的this还是obj,而setTimeout里的函数使用了箭头函数,所以它会和外层的this保持一致,也是obj;如果setTimeout里的函数没有使用箭头函数,那么它打出来的应该是window对象。
四、多层对象嵌套里函数的this
这里是笔者在学习时遇到的一点疑惑。箭头函数里的this是和外层保持一致的,但是如果这个外层有好多层,那它是和哪层保持一致呢?
直接上例子:
const obj = {
a: function() { console.log(this) },
b: {
c: function() {console.log(this)}
}
}
obj.a() // 打出的是obj对象, 相当于obj.a.call(obj)
obj.b.c() //打出的是obj.b对象, 相当于obj.b.c.call(obj.b)
上面的代码都符合直觉,接下来把obj.b.c对应的函数换成箭头函数,结果如下:
const obj = {
a: function() { console.log(this) },
b: {
c: () => {console.log(this)}
}
}
obj.a() //没有使用箭头函数打出的是obj
obj.b.c() //打出的是window对象!!
obj.a调用后打出来的是obj对象,而obj.b.c调用后打出的是window对象而非obj,这表示多层对象嵌套里箭头函数里this是和最最外层保持一致的。
上面的内容就是笔者学习箭头函数中梳理出来的知识点,如有错误,请批评指正!这是我在掘金上写的第三篇文章,感谢阅读!
本文参考:this 的值到底是什么?一次说清楚
总结
到此这篇关于JS中箭头函数与this的写法和理解的文章就介绍到这了,更多相关JS箭头函数与this内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!