JavaScript: this


JavaScript: Все о ключевом слове JS this

Введение

Как фронтенд-инженер, это ключевое слово вы будете видеть очень часто. Это довольно фундаментальное понятие, но чрезвычайно важное, если вы хотите стать лучше в javascript или как frontend-инженер. В этой статье я расскажу обо всех знаниях, связанных с этим.

глобальная среда

Для удобства ниже мы будем называть глобальное окружение ge.

По сути, если вы вызовете this непосредственно в ge, вы получите объект window. И это строго одинаково.

this === window; // true
Вход в полноэкранный режим Выход из полноэкранного режима

И, конечно, приведенный ниже код остается верным:

this.document === document;
Вход в полноэкранный режим Выход из полноэкранного режима

Более того, если вы попытаетесь добавить новый атрибут к window непосредственно через this, то это также осуществимо. Например:

this.myName = "Tris";
console.log(myName); // Tris
console.log(window.myName); // Tris

this.myName === window.myName; // true
this.myName === myName; // true
Войти в полноэкранный режим Выйти из полноэкранного режима

Даже если вы примените строгий режим, добавив use strict в верхней части файла, он все равно будет вести себя так, как будто this в основном указывает на объект window.

функциональная среда

Для удобства ниже мы будем называть функциональное окружение fe.

В fe, если вы вызываете this, ее ссылка зависит от того, как вы вызываете эту функцию. Если вы вызываете функцию напрямую, this будет ссылаться на объект window напрямую, это называется Sample Call.

Например:

function fn() {
  return this;
}

fn() === window; // true
Войти в полноэкранный режим Выйти из полноэкранного режима

Однако чаще всего при разработке мы предпочитаем избегать прямого вызова функции (Sample Call). Возьмем пример разработки Vue:

const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      console.log(this.myName); // Ray
    },
  },
  created() {
    this.getName();
  },
});
Вход в полноэкранный режим Выход из полноэкранного режима

Выглядит просто замечательно, верно? Но давайте посмотрим, что произойдет, если немного изменить код:

const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const array = [1, 2, 3];
      array.forEach(function () {
        console.log(this.myName); // undefined * 3 ,WTF?
      });
    },
  },
  created() {
    this.getName();
  },
});
Войти в полноэкранный режим Выйти из полноэкранного режима

Это очень странно, верно? Почему простой forEach приводит к таким странным результатам? Что ж, давайте сначала посмотрим на исходный код forEach:

Array.prototype.forEach = function(callback) {
    for(let index = 0; index < this.length(); index++> {
        callback(this[index], index, this);
    })
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мы видим, что функция, переданная в forEach, является sample called, что приводит к конфликту this, поэтому причина this неверного указания ссылки.

Как нам справиться с этой проблемой, или, если сказать другими словами, какой способ программирования лучше, чтобы предотвратить и избежать этих странных запутанных проблем со ссылками this?

Первый способ — объявить переменную для резервирования this.

const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const vm = this;
      const array = [1, 2, 3];
      array.forEach(function () {
        console.log(vm.myName); // Ray * 3,is Good!
      });
    },
  },
  created() {
    this.getName();
  },
});
Вход в полноэкранный режим Выход из полноэкранного режима

Второй способ — использовать функцию arrow.

const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const array = [1, 2, 3];
      array.forEach(() => {
        console.log(this.myName); // Ray * 3,is Good!
      });
    },
  },
  created() {
    this.getName();
  },
});
Войти в полноэкранный режим Выйти из полноэкранного режима

В любом случае, проблема решена. Для первого способа создание переменной для хранения this в компоненте Vue разделяет разные ссылки this под разным окружением. Но второй способ кажется несколько запутанным. Почему простая функция arrow function решает проблему? Не волнуйтесь, все будет объяснено позже.

Итак, в основном, когда вы вызываете функцию напрямую или даже передаете анонимную функцию в качестве параметра, это часто вызов образца, который может привести к таким проблемам.

const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      (function () {
        console.log(this.myName); // undefined,WTF...
      })();
    },
  },
  created() {
    this.getName();
  },
});
Вход в полноэкранный режим Выход из полноэкранного режима

функция объекта

Другой очень распространенный случай — это когда мы помещаем this в функцию объекта javascript. Это также очень опасное место, которое может привести к проблеме неправильного направления ссылки this.

var myName = "oh No!";

var obj = {
  myName: "Tristan",
  fn: function () {
    console.log(this.myName);
  },
};

obj.fn(); // Tristan

