js中的this指向详解

js中的this指向详解

摘要:

在非箭头函数下, this 指向调用其所在函数的对象,而且是离谁近就是指向谁(此对于常规对象,原型链, getter & setter等都适用);构造函数下,this与被创建的新对象绑定;

DOM事件,this指向触发事件的元素;

内联事件分两种情况

bind绑定, call & apply 方法等

全局环境下的情况

在全局环境下,this始终指向全局对象(window)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MLrW8z8-1617796454132)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407115209430.png)]

函数调用

普通函数内部的this分为两种情况,严格模式和非严格模式,非严格模式下,this默认指向全局对象window,严格模式下this为undefined

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TaKNUUOK-1617796454134)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407115412749.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YkOb07kG-1617796454138)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407115435743.png)]

对象中的this

对象内部的方法的this指向调用这些方法的对象

  • 1.函数定义的位置不影响其this的指向,this指向只和调用函数的对象有关

  • 2.多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象,而非window)

    //1
    var o = {
      prop: 37,
      f: function() {
        return this.prop;
      }
    };
    console.log(o.f());  //37
    var a = o.f;
    console.log(a()) //undefined
    
    var o = {prop: 38};
    
    function independent() {
      return this.prop;
    }
    
    o.f = independent;
    
    console.log(o.f()); // logs 38
    
    //2
    o.b = {
      g: independent,
      prop: 42
    };
    console.log(o.b.g()); // logs 42
    

原型链中的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 函数并执行,但这与函数内部this指向对象 p 没有任何关系,只需记住谁调用指向谁。

以上对于函数作为getter & setter 调用时同样适用。

构造函数中this

构造函数中的this与被创建的新对象绑定。

注意:当构造器返回的默认值是一个this引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回this。

function C(){
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37


function C2(){
  this.a = 37;
  return {a:38};
}

var b = new C2();
console.log(b.a); // logs 38

call & apply

当函数通过Function对象的原型中继承的方法 call() 和 apply() 方法调用时, 其函数内部的this值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它。

function add(c, d){
  return this.a + this.b + c + d;
}

var o = {a:1, b:3};

console.log(add.call(o, 5, 7)); // 1 + 3 + 5 + 7 = 16

console.log(add.apply(o, [10, 20])); // 1 + 3 + 10 + 20 = 34

function tt() {
  console.log(this);
}

tt.call(5);  // Number {[[PrimitiveValue]]: 5}
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

bind 方法

bind方法在ES5引入, 在Function的原型链上, Function.prototype.bind。通过bind方法绑定后, 函数将被永远绑定在其第一个参数对象上, 而无论其在什么情况下被调用。

function f(){
  return this.a;
}

var g = f.bind({a:"qwer"});
console.log(g()); // qwer

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, qwer

DOM 事件处理函数中的 this & 内联事件中的 this

DOM事件处理函数

当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件

function bluify(e) {
  //在控制台打印出所点击元素
  console.log(this);
  //阻止时间冒泡
  e.stopPropagation();
  //阻止元素的默认事件
  e.preventDefault();
  this.style.backgroundColor = '#f3080e';
}

// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');

// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', bluify, false);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y3B6B04E-1617796454142)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151311635.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rqoisjw4-1617796454145)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151335775.png)]

内联事件

内联事件中的this指向分两种情况:

  • 1.当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
  • 2.当代码被包括在函数内部执行时,其this指向等同于 函数直接调用的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFemV2Xe-1617796454147)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151603752.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3O9J4RW3-1617796454149)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151653055.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twxUkeSv-1617796454150)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151634531.png)]

setTimeout & setInterval

对于延时函数内部的回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)

  function Person() {
    this.age = 0;
    setTimeout(function() {
      console.log(this);
    }, 3000);
  }

  var p = new Person();//3秒后返回 window 对象
  // ==============================================
//通过bind绑定
    function Person() {
      this.age = 0;
      setTimeout((function() {
        console.log(this);
      }).bind(this), 3000);
    }

  var p = new Person();//3秒后返回构造函数新生成的对象 Person{...}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6cFOvs0T-1617796454151)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151937028.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tv9uoFLx-1617796454154)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407151955251.png)]

箭头函数中的 this

由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值,

  • 1.所以 call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。

  • 2.考虑到 this 是词法层面上的,严格模式中与 this 相关的规则都将被忽略。(可以忽略是否在严格模式下的影响)

  • 3.call,apply,bind这些函数不能改变箭头函数this的值

因为箭头函数可以捕获其所在上下文的this值 所以

function Person() {  
    this.age = 0;  
    setInterval(() => {
        // 回调里面的 `this` 变量就指向了期望的那个对象了
        this.age++;
    }, 3000);
}

var p = new Person();

以上代码可以得到我们所以希望的值,下图可以看到,在setTimeout中的this指向了构造函数新生成的对象,而普通函数指向了全局window对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-axlxWBlk-1617796454155)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407152231179.png)]

对于是否严格模式示例代码(可以复制进控制台进行验证)严格模式下普通函数的this应该为undefined

var f = () => {'use strict'; return this};
var p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D9mKeYqt-1617796454157)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407152533182.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jYMjhIS-1617796454158)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407152546046.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15fLteDg-1617796454159)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407152656933.png)]

call 无法改变箭头函数this的指向

var adder = {
  base : 1,
    
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function inFun(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3,其内部的this并没有因为call() 而改变,其this值仍然为函数inFun的this值,指向对象adder

以上的箭头函数都是在方法内部,总之都是以非方法的方式使用,如果将箭头函数当做一个方法使用会怎样呢?

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 {...}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSlaEfsM-1617796454161)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407152915002.png)]

可以看到,作为方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象

在function中有一个箭头函数会怎么样?里面的箭头函数会捕获外面的function的this的值

function a() {
  this.b = 10;
  let c = () => {
    console.log(this);
  };
  c();
}
a();
当a自己调用的时候,他的this应该是window,所以箭头函数的c应该是window

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mtmgk74k-1617796454162)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407154317041.png)]

function a() {
  this.b = 10;
  let c = () => {
    console.log(this);
  };
  c();
}

a();
let obj = {
  a: a
};
obj.a();
//当a在obj中调用的时候,a的this就是obj,所以箭头函数c的this也应该是obj

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BaTy6RB6-1617796454164)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20210407154518354.png)]

console.log(this);
};
c();
}
a();
当a自己调用的时候,他的this应该是window,所以箭头函数的c应该是window




[外链图片转存中...(img-Mtmgk74k-1617796454162)]



function a() {
this.b = 10;
let c = () => {
console.log(this);
};
c();
}

a();
let obj = {
a: a
};
obj.a();
//当a在obj中调用的时候,a的this就是obj,所以箭头函数c的this也应该是obj




[外链图片转存中...(img-BaTy6RB6-1617796454164)]



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值