你真的了解this吗?

135 阅读3分钟

前言

在javascript中this指向可能对于初学者来说确实不太好理解,甚至对于中级开发来说也有点难度,当然可能我也就是其中的一个哈,今天有时间正好来复习下。

正文

在javascript中this有多种使用场景,比如:

  1. 普通函数中的this
  2. 箭头函数中的this
  3. class中的this
  4. 构造函数中的this
  5. call、apply、bind
  6. ...

全局作用域中的this

<script>
   console.log(this); //this->window
</script>

this指向window

普通函数中的this

var obj = {
    name: "obj的name",
    fn1: function() {
        console.log(this.name); 
    },
    fn2: function(){
        handler() 
    }
}
function handler(){
  console.log(this); 
}
obj.fn1() // obj的name
obj.fn2() // window
handler() // Window

首先就是普通函数中的this,直接调用是指通过 handler() 方式调用。简单来说就是谁调用就指向谁,如果是在严格模式下调用那么this就会指向undefined

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

箭头函数中的this

var name = "全局name"
var obj = {
  name: "obj的name"
}
var handler = () => {
  console.log(this.name);
}
handler()  // 全局name
handler.call(obj) // 全局name

箭头函数中的this有点特殊,在定义的时候就已经确定了this的指向,后期无法改变,可以看到打印的结果都是 “全局name”。

构造函数中的this

function Handler(n){
  this.name = n
  console.log(this)
}
// 作为构造函数使用
var o = new Handler("Lily"); // this为当前对象 Person {name: "Lily"}
// 作为普通函数使用
Handler("Lily"); // Window
  1. 用new创建对象的时候调用了构造函数。\
  2. 构造函数和普通函数的区别在于调用方式,而不是定义方式,如果按Handler("Lily")方式调用,他就是个普通函数,由于普通函数中的this是于Window,所以上面函数以Handler("Lily")方式调用后创建了1个全局变量。\
  3. new关键字改变了函数内this的指向,使其指向刚创建的对象。

window定时器中的this

var name = 'window的name';
var obj = {
    name: 'obj的name',
    fn: function () {
        var timer = null;
        clearInterval(timer);
        timer = setInterval(function () {
            console.log(this.name);  // window的name
        }, 1000)
    }
}

从this.name可以看出this的指向是window。如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的。当然如果想要改变他的指向有2种办法,第一种就是在定时器函数外层保存this,在定时器里面使用这个保存this的变量,第二种就是使用call,apply改变他的指向。

call、apply、bind

var name = "window的name";
var obj = {
    name: 'obj的name',
}
var handler = () => {
  console.log(this.name);
}
handler() // window的name
handler.call(obj, 参数1, 参数2) // obj的name
handler.apply(obj, [参数1, 参数2]) // obj的name
handler.bind(obj, 参数1, 参数2)() // obj的name

可以看到call、apply、bind都可以改变this的指向,不同的是call和apply是直接执行函数的,而bind是返回一个新的函数需要再次执行,call和apply不同的是传参数的方式不同call是一个一个参数传,apply是传数组

class中this的指向

class Handler {
  constructor(name) {
    this.name = name
  }
  speak() {
    console.log(this)
  }
}

const h = new Handler('Callback')
h.speak();  // Person {name: 'Callback'}

实际上,方法内部的 this 永远指向的是调用这个方法的对象,通过 Handler 的实例 p 调用 speak() 方法,那么 this 就指向 p,其实是和es5的构造函数是一样的。

总结: 网上很多文章都说谁调用this就指向谁,不是使用所有的都是,对于普通函数的this指向是对的,但是对于箭头函数,定时器中的this指向就不对了。总之还需平时多发现多积累才能成长,加油!