理解 JS 中的 This, Bind, Call, 和 Apply

更新日期: 2019-10-29阅读: 1.8k标签: this

该this关键字是在JavaScript中一个非常重要的概念,也是一个特别令人迷惑的这两个新的开发者和那些谁拥有在其他编程语言的经验。在JavaScript中,this是对对象的引用。该对象this是指可以改变,含蓄地基于它是否是全球性的,在对象上,或者在一个构造函数,也可以明确地变化根据的使用Function原型方法bind,call和apply。

尽管这this是一个复杂的话题,但它也是在您开始编写第一个JavaScript程序后立即出现的话题。无论您是尝试访问文档对象模型(dom)中的元素还是事件,构建用于以面向对象的编程风格编写的类,还是使用常规对象的属性和方法,都将遇到this。

在本文中,您将了解什么this是指隐含根据上下文,您将学习如何使用bind,call和apply方法,明确确定的值this。

在以下四个主要上下文中,this可以隐式推断出的值:

  • 全局背景
  • 作为对象内的方法
  • 作为函数或类的构造函数
  • 作为DOM事件处理程序


全局

在全局上下文中, this指的是全局对象。在浏览器中工作时,全局上下文为window。当您使用Node.js时,全局上下文为global。

对于示例,您将在浏览器的Developer Tools控制台中练习代码。如果您不熟悉在浏览器中运行JavaScript代码,请阅读如何使用JavaScript开发者控制台

如果您在this没有任何其他代码的情况下记录了值,那么您将看到对象this所指。

console.log(this)
// Output
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

你可以看到,this就是window,这是一个浏览器的全局对象。

在“了解JavaScript中的变量,作用域和提升”中”中,您了解到函数具有自己的变量上下文。您可能会倾向于认为它this会遵循函数内的相同规则,但事实并非如此。顶层函数仍将保留this全局对象的引用。

您编写一个顶层函数,或者一个不与任何对象关联的函数,如下所示:

function printThis() {
  console.log(this)
}

printThis()
// Output
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

即使在函数内,this仍指window或全局对象。

但是,当使用严格模式时时,this全局上下文中函数内部的上下文将是undefined。

'use strict'

function printThis() {
  console.log(this)
}

printThis()
// ~~~~Output
undefined

通常,使用严格模式来降低this出现意外范围的可能性更为安全。很少有人会window使用引用该对象this。

有关严格模式及其对错误和安全性所做的更改的详细信息,请阅读MDN上的严格模式文档。


对象方法

甲方法是在对象上的功能,或者,一个对象可以执行任务。一种this用于引用对象属性的方法。

const america = {
  name: 'The United States of America',
  yearFounded: 1776,

  describe() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  },
}

america.describe()
// Output
"The United States of America was founded in 1776."

在此示例中,this与相同america。

在嵌套对象中,this指的是方法的当前对象范围。在以下示例中,this.symbol在details对象内是details.symbol。

const america = {
  name: 'The United States of America',
  yearFounded: 1776,
  details: {
    symbol: 'eagle',
    currency: 'USD',
    printDetails() {
      console.log(`The symbol is the ${this.symbol} and the currency is ${this.currency}.`)
    },
  },
}

america.details.printDetails()
Output
"The symbol is the eagle and the currency is USD."

另一种思考方式是this在调用方法时引用点左侧的对象。


函数构造器

使用new关键字时,它将创建构造函数或类的实例。class在ECMAScript 2015更新为JavaScript引入语法之前,函数构造函数是初始化用户定义对象的标准方法。在“理解JavaScript中的类”中”中,您将学习如何创建函数构造函数和等效的类构造函数。

function Country(name, yearFounded) {
  this.name = name
  this.yearFounded = yearFounded

  this.describe = function() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  }
}

const america = new Country('The United States of America', 1776)

america.describe()
Output
"The United States of America was founded in 1776."

在这种情况下,this现在绑定到的实例Country,该实例包含在america常量中。


类构造器

类上的构造函数的作用与函数上的构造函数的作用相同。在了解JavaScript中的类中,了解有关函数构造函数和ES6类之间的异同的更多信息。

class Country {
  constructor(name, yearFounded) {
    this.name = name
    this.yearFounded = yearFounded
  }

  describe() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  }
}

const america = new Country('The United States of America', 1776)

america.describe()

this在describe方法指的实例Country,这是america。

Output
"The United States of America was founded in 1776."


DOM事件处理程序

