javascript中this指向解析

317 阅读4分钟

javascript中this的指向问题非常具有迷惑性,而分辨this指向问题简单来说可以记住以下两点。

在this定义时并不能确定它的指向

在通常情况下this永远指向其执行时所在函数的所属对象

下面简单从这两点谈谈this的指向问题

一,在this定义时并不能确定它的指向

那为什么在this定义时不能确定this的指向呢,如果在this定义时就确定了它的指向不是就简单多了吗。 这是因为js是一门非常灵活的语言,函数在js中属于引用数据类型,什么叫引用数据类型,简单来说定义一个函数

function fun(){
    console.log('this is a function')
}

那么fun在栈内存中储存的并不是一段函数体代码,而是储存的是一段类似于0xE3434这样的东西,而0xE3434是一个指向堆内存的指针,当定义一个函数时,会先在堆内存中开一个房间,把函数体放到这个房间中,把这个房间的放假号设为0xE3434,然后再把这个房间号储存在fun这个变量上面,而当我们执行fun时,js引擎会先拿到这个房间号,然后再去这个房间号对应的房间去拿函数体来执行。简单的画了个图,感受一下

而在这个过程中this的指向就取决与谁去让js引擎去这个房间拿东西.

let obj1 = {
  x:'obj-x',
  fun:function () {
    console.log(this.x)
  }
}
let obj2 = {
  x:'obj2-x',
  fun:obj1.fun
}
obj2.fun() // obj2-x

在上面这个代码中obj2中变量fun储存的就是obj1中变量fun储存的内容,所以在obj2.fun()执行时函数中的this指向obj2,换言之是obj2让js引擎去该房间拿东西,所以this指向的是obj2。 这就是为什么 ### 在this定义时并不能确定它的指向 ### 因为并不确定this在什么环境下执行,自然也不能确定this的指向。

二,在通常情况下this永远指向其执行时所在函数的所属对象

这个问题在上面也进行了一部分说明,现在来说另一部分。什么叫 “通常情况下”。 通常情况下是指在没有人为改变this指向的情况下,而人为绑定this指向可以通过以下几种方式:

function.call

call 方法是一个提供给函数调用的方法,调用方法类似与

fun.call(obj,arg1,arg2,...)

第一个参数就是你想要给fun所绑定的this值,其后的参数是想要传递给fun函数的实参。上面代码的意思就是:把fun的this绑定到obj上,然后传给fun的参数为arg1,arg2, ...,并且执行fun函数。 值得注意的是call方法是一个一次性的方法,既只有在调用call方法时this的指向才会改变,调用结束后this指向又变为不明确。

fun.call(obj,arg1,arg2,...) // this => obj
fun() // this => window 

该方法并不能永久改变this指向。

function.apply

fun.apply(obj,[arg1,arg2,...])

该方法同call方法的使用方法相同,唯一不同的时apply只接收两个参数,第二个参数是需要传递给fun的实参数组。 那call和apply只有这一个区别吗,是的,两者只有这个区别。

function.bind

bind也是用于显示绑定this,只不过bind会返回一个永久改变this的原函数的拷贝 例:

let obj1 = {
    x: 'obj1-x',
    fun: function () {
      console.log(this.x)
    }
  }
  function fun2() {
    console.log(this.x)
  }
  fun2.bind(obj1)() // obj1-x
  fun2() // undefined

bind也可以接收多个参数,多余参数会作为返回函数的初始参数。

箭头函数

es6中新增了一种函数的定义方式:箭头函数,箭头函数在执行时会自动将函数内的this指向与函数所在上下文的this指向保持一致,也就是说,无论是哪个人让js引擎去取函数代码,函数的this指向都与这个人所在上下文的this指向保持一致。

new

new 是js中的一个操作符,也是js中少有的一个一目操作符,new 的作用对象是一个构造函数,构造函数与普通的函数并没有什么不同,之所以叫构造函数是从一个函数的用途来区分,如果一个函数是用来实例化一个子类的,那么这个函数就可以叫做构造函数,正式因为js不是传统的面向对象语言,所以在es6之前通过new操作符来实现子类的继承属性。而new操作符会把构造函数中的this指向绑定到操作接收方。

 function Car(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.showCarYear=function(){
      console.log(this.year)
    }
  }
  var mycar = new Car("Eagle", "Talon TSi", 1993);
  mycar.showCarYear() // 1993

这段代码创建了 mycar 并给他的属性指定值,于是 mycar.make 的值为"Eagle", mycar.year 的值为1993,以此类推。