JS中this的指向

JS中this的指向

  1. this永远指向一个对象

  2. this的指向完全取决于函数调用的位置

  3. JavaScript支持运行环境动态切换,this的指向是动态的

全局上下文(Global Context)

在全局执行环境中(在任何函数体外部),this都是指向全局对象,在浏览器中,window对象即是全局对象。

// 在浏览器中,window 对象同时也是全局对象:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"
console.log(this.b)    // "MDN"

函数上下文(Function Context)

在函数内部,this的值取决于函数的调用方式。这包括普通函数调用、作为对象的方法调用、构造函数调用、以及通过call、apply或bind方法调用。

普通函数调用

如果一个函数不是作为对象的方法被调用,而是作为普通函数被调用,那么 this 通常指向全局对象(在严格模式下,this 是 undefined)

var name = 'window';
var doSth = function(){
  console.log(this.name);
}
doSth();  // 'window'

let 不给顶层对象添加属性(浏览器为Window)

let name2 = 'window2';
let doSth2 = function(){
  console.log(this === window);
  console.log(this.name2);
}
doSth2() // true, undefined

严格模式下,正常函数中的 this 行为不同,如未定义输出undefined

'use strict'
var name3 = 'window3';
var doSth3 = function(){
    console.log(typeof this === 'undefined');
    console.log(this.name3);
}
doSth3();

window.doSth3();

作为对象的方法调用

如果一个函数作为对象的方法被调用,this 就指向这个对象

var name = 'window';
var doSth = function(){
    console.log(this.name);
}
var student = {
    name: 'dog',
    doSth: doSth,
    other: {
        name: 'other',
        doSth: doSth,
    }
}

student.doSth(); // 'dog'
// call like this
student.doSth.call(student);

student.other.doSth(); // 'other'
// call like this
student.other.doSth.call(student.other);

将对象中的函数分配给变量,实际上又是一个普通函数,所以使用普通函数的规则(默认绑定)。

var studentDoSth = student.doSth;
studentDoSth(); // 'window'
// call like this :
studentDoSth.call(undefined);

o.f() 被调用时,函数内的 this 将绑定到 o 对象

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

也可以先定义函数,然后再将其附属到o.f,这样做的结果是一样的

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // 37

原型链中的 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
构造函数调用

当一个函数用作构造函数时(使用new关键字),this 指向新创建的对象。

function Foo() {
  this.name = 'Test';
  console.log(this);
}

// 使用new关键字调用
var obj = new Foo();  // this指向新创建的obj对象
console.log(obj.name); // 输出 'Test'

// 不使用new关键字调用
Foo();  // this指向全局对象(非严格模式)或undefined(严格模式)
console.log(window.name); // 在非严格模式下,输出 'Test'
  1. 如果构造函数没有返回对象(也就是没有返回值或者返回非对象),那么new表达式的结果就是新创建并且被this关键字指向的对象。
  2. 如果构造函数返回一个对象,那么new表达式的结果就是这个返回的对象。
function C(){
  this.a = 37;
}

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


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

o = new C2();
console.log(o.a); // logs 38
箭头函数

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

this 被设置为它被创建时的环境

var name = 'window';
var student = {
    name: 'dog',
    doSth: function(){
        // var self = this;
        var arrowDoSth = () => {
            // console.log(self.name);
            console.log(this.name);
        }
        arrowDoSth();
    },
    arrowDoSth2: () => {
        console.log(this.name);
    }
}
student.doSth(); // 'dog'
student.arrowDoSth2(); // 'window'

