js中老生常谈的this,你理解了吗

214 阅读4分钟

1.看题

先来看几道题目,如果都做对了的话;那么恭喜你,可以跳过这篇文章。省下几分钟时间。

答案在题目后面

 //第一题
 function whatIsThis1() {
      "use strict"
      return this
  }
  function whatIsThis2() {
      return this
  }
  console.log(whatIsThis1() === window);
  console.log(whatIsThis2() === window);
//第二题
 const that1 = {
      whatIsThis: function () {
          return this
      }
  }
  const that2 = {
      whatIsThis: that1.whatIsThis
  }
  const identify = that2.whatIsThis;

  console.log(that1.whatIsThis() === that1);
  console.log(that2.whatIsThis() === that1);
  console.log(identify() === that1);
  console.log(that1.whatIsThis.call(that2) === that2);
//第三题
function That() {
    this.whatIsThis = () => this;
}
const that1 = new That();
const that2 = {
    whatIsThis: that1.whatIsThis
}

console.log(that1.whatIsThis() === that1);
console.log(that2.whatIsThis() === that2);
//第四题
function That() {
    this.whatIsThis = function(){
        return this
    }.bind(this)
}
const that1 = new That();
const that2 = {
    whatIsThis: that1.whatIsThis
}

console.log(that1.whatIsThis() === that1);
console.log(that2.whatIsThis() === that2);

答案

 //第一题
    false
    true
 //第二题
    true
    false
    false
    true
 //第三题
    true
    false	
 //第四题
    true
    false

2. js中的this是什么

2.1. 在全局上下文中

无论是否处于严格模式,this都指向全局对象,在浏览器环境,即this===window。

2.2. 在函数上下文中

this表示函数上下文,即与函数调用相关联的对象。通常称之为函数上下文。this的值与函数调用方式和定义方式有关

  • 在严格模式下,如果执行环境中没有指定this的值,则this===undefined
  • 在非严格模式下,this的值与函数调用方式和定义方式有关。下面从函数调用方式开始,来看下this的取值。

3.函数调用方式

3.1.作为函数直接被调用

这是函数最简单直接的调用方式。声明一个函数,然后执行。

function whatIsThis(){
	return this
}
whatIsThis()

这种方式没有指定this的值,在严格模式下this===undefined;非严格模式下,则默认指的是全局上下文,在浏览器中即this===window

3.2. 作为方法被调用

函数作为一个对象的属性被调用,此时this绑定的就是该对象。函数内部可以也通过this访问到对象的所有属性。

function whatIsThis(){
  return this.name;
}
const that1 = {
	name:'that1',
    getThis:whatIsThis
}
that1.getThis()

这里新建了一个whatIsThis函数和that1对象,that1的属性getThis是对函数whatIsThis的引用。g通过that1.getThis(),执行的就是 whatIsThis本身。此时,that1也就成了whatIsThis的上下文了。

3.3. 作为构造函数被调用

作为构造函数调用跟3.2作为方法调用类似。

要通过构造函数调用,就需要使用new关键字。

function whatIsThis(){
	return this;
}
function That() {
    this.getThis = whatIsThis
}
const that = new That();
that.getThis()

这里简单说下new关键字都做了什么事情

  • 创建一个空对象
  • 创建的对象被作为this参数传递给构造函数,作为构造函数的上下文
  • 创建的对象作为返回值返回

以上代码通过构造函数方式new That()调用,每次都会产生一个新的对象,每个对象下都有getThis属性引用whatIsThis。这里that.getThis()就相当于3.2中函数作为对象方法被调用,函数whatIsThis的上下文自然就是调用方法的对象that。

3.4.使用apply和call调用

apply和call都可以显式地为函数绑定特定的函数上下文。两者的区别仅仅是参数不同。第一个参数都是要指定的上下文对象。call剩余参数则是对应函数的一个个参数;apply的第二个参数是函数参数的数组集合。

fn.call(context,arg1,arg2...)

fn.apply(context,[arg1,arg2...])

function whatIsThis(){
    console.log(this)	
    console.log(arguments)
}
const that1={name:'that1'}
const that2={name:'that2'}
whatIsThis.call(that1,1,2)
whatIsThis.apply(that2,[1,2])

call和apply都会显示地把第一个参数作为函数whatIsThis的上下文。

whatIsThis.call(that1,1,2) 会绑定that1作为函数whatIsThis的上下文。

whatIsThis.apply(that2,[1,2]) 则会绑定that2作为函数whatIsThis的上下文。

4.箭头函数和bind

4.1.箭头函数

箭头函数没有单独的this值。箭头函数的this取决于箭头函数声明时的上下文。

const arrowFn = ()=>this

const that1 = {
     getThis:arrowFn
}
function That(){
	this.getThis = ()=>this
}
const that2 = new That()
that1.getThis()
that2.getThis()

that1对象中的getThis是arrowFn引用,arrowFn声明在全局上下文中,因此that1.getThis()中this===window

that2通过构造函数调用,this被绑定为构造函数新建的对象,这里构造函数新建的对象被that2引用,即this===that2

4.2. bind

bind同样能显示的绑定函数上下文,与call和apply不同的是,bind返回一个重新绑定上下文后的新函数,而不是执行函数。

const that1 ={name:'that1'}
function That(){
  this.getThis = function(){return this}
}
const that2 = new That()
that2.getThis
const newGetThis = that2.getThis.bind(that1)
newGetThis()

通过构造函数调用that2.getThis()时,getThis中的this即为that2

而newGetThis中的上下文已经通过bind重新绑定为that2

5.总结

  • this表示函数上下文,即与函数调用相关联的对象。this的值与函数调用方式和定义方式有关
  • 函数作为函数调用;非严格模式下,this指向全局window对象;严格模式下则指向undefined
  • 函数作为方法调用;this通常指向调用的对象
  • 函数作为构造函数调用;this指向新创建的对象
  • 通过call、apply调用;this指向call、apply的第一个参数
  • 箭头函数this取决于声明的时候
  • 函数可通过bind返回一个新的函数,返回的函数中this指向bind方法传入的参数