彻底搞懂 JavaScript 中的 this 指向

Posted Hhpon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底搞懂 JavaScript 中的 this 指向相关的知识,希望对你有一定的参考价值。

前言


本文将从以下几方面阐述 javascript 中 this 的指向问题。

  • 标准函数中,this的引用值是什么
  • 箭头函数中,this的引用值是什么
  • 使用new关键字创建对象时,this的引用值是什么
  • 闭包中使用this时,this的引用值什么

标准函数中,this 的引用值是什么

标准函数中,this引用的是把函数当成方法调用的上下文对象

在标准函数中this的值是会根据方法被调用的情况改变所引用的值

window.identity = "The Window"
let object = {
    identity: \'My Object\',
    getIdentityFunc() {
        console.log(this.identity)
    }
}
object.getIdentityFunc()  // My Object
const getIdentityFunc = object.getIdentityFunc
getIdentityFunc()  // The Window

箭头函数中,this 的引用值是什么

在箭头函数中,this引用的是定义箭头函数的上下文

由此可以看出箭头函数中的this引用值是固定的

window.identity = "The Window"
let object = {
    identity: \'My Object\',
    testIdentity: this.identity,
    getIdentityFunc:() => {
        console.log(this.identity)
    }
}
console.log(object.testIdentity)  // The Window
object.getIdentityFunc()  // The Window

面试的时候我就遇到了这个问题,当时我心想

“在箭头函数中,this引用的是定义箭头函数的上下文对象”

大家如果仔细比较这两句话可以发现我心里想的多了两个字(或者说我就是囫囵的看了一遍书,根本没有好好的去理解、去验证),这两个字不多不少让我把this的引用值认为成object这个对象了,所以我认为最后的结果应该是My Object

那么我们现在来具体的看看这个定义箭头函数的上下文是什么呢?
在我看来,我们可以把this值看成上下文

那定义箭头函数的上下文其实就是objectthis值呗;那我们要知道的其实就是objectthisidentity属性是什么呗?

在上方代码的倒数第二行就是为了说明这个问题,由于属性testIdentity的值是The Window我们能得出objectthis引用值其实是window

由于函数getIdentityFunc中的this引用值其实是objectthis,也就是window。所以函数getIdentityFunc中的this.identityThe Window

既然箭头函数中的 this 是固定的,那么类似 call 的函数能改变 this 的引用值吗

在JS中我们可以使用callapplybind方法改变this指向,既然箭头函数的this值引用值是固定的,那我们能使用这几个方法改变这个引用值吗?

看下面的代码

window.identity = "The Window"
let object = {
    identity: \'My Object\',
    getIdentityFunc() {
        console.log(this.identity)
    }
}
let object1 = {
    identity: \'My Object1\'
}
object.getIdentityFunc.call(object1)  // My Object1
window.identity = "The Window"
let object = {
    identity: \'My Object\',
    testIdentity: this.identity,
    getIdentityFunc:() => {
        console.log(this.identity)
    }
}
console.log(object.testIdentity)  // The Window
object.getIdentityFunc()  // The Window
object.getIdentityFunc.call(object)  // The Window

对比这两处代码我们能发现call方法根本无法改变箭头函数的this的引用值。

使用 new 关键字创建对象时,this 的引用值是什么

在实例中this的引用值是当前所在的实例
window.identity = "The Window"

function Obj() {
  this.identity = "My Object"

  this.getIdentityFunc = function () {
    console.log(this.identity)
  }

  this.getIdentityFunc1 = () => {
    console.log(this.identity)
  }
}

const obj = new Obj()
obj.getIdentityFunc()  // My Object
obj.getIdentityFunc1() // My Object

我们可以看到两个函数中输出的都是My ObjectgetIdentityFunc函数的结果也之前调用的差别不大,因为在这个函数里面this的引用值依然是obj这个上下文对象。

但是在函数getIdentityFunc1中,虽然都是箭头函数(和第二个例子的代码做比较),但是这一次的结果确实My Object,也就是说这一次的this引用值是对象obj

那么这个情况的原因是什么呢?我们来回忆一下刚刚第二个例子里面箭头函数的this值是等于对象objectthis值,事实上这一次也是。所以造成这两次结果不同的原因其实是这两次的“函数上下文”不一样了。

  • 第二个例子中 object的函数上下文this引用值是window
  • 这次obj的函数上下文this的引用值是他自己obj

闭包中使用 this 时,this 的引用值什么

在闭包中使用 this 会让代码变复杂。如果内部函数没有使用箭头函数定义,则 this 对象会在运行时绑定到执行函数的上下文。如果在全局函数中调用,则 this 在非严格模式下等于 window,在严格模式下等于 undefined。如果作为某个对象的方法调用,则 this 等于这个对象。

从这一段话中我们可以提取出以下几条信息。

  1. 如果闭包的内部函数是使用箭头函数定义的,基本不受影响,我们只需要按照箭头函数的this指向判断方法判断就可以。
  2. 如果在全局函数中调用,则 this 在非严格模式下等于 window,在严格模式下等于 undefined
  3. 如果作为某个对象的方法调用,则 this 等于这个对象

内部函数使用箭头函数定义的,基本不受影响

window.identity = "The Window"
let object = {
  identity: "My Object",
  getIdentityFunc() {
    const doit = () => {
      console.log(this.identity)
    }
    return doit
  },
}

object.getIdentityFunc()() // My Object