在浏览器中,this事件处理程序具有特殊的上下文。在由调用的事件处理程序中addEventListener,this将引用event.currentTarget。开发人员通常会简单地根据需要使用event.target或event.currentTarget访问DOM中的元素,但是由于this引用在此上下文中发生了变化,因此了解这一点很重要。

在下面的示例中,我们将创建一个按钮,向其中添加文本,然后将其附加到DOM。当我们this在事件处理程序中记录值时,它将打印目标。

const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)

button.addEventListener('click', function(event) {
  console.log(this)
})
Output
<button>Click me</button>

将其粘贴到浏览器中后,您将在页面上看到一个“点击我”按钮。如果单击该按钮,您将看到<button>Click me</button>出现在您的控制台中,因为单击该按钮会记录元素,该元素就是按钮本身。因此,如您所见,this指向目标元素,这是我们向其添加事件侦听器的元素。


显式上下文

在前面的所有示例中,的值this都是由其上下文确定的-无论它是全局的,在对象中,在构造的函数或类中还是在DOM事件处理程序上。但是,使用call,apply或bind可以显式确定this应引用的内容。

很难准确定义何时使用call,apply或bind,因为这将取决于程序的上下文。bind当您想使用事件来访问另一个类中一个类的属性时,它可能特别有用。例如,如果要编写一个简单的游戏,则可以将用户界面和I / O分成一个类,将游戏逻辑和状态分成另一个类。由于游戏逻辑需要访问输入,例如按键和单击,因此您希望bind事件能够访问this游戏逻辑类的值。

重要的部分是要知道如何确定this引用的对象,您可以使用上一部分中的内容隐式地进行操作,或者使用接下来将要学习的三种方法来显式地进行操作。

call apply

call与apply它们非常相似-它们调用具有指定this上下文和可选参数的函数。call和之间的唯一区别apply是,call要求将参数逐一传递,并将apply参数作为数组。

在此示例中,我们将创建一个对象,并创建一个引用this但没有this上下文的函数。

const book = {
  title: 'Brave New World',
  author: 'Aldous Huxley',
}

function summary() {
  console.log(`${this.title} was written by ${this.author}.`)
}

summary()
Output
"undefined was written by undefined"

由于summary并且book没有连接,因此在全局对象上寻找那些属性时,summary仅进行打印本身就可以调用undefined。

注意:在严格模式下尝试这样做会导致Uncaught TypeError: Cannot read property 'title' of undefined,就像this它本身一样undefined。

但是,您可以使用call和apply调用函数上的this上下文book。

summary.call(book)
// or:
summary.apply(book)
Output
"Brave New World was written by Aldous Huxley."

现在book,summary在应用这些方法之间以及何时应用这些方法之间存在联系。让我们确切地确认是什么this。

function printThis() {
  console.log(this)
}

printThis.call(book)
// or:
whatIsThis.apply(book)
Output
{title: "Brave New World", author: "Aldous Huxley"}

在这种情况下,this实际上成为了作为参数传递的对象。

这就是为何call和apply是相同的,但有一个小的差异。除了能够将this上下文作为第一个参数传递之外,您还可以传递其他参数。

function longerSummary(genre, year) {
  console.log(
    `${this.title} was written by ${this.author}. It is a ${genre} novel written in ${year}.`
  )
}

随着call每增加值要传递被作为一个额外的参数。

longerSummary.call(book, 'dystopian', 1932)
Output
"Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932."

如果您尝试使用发送完全相同的参数apply,则会发生以下情况:

longerSummary.apply(book, 'dystopian', 1932)
Output
Uncaught TypeError: CreateListFromArrayLike called on non-object at <anonymous>:1:15

相反,对于apply,您必须在数组中传递所有参数。

longerSummary.apply(book, ['dystopian', 1932])
Output
"Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932."

单独或以数组形式传递参数之间的区别是微妙的,但请务必注意。它可能更简单,更方便使用apply,因为如果某些参数详细信息发生更改,则不需要更改函数调用。

bind

这两个call和apply是一次性使用的方法,如果调用该方法this上下文中,有它,但原有的功能将保持不变。

有时,您可能需要一遍this又一遍地在另一个对象的上下文中使用一种方法,在这种情况下,您可以使用该bind方法来创建一个带有显式绑定的全新函数this。

const braveNewWorldSummary = summary.bind(book)

braveNewWorldSummary()
Output
"Brave New World was written by Aldous Huxley"

在此示例中,每次调用时braveNewWorldSummary,它将始终返回this绑定到其的原始值。尝试将新this上下文绑定到它会失败,因此您始终可以信任绑定的函数以返回this期望的值。

