js this指向问题以及改变this指向的三种方法(call/apply/bind)
在JavaScript
中,this
指向是一个很令人头疼的一个东西,由于JavaScript
可以运行在浏览器环境中,也可以运行在node
环境中,两者的this
指向有着不同的特性,this
指向问题往往会令你的代码朝着意想不到的方向运行,因此JavaScript
也提供了几种修改this
指向的方法,我们一一说明。
浏览器环境中this
的指向
下面先介绍在浏览器环境中this
的指向问题;
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 全局模式下,this指向window
console.log(this);
// 全局变量是设置在window上的,因此下面的写法是一样的
a = 10; // window.a = 10
this.b = 10;
console.log(a === b); // true
function fn1() {
console.log(this);
}
fn1(); // this指向window
window.fn1(); // this还是指向window
var obj = {
c: 10,
fn2: function () {
console.log(this);
}
}
obj.fn2() // this指向obj
window.obj.fn2() // this指向obj
var obj2 = {
d: 10,
e: {
f: 10,
fn3: function () {
console.log(this);
}
}
}
window.obj2.e.fn3() // this指向e
let fn4 = window.obj2.e.fn3;
fn4() // this指向window
</script>
</body>
</html>
总结:
this
在定义的时候不会指定,只有在函数调用的时候才会指定this
指向的是在调用函数时最近的调用者
node
环境中this
的指向
而在node
环境中,this
的指向和浏览器中有着较大出入;
在全局打印this
,这里的this
是一个空对象{}
console.log(this); // {}
在浏览器环境中,这里的结果是true
,但是在node
环境中,会报错,因为变量b
是定义在this上的,而this
指向的是一个空对象,而a === b
中的变量b
是根本没有定义的
a = 10;
this.b = 10;
console.log(a === b); // ReferenceError: b is not defined
有趣的是,在函数中,this指向的又不是空对象,而是global
对象,指向函数fn1
的调用者global
function fn1() {
console.log(this);
}
fn1(); // Object [global] {...}
var obj = {
c: 10,
fn2: function () {
console.log(this);
}
}
obj.fn2() // { c: 10, fn2: [Function: fn2] } => 指向obj
var obj2 = {
d: 10,
e: {
f: 10,
fn3: function () {
console.log(this);
}
}
}
obj2.e.fn3() // { f: 10, fn3: [Function: fn3] } => 指向e
总结:
node
环境中直接打印的this
指向空对象,浏览器环境直接打印this
指向window
this
指向和浏览器环境中是一样的,只是区别在于,浏览器环境全局是window
,而node
环境全局是global
改变this
指向的三种方法;
call
function fun1(a, b, c) {
console.log(this);
console.log(a + b + c);
}
obj = { name: 'jerry', age: 18 };
fun1() // 这里函数里的this指向window对象
fun1.call(obj, 1, 2, 3); // 这里函数中的this指向obj,而不是fun1
apply
apply
和call
的区别是apply
传递的参数需要使用数组,而call
直接传就行了
function fun2(a, b, c) {
console.log(this);
console.log(a + b + c);
}
fun2.apply(obj, [1, 2, 3])
bind
bind
区别是bind
只会改变this
指向,并不会执行函数,而call
和apply
不仅改变this
指向,并且会执行函数
function fun3(a, b, c) {
console.log(this);
console.log(a + b + c);
}
fun3.bind(obj, 1, 2, 3); // 并不会执行fun3
需要特别注意的是,bind方法并不会直接去更改函数的this指向,而是返回一个新的修改了this指向的函数,如果要调用,则需要使用其他变量接收或者直接调用
function fun3(a, b, c) {
console.log(this);
console.log(a + b + c);
}
fun3.bind(obj, 1, 2, 3); // 并不会执行fun3
fun3() // 这里的this指向并没有发生改变
// 如果想要调用,用变量接收
let newFun3 = fun3.bind(obj, 1, 2, 3);
newFun3()
// 或者直接调用
fun3.bind(obj, 1, 2, 3)()
一个bind方法的小例子
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>获取验证码</button>
<script>
// 注意这里,如果不加bind(this),那么定时器里面的this指向的是window
// 这里加了bind(this),那么定时器里面的this就指向了btn事件源,从而可以使用this来操作btn
let btn = document.querySelector('button');
btn.onclick = function () {
let time = 10;
this.disabled = true;
this.innerText = `重新获取(${time})`
this.timer = setInterval(function () {
time -= 1;
this.innerText = `重新获取(${time})`
if (time == 0) {
this.disabled = false;
this.innerText = `获取验证码`
clearInterval(this.timer)
}
}.bind(this), 1000)
}
</script>
</body>
</html>
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com