前言

首先需要明确的是,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指向分两种情况:

  1. 当代码被内联处理函数调用时,它指向监听器所在的DOM元素
  2. 当代码被包括在函数内部执行时,其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值。需要注意的是:

  1. call(),apply(),bind()方法对于箭头函数来说只是传入参数,对它的this毫无影响
  2. 考虑到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

版权声明:本文为jsunwang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/jsunwang/p/13393353.html