const braveNewWorldSummary = summary.bind(book)

braveNewWorldSummary() // Brave New World was written by Aldous Huxley.

const book2 = {
  title: '1984',
  author: 'George Orwell',
}

braveNewWorldSummary.bind(book2)

braveNewWorldSummary() // Brave New World was written by Aldous Huxley.

尽管此示例尝试braveNewWorldSummary再次绑定,this但从第一次绑定起便保留了原始上下文。


箭头函数

箭头函数没有自己的this绑定。相反,它们上升到下一个执行级别。

const whoAmI = {
  name: 'Leslie Knope',
  regularFunction: function() {
    console.log(this.name)
  },
  arrowFunction: () => {
    console.log(this.name)
  },
}

whoAmI.regularFunction() // "Leslie Knope"
whoAmI.arrowFunction() // undefined

在确实要this引用外部上下文的情况下,使用箭头功能会很有用。例如,如果您在类内部有一个事件侦听器,则可能要this引用该类中的某个值。

在此示例中,您将像以前一样将按钮创建并附加到DOM,但是该类将具有一个事件侦听器,该事件侦听器将在单击时更改按钮的文本值。

const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)

class Display {
  constructor() {
    this.buttonText = 'New text'

    button.addEventListener('click', event => {
      event.target.textContent = this.buttonText
    })
  }
}

new Display()

如果单击该按钮,则文本内容将更改为的值buttonText。如果您此处未使用箭头函数,this则等于event.currentTarget,并且如果不显式绑定它,就无法使用它来访问类中的值。这种策略通常用于像react这样的框架中的类方法上。


结论

在本文中,您了解了this在JavaScript,和许多不同的值,可能已经基于隐式运行时绑定,并明确通过结合bind,call和apply。您还了解了如何this使用箭头功能中缺少绑定的方式来引用不同的上下文。有了这些知识,您应该能够确定程序中的价值this。

原文在 https://www.digitalocean.com/community/conceptual_articles/understanding-this-bind-call-and-apply-in-javascript
作者是Tania Rascia  

链接: https://www.fly63.com/article/detial/6163

解读Javascript中的this机制,别再为了 this 发愁了

JavaScript中有很多令人困惑的地方,或者叫做机制。但是,就是这些东西让JavaScript显得那么美好而与众不同。比方说函数也是对象、闭包、原型链继承等等,而这其中就包括颇让人费解的this机制。

js中this的详细解释,JavaScript中this绑定规则【你不知道的JavaScript】

this的绑定过程之前的调用栈 和 调用位置,this绑定规则:1、默认模式,2、隐式绑定,3、显式绑定,4、new绑定

JavaScript中this的运行机制及爬坑指南

在 JavaScript 中,this 这个特殊的变量是相对比较复杂的,因为 this 不仅仅用在面向对象环境中,在其他任何地方也是可用的。 本篇博文中会解释 this 是如何工作的以及使用中可能导致问题的地方,最后奉上最佳实践。

JS中this的指向问题(全)

this关键字在js中的指向问题不管是工作还是面试中都会经常遇到,所以在此对它进行一下总结:全局作用域中、闭包中指window、函数调用模式:谁调用就指谁、构造函数中,this指实例对象、apply/call改变this的指向、bind改变this指向等

彻底淘汰并消除JavaScript中的this

如果这很难明白,为什么我们不停止使用它呢?认真的思考一下?如果你读过 将90%的垃圾扔进垃圾桶后,我如何重新发现对JavaScript的爱, 当我说扔掉它时,你不会感到惊讶,this被丢弃了

JavaScript this常见指向问题

this的用法:直接在函数中使用 谁调用这个函数this就指向谁 ,对象中使用, 一般情况下指向该对象 ,在构造函数中使用

JavaScript函数中this的四种绑定策略

this的四种绑定策略:默认绑定、隐式绑定、显示绑定、new绑定。首先要明确的是一般情况下,this不是函数被定义时绑定,而是函数被调用时被绑定 ,那么函数中的this有四种绑定方式:

改变this的指向

this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象.this永远指向函数的调用者。随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。

js手写实现apply,call,bind

apply和call的区别就是传的参数形式不一样。call是一个一个的传,apply可以将参数以数组的形式传进去。而bind是传入第二个和后面的参数,且绑定this,返回一个转化后的函数。

this指向问题的经典场景

以函数形式调用,this指向window;以方法形式调用,this指向调用方法的那个对象;构造函数调用,this指向实例的对象;使用window对象的方法使,指向window;多重场景改变this指向

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!