引言
JavaScript中的this关键字是一个常见但也相当令人困惑的概念。对于初学者来说,理解this关键字的工作原理和用法是精通JavaScript中九九八十一难的关键一难。本文将深入探讨this关键字的各个方面,帮助读者更好地理解和应用它。
正文
1. this关键字的基本概念
在JavaScript中,this关键字代表当前执行代码的上下文对象。它的取值是在函数被调用时动态确定的,取决于调用函数的方式。在不同情况下,this可能指向全局对象、调用函数的对象、新创建的对象实例或者显式指定的值,想搞清楚this到底是指向谁,就像给一个走丢的小孩找爹妈,需要我们通过各种线索以及小孩(this)提供的信息去抽丝剥茧调查清楚。
2. 函数中的this
在函数中,this关键字的值取决于函数的调用方式。这里将讨论全局函数、对象方法和构造函数中的this关键字行为,并提供示例代码来说明不同情况下this的指向。
全局函数(默认绑定)
很多对this略有耳闻的小白看到这里时肯定想跳过了,毕竟全"局函数中的this指向全局"就像是司马昭之心------路人皆知,但是,你知道为什么吗?或者说,以下几种情况,this又指向谁?
js
var a = 3
function foo() {
foo2()
}
function foo2() {
console.log(this.a);
}
foo()//得到3
js
var a = 3
function foo() {
function foo2() {
console.log(this.a);
}
foo2()
}
foo()
在这里,第一种情况好解释,毕竟人家foo2可是实打实的定义在全局,不管在哪调用都属于全局函数,那么第二种呢?
很明显,即使foo2被定义在函数foo内部,但其中的this依旧指向全局。this:洋装虽然穿在身,我心依然是全局心。其实,准确来说,函数中的this指向的是这个函数的词法作用域。foo的词法作用域在哪?显而易见在全局,那foo2的词法作用域又在哪?在foo里面?你要是这么回答面试官就可以收拾收拾回家了。因为函数内部不存在词法作用域,函数的词法作用域只能表示自己在哪里被声明,就好比问你是哪个省的?(我是妈妈生的)你回答我是广东省的,那广东省是你的吗?显然不是。这就是函数的词法作用域和函数间的关系。因此foo2里面的this会一路往外找,直到找到foo2的词法作用域,也就是全局。同理,无论这里function套function套多少层,哪怕套到10086层,this依旧会坚定不移指向全局,这也是this的默认绑定规则。
对象方法(隐式绑定)
当函数处于对象内部的时候,this会指向对象内部,无论是在函数内声明,还是通过引用的方式将全局函数赋值给对象内部的变量
js
var obj = {
a: 2,
b: 3,
foo: function () {
console.log(this.a);
console.log(this.b);
}
}
obj.foo()
js
function foo2() {
console.log(this.a);
console.log(this.b);
}
var obj = {
a: 2,
b: 3,
foo: foo2
}
obj.foo()
理解并不难,就好比我现在问你你是哪里的,你会说你是广东的,或者说你是江苏的,但如果我当着你对象的面问你是哪的,你就会扭捏着说你是对象的。
3. 隐式绑定和显式绑定
JavaScript中的this关键字可以通过隐式绑定和显式绑定来控制。本节将将介绍call、apply和bind方法,这些方法可以用来显式地绑定this关键字,就像是拍花子,他们仨往this头上一拍,this就会乖乖指向他们指定的位置
call
call的使用方法如下:
js
function foo(n, m) {
console.log(this.a, n, m);
}
var obj = {
a: 2
}
foo.call(obj, 100, 21)//输出2,100,21
这里使用call,同时多传了一个参数obj给函数foo,call会将obj与foo进行显示绑定,使foo内的this不再指向函数的词法作用域,而是指向obj,所以输出的this.a不会报错,而是会读取到被指向的obj内部的a
apply
apply 方法和 call 方法类似,不同之处在于传入参数的方式。当原函数需要多个实参时,call可以直接接在指定的对象后面,但apply需要将所有实参写入数组,再将数组作为唯一实参传入函数。
js
function foo(n, m) {
console.log(this.a, n, m);
}
var obj = {
a: 2
}
foo.apply(obj, [132, 254])
bind
bind在三种显示绑定中显得较为突出,因为它的另外两位同事直接接在函数名后再正常调用函数即可,但bind不愿意,当我们用相同的方法使用bind时,它不会让我们的函数顺利运行,而是会返回一个与目标对象绑定过的新函数,需要重新将返回的新函数调用才会实现我们的预期目标。但它也不是一无是处,bind的传参方式相当任性,支持使用者把参数以任何组合传入函数,说起来有点抽象,以代码解释会更清楚
js
function foo(n, m) {
console.log(this.a, n, m);
}
var obj = {
a: 2
}
var bar1= foo.bind(obj)//bind返回的是函数体
bar(123, 1235)
var bar2= foo.bind(obj, 123)//bind返回的是函数体
bar2(1235)
var bar3= foo.bind(obj, 123, 1235)//bind返回的是函数体
bar3()
4. 箭头函数与this
箭头函数在JavaScript中引入了一种新的函数语法,它对this关键字的行为有所不同。我们将探讨箭头函数中this的行为,并比较它与普通函数的差异。箭头函数与传统的 JavaScript 函数在处理 this 上有一些重要的不同之处。在箭头函数中,this 的值取决于箭头函数是如何被创建的,而不是如何被调用的。具体来说,箭头函数没有自己的 this 值,而是捕获(capture)了外层作用域中的 this 值。
这意味着,在箭头函数内部,this 的值与包围它的普通函数的 this 值相同。这通常是非常有用的,特别是在需要在回调函数或嵌套函数中使用外部函数的 this值时。
举个例子:
javascript
function Person() {
this.age = 0;
setInterval(() => {
this.age++; // this指向了Person对象
console.log(this.age);
}, 1000);
}
var p = new Person();
在上面的例子中,箭头函数捕获了 Person 对象的 this 值,因此无论 setInterval 如何调用这个箭头函数,this 都会指向 Person 对象。
相比之下,在传统的 JavaScript 函数中,this 的值是根据函数如何被调用而变化的,这可能会导致一些意外的行为和 bug。箭头函数的引入使得在 JavaScript 中更容易管理 this的值,尤其是在复杂的嵌套函数中。
5. 常见错误和陷阱
在使用this关键字时,初学者常常会遇到一些常见的错误和陷阱。本节将列举一些常见的错误,并提供避免这些错误的建议和技巧,帮助读者避免在使用this关键字时犯错。
当使用JavaScript中的this关键字时,有一些常见的陷阱和错误可能会导致程序行为出乎意料。以下是一些常见的陷阱和错误:
全局上下文中的this指向全局对象
在浏览器环境中通常是window对象,在Node.js环境中是global对象。如果不小心在全局上下文中误用this,可能会对全局对象造成意外影响。
箭头函数中的this指向
箭头函数的this并不是动态绑定的,而是从外层作用域继承而来。如果不理解箭头函数中this的行为,可能会导致预期之外的结果。
在闭包中使用this造成混乱
在闭包中使用this可能会导致意外的行为,因为this的值是在函数被调用时确定的,而不是在函数被创建时确定的。
忘记使用new关键字调用构造函数
如果在创建对象实例时忘记使用new关键字调用构造函数,那么this将指向全局对象,而不是新创建的对象实例。
这些陷阱和错误都与对this的理解和使用有关,通常可以通过更深入地理解JavaScript中this的工作原理以及谨慎地编写代码来避免。希望以上列举的常见陷阱和错误能够帮助你更好地避免在使用this时出现问题。
总结
本文探讨了JavaScript中的this关键字。我们从基本概念开始,讨论了函数中的this、隐式绑定和显式绑定、箭头函数与this,以及常见错误和陷阱。最后,我们探讨了this关键字在事件处理中的应用。通过阅读本文,希望各位读者能够对JavaScript中的this关键字有更深入的理解,并能够更好地应用它。