如果在全局函数中调用,则 this 在非严格模式下等于 window,在严格模式下等于 undefined

window.identity = "The Window"
let object = {
  identity: "My Object",
  getIdentityFunc() {
    return function () {
      console.log(this.identity)
    }
  },
}

object.getIdentityFunc()() // The Window

如果作为某个对象的方法调用,则 this 等于这个对象

window.identity = "The Window"
let object = {
  identity: "My Object",
  testIdentity: this.identity,
  getIdentityFunc: () => {
    console.log(this.identity)
  },
  getIdentityFunc1() {
    return function () {
      console.log(this.identity)
    }
  },
  getIdentityFunc2() {
    function doit() {
      console.log(this.identity)
    }
    return doit
  },
  getIdentityFunc3() {
    const doit = () => {
      console.log(this.identity)
    }
    return doit
  },
}

object.getIdentityFunc() // The Window
object.getIdentityFunc1()() // The Window
object.getIdentityFunc2()() // The Window 
object.getIdentityFunc3()() // My Object

let object1 = {
  identity: "My Object1",
  getIdentityFunc1: object.getIdentityFunc1(),
  getIdentityFunc2: object.getIdentityFunc2(),
}

object1.getIdentityFunc1()  // My Object1
object1.getIdentityFunc2()  // My Object1

练习题目

const test = {
  name: "Bill",
  show1: function () {
    console.log(this.name)
  },
  show2: () => {
    console.log(this.name)
  },
  show3: () => {
    function innerFunction() {
      console.log(this.name)
    }
    innerFunction()
  },
}

test.show1()
test.show2()
test.show3()

// 参考答案
Bill
undefined
undefined
var name = "window"

var person1 = {
  name: "person1",

  foo1: function () {
    console.log(this.name)
  },

  foo2: () => console.log(this.name),

  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },

  foo4: function () {
    return () => {
      console.log(this.name)
    }
  },
}

var person2 = { name: "person2" }

person1.foo1() 
person1.foo1.call(person2)
person1.foo2()
person1.foo2.call(person2)
person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)
person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

// 参考答案
person1
person2
window
window
window
window
person2
person1
person2
person1
var name = "window"

function Person(name) {
  this.name = name

  this.foo1 = function () {
    console.log(this.name)
  }
  this.foo2 = () => console.log(this.name)
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person1 = new Person("person1")
var person2 = new Person("person2")
person1.foo1()
person1.foo1.call(person2)
person1.foo2()
person1.foo2.call(person2)
person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)
person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

// 参考答案
person1
person2
person1
person1
window
window
person2
person1
person2
person1
window.identity = "The Window"

let object = {
  identity: "My Object",
  testThis: this.identity,
  getIdentityFunc() {
    console.log(this.identity)
  },
  getIdentityFunc1: () => {
    console.log(this.identity)
  },
  getIdentityFunc2: () => {
    function innerFunction() {
      console.log(this.identity)
    }
    innerFunction()
  },
  getIdentityFunc3() {
    return function () {
      console.log(this.identity)
    }
  },
  getIdentityFunc4() {
    return () => {
      console.log(this.identity)
    }
  },
  getIdentityFunc5: () => {
    return function () {
      console.log(this.identity)
    }
  },
  getIdentityFunc6: () => {
    return () => {
      console.log(this.identity)
    }
  },
  getIdentityFunc7() {
    console.log(this.identity)
    function innerFunction() {
      console.log(this)
      console.log(this.identity)
    }
    innerFunction()
  },
  getIdentityFunc8() {
    const innerFunction = () => {
      console.log(this.identity)
    }
    innerFunction()
  },
}

object.getIdentityFunc() // My Object
let getIdentityFunc = object.getIdentityFunc
getIdentityFunc() // The Window

object.getIdentityFunc1() // The Window
let getIdentityFunc1 = object.getIdentityFunc1
getIdentityFunc1() // The Window

object.getIdentityFunc2() // The Window
let getIdentityFunc2 = object.getIdentityFunc2
getIdentityFunc2() // The Window

object.getIdentityFunc3()() // The Window
let getIdentityFunc3 = object.getIdentityFunc3()
getIdentityFunc3() // The Window

object.getIdentityFunc4()() // My Object
let getIdentityFunc4 = object.getIdentityFunc4()
getIdentityFunc4() // My Object

object.getIdentityFunc5()() // The Window
let getIdentityFunc5 = object.getIdentityFunc5()
getIdentityFunc5() // The Window

object.getIdentityFunc6()() // The Window
let getIdentityFunc6 = object.getIdentityFunc6()
getIdentityFunc6() // The Window

object.getIdentityFunc7()
object.getIdentityFunc8()

// 参考答案

My Object
The Window
The Window
The Window
The Window
The Window
The Window
The Window
My Object
My Object
The Window
The Window
The Window
The Window
The Window
My Object

文章参考文献

  • 《JavaScript 高级程序设计》
  • JavaScript之彻底搞懂this的指向

以上是关于彻底搞懂 JavaScript 中的 this 指向的主要内容,如果未能解决你的问题,请参考以下文章

彻底搞懂JavaScript中的作用域和闭包

这次彻底搞懂JavaScript中的原型与原型链

彻底搞懂 JavaScript 的函数特点

这一次,彻底搞懂箭头函数

彻底搞懂JS事件中的循环机制 Event Loop

一文带你彻底搞懂Java和JavaScript的区别与相似之处(纯干货建议收藏)