this指向
前言
首先需要明确的是,this的指向在函数定义的时候是无法确定的,只有函数执行的时候才能确定this到底指向谁。在非箭头函数下,this指向调用其所在函数的对象,而且是离谁近就指向谁(此对于常规对象,原型链,getter&setter等都适用);构造函数下,this与被创建的新对象绑定;DOM事件中,this指向触发事件的元素;还有内联事件、延时函数,箭头函数等情况。以下展开讨论
全局环境下
在全局环境下,无论是否严格模式,this始终指向全局对象(window)
console.log(this.document === document); // true
// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
函数上下文调用
函数直接调用
普通函数内部的this分【严格模式】和【非严格模式】两种情况
非严格模式下,this默认指向全局对象window
function f1(){
return this;
}
f1() === window; // true
严格模式下,this默认为undefined
function f2(){
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true
对象中的this
对象内部方法的this指向调用这些方法的对象。注意:
1.函数的定义无法确定this的指向,this指向只与调用函数的对象有关
2.多层嵌套的对象,内部方法的this指向离被调用函数最近的对象
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a);//12
}
}
}
o.b.fn();
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
为什么这里的this会指向window呢,首先我们知道看this指向就看执行的时候调用它的对象。上例中虽然函数fn是被对象b引用,但是在将fn赋值给变量j的时候并没有执行,而变量j是window的属性,所以当后面调用j()的时候指向的是window。
原型链中的this
原型链中的方法的this仍然是指向调用它的对象
var o = {
f : function(){
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
上例中,虽然在p中没有属性f,但当在执行p.f()时,会查找p的原型链,找到f函数并执行,这个时候是p调用f函数。
以上对于函数作为getter&setter时同样适用。
构造函数中的this
构造函数中的this与新创建的新对象绑定。
function Fn(){
this.user = "jjj";
}
var a = new Fn();
console.log(a.user); //jjj
这里对象a可以点出函数Fn里面的user是因为new时用变量a创建了一个Fn的实例,将构造函数中的this与新创建的实例化对象a绑定,相当于复制了一份Fn到对象a里面,此时仅仅是创建,并没有执行,而执行a.user时调用Fn的对象是a,那么this指向的自然就是a。
构造函数中有return时
function Fn(){
this.user = "jjj";
return { user: 'aaa' }
}
var a = new Fn();
console.log(a.user); //aaa
function fn()
{
this.user = 'jjj';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = 'jjj';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = 'jjj';
return 1;
}
var a = new fn;
console.log(a.user); //jjj
function fn()
{
this.user = 'jjj';
return undefined;
}
var a = new fn;
console.log(a.user); //jjj
function fn()
{
this.user = 'jjj';
return undefined;
}
var a = new fn;
console.log(a); //fn {user: "jjj"}
总结:如果返回值是一个对象,那么this指向的就是返回的那个对象,如果返回值不是一个对象,那么this还是指向函数的实例。
function fn()
{
this.user = 'jjj';
return null;
}
var a = new fn;
console.log(a.user); //jjj
值得注意的一点是,虽然null也是对象,但如果是返回了null,这个时候的this还是指向那个函数的实例,null比较特殊。
DOM事件处理函数
当函数被当做监听事件处理函数时,其this指向触发该事件的元素(针对于addEventListener事件)
// 被调用时,将关联的元素变成蓝色
function bluify(e){
console.log(this); //在控制台打印出所点击元素
e.stopPropagation(); //阻止时间冒泡
e.preventDefault(); //阻止元素的默认事件
this.style.backgroundColor = '#A5D9F3';
}
// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');
// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
内联事件
内联事件中的this指向分两种情况:
- 当代码被内联处理函数调用时,它指向监听器所在的DOM元素
- 当代码被包括在函数内部执行时,其this指向等同于函数直接调用的情况,即非严格模式指向全局对象window,严格模式指向undefined
页面
控制台打印
setTimeout & setInterval
对于延时函数内部的回调函数的this指向全局对象window
//默认情况下代码
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);// 打印出window对象
}, 3000);
}
var p = new Person();//3秒后返回 window 对象
当然也可以通过bind方法改变其内部函数的this指向
//通过bind绑定
function Person() {
this.age = 0;
setTimeout((function() {
console.log(this);// 打印出Person对象
}).bind(this), 3000);
}
var p = new Person();//3秒后返回构造函数新生成的对象 Person{...}
箭头函数中的this
箭头函数不绑定this,它会捕获其所在上下文的this值,作为自己的this值。需要注意的是:
- call(),apply(),bind()方法对于箭头函数来说只是传入参数,对它的this毫无影响
- 考虑到this是词法层面上的,可以忽略是否在严格模式下的影响。
function Person() {
this.age = 0;
setTimeout(() => {
console.log(this) // Person对象
}, 3000);
}
var p = new Person();
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this) // Window对象
}, 3000);
}
var p = new Person();
验证严格模式影响
var f = () => {'use strict'; return this};
var p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true
以上的箭头函数都是在方法内部,以非方法的方式调用,如果将箭头函数当做方法调用会怎样呢?
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log( this.i, this)
}
}
obj.b(); // undefined window{...}
obj.c(); // 10 Object {...}
可以看到,作为方法的箭头函数指向全局window对象,而普通函数则指向调用它的对象。
参考:
https://www.cnblogs.com/pssp/p/5216085.html
https://www.cnblogs.com/dongcanliang/p/7054176.html