什么是this?

617 阅读7分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

在我们平常的学习工作中,经常会遇到this ,那么到底什么是this你真的搞懂了吗?在JavaScript中,this是一个很复杂的关键字,也是特别烦人的,很多人都会被this搞的晕头转向。下面这篇文章我们就来介绍一下this到底是什么?

什么是this?

this 是JavaScript中的一个关键词,在JavaScript中,this不是固定不变的,它会随着执行环境的改变而改变。实际上this就是一个指针,它最终指向调用函数的那个对象。 通常this的指向分为很多种情况,下面我们一一 来介绍:

1. 默认情况(默认绑定)

默认的this

console.log(this); // window,这里不区分严格模式和非严格模式,都是指向window

在默认情况下,this指向window

函数中的this

"use strict";
function fn(){
    console.log(this);//非严格模式指向window,严格模式指向undefined
}
fn();

在严格模式下,函数中的this是undefined,但是在非严格模式下,函数中的this指向window

2. 隐式绑定

对象方法中的this

1.如果函数调用是在一个对象上触发的,即存在上下文对象,那么这个函数中的this指向调用函数的对象

function sayHi(){
    console.log('Hello,', this.name);
}
var person = {
    name: 'jack',
    sayHi: sayHi
}
var name = 'bob';
person.sayHi(); // Hello,jack

2.需要注意的是,在对象的属性链中,只有最后一个属性会影响函数中的this。(就近原则)

function sayHi(){
    console.log('Hello,', this.name);
}
var person2 = {
    name: 'jack',
    sayHi: sayHi
}
var person1 = {
    name: 'bob',
    friend: person2
}
person1.friend.sayHi();// Hello,jack

回调函数中的this

1.普通回调函数中

function fn(f){
    f();
}
function fn1(){
    console.log(this);
}
fn(fn1);

在这个例子中,fn1被作为回调函数调用,回调函数中的this指向和默认绑定的一样,在非严格模式下指向window,在严格模式下指向undefined。

promise中的.then回调也属于普通回调,同样遵循此规则。

2.特殊回调函数(定时器回调函数)

setTimeout(function(){
    console.log(this);
});
setInterval(() => {
    console.log(this); 
},1000);

对于定时器中的回调,不管是在严格模式还是非严格模式下,this稳定指向window。

3.数组遍历方法中的this

var arr=[1,2];
arr.forEach(function(){
    console.log(this);
})

在数组的遍历方法中,传入的方法其实也是回调函数,如果在这个方法中不指定替代this对象时,同样非严格模式指向window,严格模式指向undefined。

4.事件回调函数

document.addEventListener("click",clickhandler);
function clickhandler(e){
    console.log(this);//侦听事件的对象
}

在绑定事件的回调函数中,回调函数中的this指向被侦听事件的对象。上面的例子中,this就是document对象。

5.使用arguments关键字调用回调函数

function fn(){
    arguments[0]()
}
function fn1(){
    console.log(this);//this指向fn中arguments
}
fn(fn1);

当在函数中通过arguments调用回调函数时,回调函数中的this指向Arguments对象。

3.call、apply、bind(显式绑定)

call、apply、bind都可以通过传递参数来改变this的指向。

相同点:

  1. 都可以通过传参改变this的指向,传递的第一个参数就是this

  2. 如果第一个参数传null或undefined,那么这些值都会被忽略,this的指向就会走默认绑定规则,非严格模式下指向window

不同点:

  1. 传参不同:call( this,pra1,pra2,pra3 ),call传递参数是一个一个传递,而apply第二个参数是一个数组 apply( this,[pra1,pra2,pra3] )

  2. call和apply在执行时会直接调用函数,而bind不会调用函数。

document.onclick = fn.call(obj); // this改变为obj了,但是绑定的时候立即执行,当触发点击事件的时候执行的是fn的返回值undefined
document.onclick = fn.bind(obj); // bind会把fn中的this预处理为obj,此时fn没有执行,当点击的时候才会把fn执行

