JavaScript中this指向的速记方法

JavaScript中this指向的速记方法

前言

JavaScript中的this指向是许多开发者容易混淆的概念,但掌握它对于编写高质量的JavaScript代码至关重要。本文将提供一套简单易记的规则,帮助你快速判断this的指向,并通过实例加深理解。

一、this指向的基本规则

1. 全局上下文中的this

在全局执行上下文中(非严格模式下),this指向全局对象:

1
2
3
4
5
// 浏览器环境
console.log(this === window); // true

// Node.js环境
console.log(this === global); // true

速记口诀:全局上下文,this指向全局对象。

2. 函数调用中的this

2.1 普通函数调用

1
2
3
4
5
function fn() {
console.log(this);
}

fn(); // 非严格模式下指向window,严格模式下为undefined

速记口诀:普通函数调用,非严格模式指向全局,严格模式为undefined。

2.2 对象方法调用

1
2
3
4
5
6
7
8
const obj = {
name: '张三',
sayName() {
console.log(this.name);
}
};

obj.sayName(); // "张三"

速记口诀:对象方法调用,this指向调用该方法的对象。

2.3 构造函数调用

1
2
3
4
5
6
function Person(name) {
this.name = name;
}

const p = new Person('李四');
console.log(p.name); // "李四"

速记口诀:构造函数调用,this指向新创建的实例对象。

2.4 call/apply/bind调用

1
2
3
4
5
6
7
8
9
function fn() {
console.log(this.name);
}

const obj = { name: '王五' };
fn.call(obj); // "王五"
fn.apply(obj); // "王五"
const boundFn = fn.bind(obj);
boundFn(); // "王五"

速记口诀:call/apply/bind调用,this指向指定的第一个参数。

二、特殊情况与陷阱

1. 箭头函数中的this

箭头函数没有自己的this,它会捕获其所在上下文的this值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = {
name: '赵六',
regularFn() {
console.log(this.name); // "赵六"

const arrowFn = () => {
console.log(this.name); // "赵六" - 继承外层this
};

arrowFn();
}
};

obj.regularFn();

速记口诀:箭头函数没有自己的this,继承外层作用域的this

2. 事件处理函数中的this

1
2
3
4
5
6
7
8
9
const btn = document.getElementById('myBtn');

btn.addEventListener('click', function() {
console.log(this); // 指向触发事件的元素(btn)
});

btn.addEventListener('click', () => {
console.log(this); // 箭头函数,继承外层作用域的this
});

速记口诀:事件处理函数中,普通函数指向触发元素,箭头函数继承外层this

3. 定时器中的this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = {
name: '钱七',
regularFn() {
setTimeout(function() {
console.log(this.name); // undefined - this指向全局对象
}, 100);

setTimeout(() => {
console.log(this.name); // "钱七" - 继承外层this
}, 100);
}
};

obj.regularFn();

速记口诀:定时器回调中,普通函数指向全局,箭头函数继承外层this

三、this指向判断流程图

1
2
3
4
5
6
7
8
9
10
graph TD
A[开始判断this指向] --> B{是箭头函数吗?}
B -->|是| C[继承外层作用域的this]
B -->|否| D{是new调用吗?}
D -->|是| E[指向新创建的实例]
D -->|否| F{是call/apply/bind调用吗?}
F -->|是| G[指向指定的第一个参数]
F -->|否| H{是对象方法调用吗?}
H -->|是| I[指向调用该方法的对象]
H -->|否| J[指向全局对象或undefined]

四、实用技巧与解决方案

1. 保存this引用

在ES6之前,常用变量保存this引用:

1
2
3
4
5
6
7
8
9
const obj = {
name: '孙八',
regularFn() {
const self = this; // 保存this引用
setTimeout(function() {
console.log(self.name); // "孙八"
}, 100);
}
};

2. 使用箭头函数

ES6+推荐使用箭头函数:

1
2
3
4
5
6
7
8
const obj = {
name: '周九',
regularFn() {
setTimeout(() => {
console.log(this.name); // "周九"
}, 100);
}
};

3. 使用bind方法

1
2
3
4
5
6
7
8
const obj = {
name: '吴十',
regularFn() {
setTimeout(function() {
console.log(this.name); // "吴十"
}.bind(this), 100);
}
};

五、常见面试题解析

1. 对象方法中的this

1
2
3
4
5
6
7
8
9
10
const obj = {
name: '对象',
getName() {
return this.name;
},
getNameArrow: () => this.name
};

console.log(obj.getName()); // "对象"
console.log(obj.getNameArrow()); // undefined - 箭头函数继承全局this

2. 嵌套函数中的this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const obj = {
name: '嵌套',
outer() {
console.log(this.name); // "嵌套"

function inner() {
console.log(this.name); // undefined - this指向全局
}

inner();
}
};

obj.outer();

3. 链式调用中的this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const calculator = {
value: 0,
add(num) {
this.value += num;
return this; // 返回this以支持链式调用
},
multiply(num) {
this.value *= num;
return this;
},
getResult() {
return this.value;
}
};

const result = calculator.add(5).multiply(2).add(3).getResult(); // 13

六、this指向速记表

调用方式 this指向 示例
全局上下文 全局对象 console.log(this)
普通函数调用 全局对象/undefined fn()
对象方法调用 调用该方法的对象 obj.method()
构造函数调用 新创建的实例 new Constructor()
call/apply/bind 指定的第一个参数 fn.call(obj)
箭头函数 继承外层作用域的this () => {}
DOM事件处理函数 触发事件的元素 element.onclick = function() {}
定时器回调 全局对象/继承外层this setTimeout(fn, 100)

七、总结

掌握JavaScript中this的指向规则是每个前端开发者的必备技能。通过本文提供的速记方法和规则,你可以快速判断各种场景下this的指向:

  1. 全局上下文this指向全局对象
  2. 普通函数调用:非严格模式指向全局,严格模式为undefined
  3. 对象方法调用this指向调用该方法的对象
  4. 构造函数调用this指向新创建的实例
  5. call/apply/bind调用this指向指定的第一个参数
  6. 箭头函数:没有自己的this,继承外层作用域的this