在平时写js、react等等这些代码的时候,经常会遇到使用this的情况,当自己信心满满的写完代码,点击运行的时候,发现报错 this.xxx is undefined
或者 this.xxx is not a function
,但自己的代码里面明明定义了 xxx
的。
为什么会出现这些错误呢,其实就是 this
指向的对象里面没有定义的 xxx
。
所以搞懂 this
的指向是非常有必要的。
一、执行上下文
在开始说this之前,先简单说一下执行上下文
执行上下文有三个部分:
- 变量对象(Variable Object,VO)
- 作用域链
- this
这里里面的this就是本文要说的 this
执行上下文又分为:
- 全局执行上下文
- 函数执行上下文
全局执行上下文是在代码执行时就创建了,函数执行上下文是在函数调用的时候创建的,同一个函数,在不同时刻调用,会创建不同的执行上下文。
二、this的指向
由于this存在于执行上下文当中,而执行上下文又是在代码执行和函数调用的时候创建的,所以this的指向也是在代码执行和函数调用时确定的。
可以this使用的不同场景,将this的指向分为三种情况:
- 全局中的this
- 函数中的this
- 构造函数中的this
全局中的this
在浏览器中,全局中的this指向的是 window
,node.js中全局的this指向 globel
。
var a = 1;
console.log(this); // window
console.log(this.a); // 1
console.log(window.a); // 1
console.log(this === window); // true
函数中的this
根据函数调用方式,又可以分为以下两种情况:
- 当函数作为对象中的方法被调用时的this
- 当函数被独立调用时的this
声明式函数中的this
1、对象方法中的this
当函数作为对象中的方法被调用时,函数中this指向的是,函数的拥有者。
var a = 111;
var obj = {
a: 222,
func: function() {
console.log(this); // obj
console.log(this.a); // 222
}
}
obj.func();
在这里 func函数
,作为 obj对象
里面的方法被调用,所以 func
里面的 this
指向的就是 func函数
的拥有者,在这里就是 obj
2、被独立调用时
当函数被独立调用时,函数里面的 this
就指向全局上下文里面的 this
,即 window
;在 严格模式
下,this使 undefined
。
var a = 111;
function func1(){
var a = 222;
console.log(this); // window
console.log(this.a); // 111
}
func1();
function func2(){
"use strict"
console.log(this); // undefined
}
func2();
ES6箭头函数中的this
箭头函数中this指向是在函数定义时确定的,这一点和声明式函数定义有所区别。
在箭头函数定义时,函数this指向,定义时所在执行上下文里面的this
var func = () => {
console.log(this); // window
}
func();
func 函数是一个箭头函数,所以 func 函数里面的 this 是在其定义是确定的,定义时所在的执行上下文是全局执行上下文,上下文中的 this 指向的是 window,所以 func 函数的 this 指向的也是this。
var a = 111;
var obj = {
a: 222,
func: function() {
var fun2 = () => {
var a = 333;
console.log(this.a);
}
fun2();
}
}
obj.func();
在这段代码里,当 func函数 调用时,func 里面的 this 指向的是 obj,在 func函数里面定义了一个箭头函数,所以 func2函数 里面的 this 指向的是 func函数 执行上下文里面的 this,所以最后打印出来的是 obj对象
里面的 a(222)
。
构造函数中的this
构造函数new创建对象的过程:
- 创建一个新对象;
- 将构造函数的 this 指向这个新对象;
- 执行构造函数内部代码;
- 返回这个新对象
构造函数中的this指向生成的对象
function Obj(a){
this.a = a;
this.func = function(){
console.log(this);
console.log(this.a);
}
}
var obj = new Obj(2);
obj.func();
在这段代码当中,在使用 new
实例化对象之后,调用对象里面的func函数,这里的func函数就是 obj对象
里面的一个方法,所以func函数里面的 this,就指向函数的拥有者,所以这里打印的结果是 2
。
三、改变this指向的方法
可以使用call,apply,bind来改变函数里面的this的指向
var a = 111;
var obj1 = {
a: 222,
func: function() {
console.log(this); // {a: 333}
console.log(this.a); // 333
}
}
var obj2 = {
a: 333,
}
obj1.func.call(obj2,);
这里调用obj1里面的func方法的时候,在后面加了一个call,它会吧func里面的this改变为指向call里面指定的 obj2,所以最后打印的是 333 ,在这里调用call,函数会立即执行。
apply方法和call的作用是一样的,他们之间的区别就是,call在传递其他参数时,是一个一个传的,而apply是传递一个数组,这样当传递的数据个数增多的时候,就不用像call一样,挨个写道call后面的括号里了。
比如:
obj.func.call(obj2,param1,pram2,param3)
obj.func.aplly(obj2,[param1,pram2,param3])
bind也可以改变函数里面的this指向,它和call和apply的区别就是,它返回一个新的函数,并不会和call和apply一样立即执行。
四、其他情况下的this指向
当函数作为DOM事件处理函数
当函数被用作事件处理函数时,它的 this 指向触发事件的元素。
function func(){
console.log(this); // element
}
var button = document.getElementsByTagName('button');
button.addEventListener('click', func);
当使用 addEventListener
为DOM元素添加事件时,函数的this会指向该DOM元素。
当作为一个内联事件处理函数
<button onclick="alert(this.tagName.toLowerCase());">
button
</button>
<button onclick="alert((function(){console.log(this)})())" >
触发事件回调
</button>
这段代码会在控制台打印 window
。
所以这里需要注意的是,当函数作为内联事件时,函数外层的this是指向DOM元素的,但当函数调用时,它是属于被独立调用的函数,所以函数里面的this指向的是window。
React中的this
在React中使用 class
来定义一个组件,在 render
里面的 this
指向的还是实例化之后的对象(组件),但是将组件里面的函数作为 onClick
回调的时候,函数里面的this指向的是它的拥有者。
import React, { PureComponent } from 'react';
class App extends PureComponent {
constructor(props) {
super(props);
this.state = {
ldate: new Date()
}
// this.startTick = this.startTick.bind(this);
// this.stopTick = this.stopTick.bind(this);
// this.tick = this.tick.bind(this);
}
componentDidMount() {
// this.TimeID = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.TimeID);
}
startTick(){
console.log(this);
this.TimeID = setInterval(this.tick, 1000);
}
stopTick() {
clearInterval(this.TimeID);
}
tick() {
console.log(this);
this.setState({
ldate: new Date()
})
}
render() {
const { ldate: localstate } = this.state;
return (
<div className='app'>
<div>{localstate.toTimeString()}</div>
<button onClick={this.startTick}>点击开始</button>
<button onClick={this.stopTick}>点击结束</button>
</div>
)
}
}
export default App;
这段代码,由于render是App组件里面的一个方法,所以render里面的this指向的是App实例化之后的对象(App),在render里面使用将App里面的方法作为点击事件的回调时,onClick={this.startTick}
中的this是指向的App,所以startTick函数可以被拿到;
当点击事件触发时,由于React在内部对 JSX
代码进行了处理,将回调函数的this进行了绑定,所以startTick触发时的this是 undefined
(这里是作者的个人推测);
在startTick函数中,将 this.tick
函数作为计时函数的回调,由于this是undefined,所以找不到tick函数,会报错。
解决React里面this的指向问题
- 使用bind在constructor里面将组件中函数的this和组件绑定
- 在函数定义时,使用箭头函数定义
constructor(props) {
super(props);
this.state = {
ldate: new Date()
}
this.startTick = this.startTick.bind(this);
this.stopTick = this.stopTick.bind(this);
this.tick = this.tick.bind(this);
}
startTick = () => {
this.TimeID = setInterval(this.tick, 1000);
}
参考资料
MDN-this
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。