Главная » Статьи » Удаление ключевого слова «this» из JavaScript делает язык лучше

Удаление ключевого слова «this» из JavaScript делает язык лучше

Удаление ключевого слова «this» из JavaScript делает язык лучше

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

this теряет контекст

Методы — это функции, которые хранятся в объектах. Для того, чтобы функция знала, над каким объектом работать, используется this. this представляет функции контекст.

this теряет контекст во многих ситуациях. Оно теряет контекст внутри вложенных функций, оно теряет контекст в обратных вызовах.

Давайте возьмем случай объекта таймера. Объекты таймера ожидают завершения предыдущего вызова, прежде чем выполнить новый вызов. Они реализует рекурсивный шаблон setTimeout. В следующем примере во вложенных функциях и обратных вызовах this теряет контекст:

class Timer { constructor(callback, interval){ this.callback = callback; this.interval = interval; this.timerId = 0; } executeAndStartTimer(){ this.callback().then(function startNewTimer(){ this.timerId = setTimeout(this.executeAndStartTimer, this.interval); }); } start(){ if(this.timerId === 0){ this.executeAndStartTimer(); } } stop(){ if(this.timerId !== 0){ clearTimeout(this.timerId); this.timerId = 0; } }
} const timer = new Timer(getTodos, 2000);
timer.start(); function getTodos(){ console.log("call"); return fetch("https://jsonplaceholder.typicode.com/todos");
}

this теряет контекст, когда метод используется в качестве обработчика события. Давайте возьмем случай компонента React, который создает поисковый запрос. В обоих методах, используемых в качестве обработчиков событий, this теряется контекст:

class SearchForm extends React.Component { handleChange(event) { const newQuery = Object.freeze({ text: event.target.value }); this.setState(newQuery); } search() { const newQuery = Object.freeze({ text: this.state.text }); if (this.props.onSearch) this.props.onSearch(newQuery); } render() { return ( <form> <input onChange={this.handleChange} value={this.state.text} /> <button onClick={this.search} type="button">Search</button> </form> ); }
}

Существует множество решений этих проблем: метод bind(), шаблон that/self, функция стрелки.

this не имеет инкапсуляции

this создает проблемы безопасности. Все объявленные члены this являются публичными.

class Timer { constructor(callback, interval){ this.timerId = "secret"; }
} const timer = new Timer();
timer.timerId; //secret

Нет this, нет пользовательских прототипов

Что, если вместо того, чтобы пытаться исправить утраченный контекст this и проблемы с безопасностью, мы избавимся от всего этого разом?

Удаление this имеет множество последствий. Отсутствие this в основном означает отсутствие class, отсутствие конструктора функции, отсутствие new, отсутствие Object.create(). Удаление this означает отсутствие пользовательских прототипов в целом.

Лучший язык

JavaScript — это и функциональный язык программирования, и язык на основе прототипов. Если мы избавимся от this, у нас останется JavaScript как функциональный язык программирования. Это даже лучше. В то же время, без this JavaScript предлагает новый, уникальный способ выполнения объектно-ориентированного программирования без классов и наследования.

Объектно-ориентированное программирование без this

Вопрос в том, как строить объекты без this. У нас будут два вида объектов:

чистые объекты данных

объекты поведения

Чистые объекты данных

Чистые объекты данных содержат только данные и не имеют поведения. Любое вычисленное поле будет заполнено при создании. Чистые объекты данных должны быть неизменными. Нам нужно Object.freeze() при их создании.

Объекты поведения

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

function Timer(callback, interval){ let timerId; function executeAndStartTimer(){ callback().then(function makeNewCall(){ timerId = setTimeout(executeAndStartTimer, interval); }); } function stop(){ if(timerId){ clearTimeout(timerId); timerId = 0; } } function start(){ if(!timerId){ executeAndStartTimer(); } } return Object.freeze({ start, stop }); } const timer = Timer(getTodos, 2000);
timer.start();

У объекта timer есть два открытых метода: start и stop. Все остальное закрыто. Нет проблем с потерей this контекста, так как нет this.

Память

Система прототипов лучше в плане использования памяти. Все методы создаются только один раз в объекте-прототипе и используются всеми экземплярами.

Затраты памяти на создание объектов поведения с использованием закрытий при создании тысяч одинаковых объектов значительны. В приложении у нас есть несколько объектов поведения. Если мы возьмем, например, объект поведения хранилища, в приложении будет только один его экземпляр, поэтому при использовании закрытий для его создания не требуется никаких дополнительных затрат памяти.

В приложении могут быть сотни или тысячи чистых объектов данных. Чистые объекты данных не используют закрытия, поэтому нет затрат памяти.

Компоненты без this

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

function ListItem({ todo }){ return ( <li> <div>{ todo.title}</div> <div>{ todo.userName }</div> </li> );
}

Мы также можем создавать компоненты с сохранением состояния без использования this с помощью React Hooks. Рассмотрим следующий пример:

import React, { useState } from "react"; function SearchForm({ onSearch }) { const [query, setQuery] = useState({ text: "" }); function handleChange(event) { const newQuery = Object.freeze({ text: event.target.value }); setQuery(newQuery); } function search() { const newQuery = Object.freeze({ text: query.text }); if (onSearch) onSearch(newQuery); } return ( <form> <input type="text" onChange={handleChange} /> <button onClick={search} type="button">Search</button> </form> );
};

Удаление arguments

Если мы избавимся от this, мы также должны избавиться от arguments, поскольку у них одинаковое поведение динамического связывания.

Избавиться от этого arguments довольно легко. Мы просто используем новый синтаксис параметра rest. На этот раз параметр rest является объектом массива:

function addNumber(total, value){ return total + value;
} function sum(...args){ return args.reduce(addNumber, 0);
} sum(1,2,3); //6

Заключение

Лучший способ избежать связанных с this проблем — вообще не использовать this. JavaScript без this — лучший язык. А точнее, JavaScript без this — это лучший функциональный язык программирования.

Мы можем создавать инкапсулированные объекты без использования this в качестве коллекций закрытий. С помощью React Hooks мы можем создавать без this компоненты с сохранением состояния.

Ключевое слово this не может быть удалено из JavaScript, без разрушения всех существующих приложений. Однако что-то мы сделать можем. Мы можем написать собственный код без this и позволить его использовать только в библиотеках. Тем временем вводятся новые правила ESLint, запрещающие использование this.

Автор: Cristi Salcescu

Источник: https://medium.freecodecamp.org/

Редакция: Команда webformyself.