JavaScript this
this 指向的是当前的代码上下文环境,所以不同情况下的 this 指向也不同。
1. 全局下的 this
在全局环境下,this
指向全局对象。
全局对象和宿主环境相关,在浏览器下,全局对象就是 window
对象,在 node.js
中,全局对象是 global
对象。
window === this; // 输出:true
新的标准提供了
globalThis
关键字来获取全局对象,这样就能抹平宿主的差异来操作处理全局对象了。
2. 函数中的 this
函数在不同情况下,其 this
的指向也不同。
2.1 对象下的方法
var person = {
age: ,
name: '鸽子王',
skill: '放鸽子',
say: function() {
console.log('来一段自我介绍:');
console.log('我是' + this.name);
console.log('我今年' + this.age + '岁');
console.log('我最擅长' + this.skill);
},
};
person.say();
2.2 原型链上方法的 this
var __proto__ = {
sum: function() {
return this.number1 + this.number2;
},
};
var object = Object.create(__proto__);
object.number1 = ;
object.number2 = ;
console.log(
object.sum(),
); // 输出:3
Object.create
做就就是将参数作为原型,创建一个对象。
所以 object
的第一原型就是 __proto__
对象。
2.3 getter / setter 下的 this
getter
和 setter
下的 this 也会指向调用该 getter
和 setter
的对象。
var object = {
_name: '鸽子王',
get name() {
return this._name;
},
set name(val) {
console.log(val);
this._name = val;
}
};
console.log(object.name); // 输出:鸽子王
object.name = '鸽子天王'; // 输出:鸽子天王
console.log(object.name); // 输出:鸽子天王
2.4 作为 DOM 节点的事件处理器
作为 DOM 节点的事件处理器的时,函数的 this
会指向这个 DOM 对象。
<div>
<button>点击我</button>
</div>
<script>
document.querySelector('button').addEventListener('click', function() {
this.innerHTML = '被点击了!';
});
</script>
2.5 作为一个内联的事件处理器
内联的事件处理器,其 this
指向的是 DOM 节点自身。
<div>
<button onclick="console.log(this); console.log(this === document.querySelector('button'))">点击我</button>
</div>
这个规则有局限性,只有最外层的 this 符合这个规则。
test
函数的 this 指向的是全局对象 window
。
2.6 其他大部分情况下
function fn() {
console.log(this);
console.log(this === window);
}
fn();
有的时候可能会搞混以下情况:
var object = {
username: '咸鱼',
fn: function() {
console.log(this.username);
function thisTest() {
console.log(this.username);
console.log(this === window);
}
thisTest();
},
};
object.fn();
这里 thisTest
方法输出的 username
就会是个 undefined,因为他的 this 指向的是 window,因为他不属于 object
对象的一个方法,所以 this 就指向了 window。
在回调函数中经常会碰到这个问题:
var info = {
account: '123',
password: '456',
login: function(cb) {
setTimeout(function() {
cb({
account: this.account,
password: this.password,
});
}, );
}
};
info.login(function(info) {
console.log(info);
});
通常会使用保留上层 this 的方式解决这个问题。
var info = {
account: '123',
password: '456',
login: function(cb) {
var _this = this;
setTimeout(function() {
cb({
account: _this.account,
password: _this.password,
});
}, );
}
};
info.login(function(info) {
console.log(info);
});
这样就能解决这个问题。
另外一个情况也很容易混淆 this :
var object = {
user: 'no.1',
say: function() {
console.log(this.user);
},
};
var say = object.say;
object.say(); // 输出:"no.1"
say(); // 输出:undefined
在严格模式中,这种情况下的 this
会变成 undefined
。
2.7 构造函数
在 JavaScript 构造函数也被成为 对象构造器
,用于产生对象。
function Point(x, y) {
this.x = x;
this.y = y;
}
var point = new Point(, );
console.log(point.x); // 输出:1
console.log(point.y); // 输出:2
var object = {
user: 'no.1',
};
function say() {
console.log(this.user);
}
say(); // 输出:undefined
say.call(object); // 输出:"no.1"
say.apply(object); // 输出:"no.1"
var object = {
user: 'no.1',
};
function fn(arg1, arg2, arg3) {
console.log(
this,
arg1,
arg2,
arg3,
);
}
fn.call(object, , , );
apply 的第二个参数是个数组,数组里面的项会按数组的顺序作为参数传递给函数。
var object = {
user: 'no.1',
};
function fn() {
console.log(
this,
arguments,
);
}
fn.apply(object, [, , ]);
3.2 bind
var obj1 = { value: '今天打砖' };
var obj2 = { value: '明天打转' };
var fn = function() {
console.log(this);
};
var bindFn1 = fn.bind(obj1)
var bindFn2 = bindFn1.bind(obj2);
bindFn1();
bindFn2();
可以看到 bindFn1
被绑定了 obj1
作为 this,之后不论怎么操作,他的 this 都会是 obj1
。
4. 小结
理解好 this 的处理机制可以设计出更加完善的 JavaScript 应用程序。