跳到主要内容

理解this

Javascript 的this​

  • this是js对象中的一个特殊指针,它的指向根据环境不同而发生改变
  • 它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式
  • JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。下面我们将按照调用方式的不同,分别讨论 this 的含义

作为函数调用​

这是我们最常用的方法,这种调用方式属于全局调用,所以呢,这里的 This 就理所应当的成了全局对象window

js
function example(){    
this.n = 'hello world !';   
console.log(this.n);
console.log(this);
}   
example(); // hello world ! Window

很显然,调用 example(); 时,输出了 ‘hello world !’ 还有 Window;这时候说它是全局对象好像不具备什么说服力;咱换个姿势

js
 var n = 'hello world !';  
function example(){   
console.log(this.n);
}
example(); // hello world !

你瞧,又是 ‘ hello world ! ‘,不急,咱有一千种姿势等你来解锁;再换。

js
var n = 'hello world !';  
function example(){   
this.n = 0;  
}
example();  
console.log(n); // 0 !

总结 :上述例子中,普通函数的调用把 this 作为window对象的方法进行了调用。显然 this 指向了 Window 对象;

作为对象方法调用​

js
var name="Akita";
var dogs={
name:"Collie",
showName:function(){
console.log(this.name); //表示showName方法所在的当前对象的 name 属性
}
}
dogs.showName(); //输出 Collie
var otherName=dogs.showName;
otherName(); //输出 Akita ,实际上调用showName()方法

快来看,当执行 dogs.showName(); 时,输出 Collie (牧羊犬),说明这个时候的 This 是指向 dogs 这个对象的;

调用dogs.showName();方法时, showName方法所在的当前对象是 dogs ,所以就是调用 dogs.name 。

而当我们尝试把 dogs.showName 赋给 otherName 时,我们回头想想这个 showName() 是做什么用的,很显然,输出它函数的执行环境所指向对象的 name,而此时 otherName 变量相当于 Window 对象的一个属性,因此 otherName() 执行的时候相当于 window.otherName(),即 window 对象调用 otherName 这个方法,所以 this 关键字指向 window;所以就会输出 Akita(秋田犬)。

总结:对象方法调用:指向最后调用它的对象;

  • 如果在对象的方法里的this,这个this就会指向当前对象的某个属性
  • 如果把对象的某个属性(这个属性对应的值是个方法)赋值给一个变量,那么这个对象内部的this指向就会发生改变,指向这个变量所在的window对象的一个属性
text
foo() ---> foo.call(window)

obj.foo() --> obj.foo.call(obj)

作为构造函数调用​

JavaScript 中的构造函数也很特殊,构造函数,其实就是通过这个函数生成一个新对象(object),这时候的 This 就会指向这个新对象;如果不使用 new 调用,则和普通函数一样。

js
 function example(){   
this.n = 'hello world !';  
}   
var other = new example();  
console.log(other.n); //hello world !

我们为 example 这个函数 new(构造)了一个新的对象 other,那么 this 就会指向 other 这个对象,所以就会输出 ‘hello world

总结:构造函数调用的this:指向new出来的对象; 即构造函数中的this是指向当前对象实例的

使用 apply 或 call 调用​

apply()是函数对象的一个方法,它应用某一对象的一个方法,用另一个对象替换当前对象。

js
var n = 'hello world!' 
function example(){   
console.log(this.n);  
}   
var o={};  
o.n = 'hey~';   
o.m = example;   
o.m.apply();//hello world!

来看看这行代码,当apply()的参数为空时,就是没有对象去替换掉当前的对象,所以默认调用全局对象。因此,这时会输出’hello world!’,证明this指的是全局对象。

那么试试给apply()指定一个对象。

js
var n = 'hello world!';  
function example(){    
console.log(this.n);  
}  
var o={};  
o.n = 'hey~';
o.m = example;   
o.m.apply(o);//hey~

此时输出了’hey~’,说明对象替换成功,this 指向了 o 这个对象

在ES6中 (箭头函数)​

箭头函数不用担心this指向问题

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

js
let sum = (num1,num2) => {return num1 + num2;}
//箭头函数不用担心this指向问题
function demo(){
setTimeout(function(){
console.log(this);//指向window
},3000);
setTimeout(() => {
console.log(this);//指向{}
},3000);
}
let obj = {};
demo.call(obj);

DOM事件的绑定的this指向​

  • onclick和addEventerListener中 this 默认指向绑定事件的元素。
js
document.querySelector('h1').addEventListener('click',()=>{
console.log(this) //指向window
})
document.querySelector('h1').addEventListener('click',function (){
console.log(this) //指向当前对象
})

默认绑定和隐式绑定、显式绑定、new绑定​

默认绑定​

  • this默认绑定我们可以理解为函数调用时无任何调用前缀的情景,它无法应对我们后面要介绍的另外四种情况,所以称之为默认绑定,默认绑定时this指向全局对象(非严格模式)
  • 简单来说:全局环境中,this默认绑定到window (非严格模式)
  • 严格模式下 this的默认绑定值为 undefined
text
function foo(){
console.log(this === window);
}
foo(); //true

隐式绑定​

  • 如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上
js
function foo(){
console.log(this.a);
};
var obj1 = {
a:1,
foo:foo,
obj2:{
a:2,
foo:foo
}
}

//foo()函数的前面对象是obj1,this隐式绑定到obj1
obj1.foo();//1

//foo()函数的前面对象是obj2,this隐式绑定到obj2
obj1.obj2.foo();//2

隐式丢失的问题​

  • 隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window。这种情况容易出错却又常见
js
var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
//把obj.foo赋予别名bar,造成了隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象则毫无关系
var bar = obj.foo;
bar();//0 (严格模式则为undefined)

显式绑定​

  • 通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。对于被调用的函数来说,叫做间接调用

new绑定​

  • 如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定
js
function fn(){
this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}

严格模式下this指向​

函数内部this的执行是undefined​

js
"use strict";
function f1(){
console.log(this);
}
f1() //undefined

class类this隐式丢失问题​

  • 类class和模块的内部,默认就是严格模式,所以不需要使用 use strict指定运行模式
  • 隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window,而严格模式上,默认绑定为undefined

因为ES6 默认启用严格模式,严格模式下指向 undefined, 可以使用bind方法解决

js
class Person{
constructor(name){
this.name = name
}
getName(){
console.log(this)
}
}
var p = new Person('小明')
p.getName() //Person {name: "小明"}
var pp = p.getName //赋值给了全局变量
pp() //undefined

对象方法调用赋值给一个变量this指向丢失​

  • p()返回的问题

    1、在非严格模式下 执行window,返回的是小红

    2、严格模式下,this返回的是undefined

js
"use strict";

var name ='小红'
var person = {
name :'小明',
getName(){
console.log(this.name) //指向当前对象
}
}
person.getName() //小明
var p = person.getName //赋值给了一个window变量
p() //严格模式下,this返回的是undefined

导致js里this指向混乱的原因是什么?​

  • this 是一个特殊的变量,它的指向是在运行时动态绑定的。它指向的是当前执行上下文中的调用对象
  • 在 JavaScript 中,调用上下文可以是一个函数、一个对象的方法、一个构造函数、或者是一个事件处理函数。在不同的调用上下文中,this 的指向也会不同

this绑定的优先级​