var fn = obj.fn;
fn(); // oh No!,WTF?
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что вывод вроде как не имеет смысла, поскольку вы вызываете ту же функцию fn в объекте obj. Обратите внимание, что мы использовали var вместо let, const.

Если мы используем let или const:

const myName = "oh No!";

const obj = {
  myName: "Ray",
  fn: function () {
    console.log(this.myName);
  },
};

obj.fn(); // Ray

var fn = obj.fn;
fn(); // undefined,WTF?
Войти в полноэкранный режим Выход из полноэкранного режима

В этой статье мы не будем тратить много времени на объяснение различий между var, let и const, что мы сделаем в будущем.

Найти ссылки this в js-объекте довольно просто. Вам просто нужно посмотреть, в каком объекте при вызове находится this. В качестве примера возьмем fn() выше. this вызывается под объектом obj, поэтому this ссылается на obj.

новый конструктор

Когда мы используем new перед вызываемой функцией, в javascript функция рассматривается как объект, где this становится основным свойством объекта.

function fn(myName) {
  this.myName = myName;
}

const newFn = new fn("Tris");

console.log(newFn.myName); // Tris
Вход в полноэкранный режим Выход из полноэкранного режима

Как и обычный js-объект:

const obj = {
  myName: "Tris",
};

console.log(obj.myName); // Tris
Войти в полноэкранный режим Выход из полноэкранного режима

DOM

Когда вы используете this на элементах DOM, когда вы используете его с addEventListener, this будет ссылаться на сам элемент DOM независимо ни от чего.

Например:

<button type="button" class="btn">1</button>
<button type="button" class="btn">2</button>
<button type="button" class="btn">3</button>
<button type="button" class="btn">4</button>
<button type="button" class="btn">5</button>
Войти в полноэкранный режим Выйти из полноэкранного режима
const buttons = document.querySelectorAll(".btn");

buttons.forEach(function (button) {
  button.addEventListener("click", function () {
    console.log(this); // The DOM you click
  });
});
Войти в полноэкранный режим Выйти из полноэкранного режима

Когда вы объявляете слушатель событий, добавляя addEventListener на определенном элементе DOM, он фактически будет ждать в будущем в определенный момент, когда вы совершите определенное действие, такое как click в данном случае. Между тем, функция обратного вызова будет привязана к элементу DOM, так что, в принципе, вы можете представить код выше как ниже, если это поможет понять:

const click = {
  button: '<button type="button" class="btn">1</button>',
  addEventListener() {
    console.log(this);
  },
};

click.addEventListener();
Вход в полноэкранный режим Выход из полноэкранного режима

Однако, при использовании this с элементами DOM, иногда вы обнаружите, что this ссылается на странные вещи. Это происходит, когда вы используете его с функциями arrow.

const buttons = document.querySelector(".btn");

buttons.forEach(function (button) {
  button.addEventListener("click", () => {
    console.log(this); // window ???
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Возможно, вы помните, что выше вы только что прочитали о arrow functions. На самом деле, стрелочные функции действительно включают некоторые важные моменты, связанные с этим, поэтому давайте продолжим эту эту тему, рассмотрев стрелочные функции.

выражения стрелочных функций

Прежде чем говорить о стрелочных функциях, необходимо понять разницу между стрелочными функциями и нормальными функциями.

В документации MDN говорится, что стрелочная функция не имеет своих this, arguments, super и new.target. Возникает вопрос, если у стрелочных функций нет своего this, то куда ссылается this при использовании стрелочных функций.

Оказывается, при использовании стрелочных функций this будет ссылаться на свою внешнюю область, родительскую область.
домену.

Давайте снова рассмотрим пример, который поначалу кажется запутанным:

const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const array = [1, 2, 3];
      array.forEach(() => {
        console.log(this.myName); // Ray * 3,is Good!
      });
    },
  },
  created() {
    this.getName();
  },
});
Вход в полноэкранный режим Выход из полноэкранного режима

В приведенном выше коде, если мы напишем его как array.forEach(function() {...}), результатом будет undefined или window в зависимости от среды, с которой вы работаете. Причина в том, что это приводит к вызову образца, как уже говорилось ранее. Однако при использовании стрелочных функций, из-за отсутствия this в природе наследования стрелочных функций, он затем проходит свой путь вверх, чтобы обратиться к ближайшему this, который в данном случае оказывается экземпляром vue.

ссылки

имя ссылка
документация MDN https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this
Документы MDN https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Оцените статью
devanswers.ru
Добавить комментарий