不能通过call、apply、bind来绑定箭头函数的this(它本身没有this

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

// 作为对象的一个方法调用
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 尝试使用 call 来设定 this
console.log(foo.call(obj) === globalObject); // true

// 尝试使用 bind 来设定 this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

call、apply、bind可以绑定缓存箭头函数上面的普通函数的this

var student = {
    name: 'dog',
    doSth: function(){
        console.log(this.name);
        return () => {
            console.log('arrowFn:', this.name);
        }
    }
}
var person = {
    name: 'person',
}
student.doSth().call(person); // 'dog'  'arrowFn:' 'dog'
student.doSth.call(person)(); // 'person' 'arrowFn:' 'person'
回调函数

回调函数中 this 的指向,决定于执行回调函数 时的执行上下文环境

(function(){
  console.log(this); // window
})();

setTimeout(() => {
  console.log(this); // window
}, 0);

setTimeout(function(){
  console.log(this); // window
}, 0);

组合使用

第一个setTimeout,执行obj.getage 之后,相当于setTimeout的回调是一个匿名函数,执行的时候,函数内部未设置this的指向。相当于是普通函数调用。所以this默认指向window,所以结果是undefined。

第二个setTimeout,传给setTimeout的也是一个匿名回调函数,执行匿名函数,执行到 obj.getage() 的时候,getage函数里的this,指向的就是obj了,所以能打印出10。遵循 谁调用产生 this指针的函数,this就指向谁的规则

var obj = {
    age:10,
    getage:function(){
        console.log(this.age)
    }
}

setTimeout(obj.getage,1000)   // undefined

setTimeout(function(){
    obj.getage()  // 10
},1000)
let obj={
  a:222,
  fn:function(){    
    setTimeout(()=>{console.log(this.a)});
  }
};
obj.fn(); // 222
var name = 'window'; 
var A = {
  name: 'A',
  sayHello: () => {
    console.log(this.name)
  }
}

A.sayHello(); // 输出的是window,根据刚才讲的规则就可以判断

// 那如何改造成永远绑定A呢:

var name = 'window'; 
var A = {
  name: 'A',
  sayHello: function(){
    var s = () => console.log(this.name)
    return s//返回箭头函数s
  }
}

var sayHello = A.sayHello();
sayHello();// 输出A 
let obj = {
  value: 'Hello, World',
  print: function() {
    setTimeout(function() {
      console.log(this.value);
    }, 1000);
  }
};

obj.print();  // 输出:undefined(严格模式)或者一个全局value(非严格模式)


let obj = {
  value: 'Hello, World',
  print: function() {
    setTimeout(() => {
      console.log(this.value);  // 输出:Hello, World
    }, 1000);
  }
};

// 或者

let obj = {
  value: 'Hello, World',
  print: function() {
    setTimeout(function() {
      console.log(this.value);
    }.bind(this), 1000);
  }
};

事件处理器上下文(Event Handler Context)

在DOM事件处理器中,this通常指向触发事件的元素。

在事件处理函数(或者说事件监听器)中,this通常指向触发事件的 DOM 元素。第一个事件监听器中,this打印出来的是按钮元素本身。

bluify函数作为事件处理函数使用,因此在这个函数中,this指向触发点击事件的按钮元素。

let btn = document.getElementById('btn');

btn.addEventListener('click', function () {
  console.log(this);  // button
});

btn.addEventListener('click', bluify, false);

function bluify(e) {
  console.log(this === e.currentTarget); // 总是 true

  // 当 currentTarget 和 target 是同一个对象时为 true
  console.log(this === e.target);
  this.style.backgroundColor = '#A5D9F3';
}

通过 call,apply,bind 改变this指向

  • call(a, b, c方法接收三个参数,第一个是this指向,第二个,三个是传递给函数的实参,可以是数字,字符串,数组等类型的数据类型都可以

  • apply(a, [b])和call基本上一致,唯一区别在于传参方式,apply把需要传递给fn()的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn()一个个的传递

  • bind(a, b, c:语法和call一模一样,区别在于立即执行还是等待执行

fn.bind(第一个参数是this的指向)
fn.call(第一个参数是this的指向,parma2,param3...)
fn.apply(第一个参数是this的指向,[parma2,parma3..])
//call()方法:改变fn中的this,并且把fn立即执行
fn.call(obj, 1, 2); 
//bind()方法:改变fn中的this,fn并不执行
fn.bind(obj, 1, 2); 
var obj = {
  name:'111',
  getName:function(){
    console.log(this.name)
  }
};

var otherObj = {
  name:'222',
};

var name = '333';
        
obj.getName();               // 111
obj.getName.call();          // 333
obj.getName.call(otherObj);  // 222
obj.getName.apply();         // 333
obj.getName.apply(otherObj); // 222
obj.getName.bind(this)();    // 333
obj.getName.bind(otherObj)();// 222
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

anjushi_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值