4. new 绑定

在JavaScript中,构造函数只是使用new操作符时被调用的普通函数,不属于某个类,也不会实例化一个类。所有的函数都可以通过new来调用,称为构造函数调用。

当使用new来调用函数的时候会自动执行以下操作:

    1. 创建一个空对象,构造函数中的this指向这个空对象

    2. 执行原型连接 target.__proto__ = constructor.prototype

    3. 执行构造函数方法,属性和方法都被添加到this指向的对象上

    4. 如果构造函数中没有返回其他的对象,那么就返回this(即创建的这个新对象);否则,返回构造函数中返回的对象。

function Fn(){
    this.name = 'jack';
};
let person = new Fn();
person.name//jack

上面这个例子中,这个过程就称为构造调用,this将指向新创建的对象person;

5.箭头函数绑定

箭头函数是ES6新增的,它和普通函数有一些区别,箭头函数没有自己的this,它的this继承于当前函数执行上下文中的this。

在使用箭头函数时,有几点需要注意:

    1. 函数中的this,继承的是外层代码块中的this。

    2. 箭头函数不能作为构造函数使用new调用,否则会抛出错误。TypeError: fn is not a constructor

    3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用可以使用rest参数代替。

Q:什么是rest参数? rest 顾名思义,就是剩余的。下面看两个例子就懂了。

// 例1
let fn = (...vals) => {
    console.log(vals); // [1, 2, 3]
}
fn(1, 2, 3);
// 例2
let fn = (name, ...vals) => {
    console.log(name); // jack
    console.log(vals); // [1, 2, 3]
}
fn('jack', 1, 2, 3);

    4. 不可以使用yield命令,所有箭头函数不能用作Generator函数。

    5. 箭头函数没有自己的this,所以不能用call、apply、bind这些方法去改变this的指向。

总结

this绑定无非就是分为几种场景:默认绑定、隐式绑定、显示绑定、new绑定、箭头函数绑定

  1. 默认绑定     默认绑定可以理解为函数调用时无任何调用前缀的场景 ,默认绑定的情况下,this指向全局对象window(非严格模式),this指向undefined(严格模式)。

  2. 隐式绑定     如果函数调用时,前面存在调用它的对象,那么this就会隐式的绑定到这个对象上。如果函数调用前存在多个对象,this指向距离调用自己最近的对象

  3. 显示绑定     显示绑定就是通过call、apply、bind方法改变this指向。这三个方法传递的第一个参数就是最终的this指向。如果第一个参数是null或undefined,那么this将采用默认绑定的规则,指向全局对象。

  4. new绑定     使用new绑定时,this指向新创建的那个对象。

  5. 箭头函数     箭头函数中没有this,箭头函数的this指向取决于外层作用域中的this,外层作用域或函数的this指向谁,箭头函数中的this便指向谁。

看到最后,是不是感觉对this有了一定了解了,下面就在例题中检测一下吧。

例题

  1. 例1
var obj = {
  a: 10,
  c: 50,
  b: {
    a: 0,
    c: this.a,
    run: function () {
      console.log(this.c);
    }
  }
}
obj.b.run();
  1. 例2
var length = 10;
function fn() {
  console.log(this.length);
}
var obj = {
  length: 5,
  method: function (fn) {
    fn();
    arguments[0]();
  }
}
obj.method(fn, 1);
  1. 例3
var name = '222';
var a = {
    name: '111',
    say: function () {
        console.log(this.name)
    }
}
var b = {
    name: '333',
    say: function (fn) {
        fn()
    }
}
a.say()
b.say(a.say)
  1. 例4
window.number = 2;
var obj = {
  'number': 3,
  'db1': (function () {
      console.log(this);
      this.number *= 4;
      return function () {
          console.log(this);
          this.number *= 5;
      }
  })()
}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number);

本文参考: 作者:刘小夕 链接:juejin.cn/post/684490…来源:掘金