Removing JavaScript’s “this” keyword makes it a better language. Here’s why.
thisis of course the source of much confusion in JavaScript. The reason being thatthisdepends on how the function was invoked, not where the function was defined.JavaScript without
thislooks like a better functional programming language.this losing context
Methods are functions that are stored in objects. In order for a function to know on which object to work,
thisis used.thisrepresents the function’s context.
thisloses context in many situations. It loses context inside nested functions, it loses context in callbacks.Let’s take the case of a timer object. The timer objects waits for the previous call to finish before making a new call. It implements the recursive setTimeout pattern. In the next example, in nested functions and callbacks,
thisloses context: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"); }
thisloses context when the method is used as an event handler.Let’s take the case of a React component that builds a search query. In both methods, used as event handlers,
thisloses context: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> ); } }There are many solutions for these issues : the
bind()method, the that/self pattern, the arrow function.For more on how to fix
thisrelated issue issues, take a look at What to do when “this” loses context.this has no encapsulation
thiscreates security problems. All members declared onthisare public.class Timer{ constructor(callback, interval){ this.timerId = "secret"; } }const timer = new Timer(); timer.timerId; //secretNo this, no custom prototypes
What if, instead of trying to fix
thislosing context and security problems, we get rid of it all together?Removing
thishas a set of implications.No
thisbasically means noclass, no function constructor, nonew, noObject.create().Removing
thismeans no custom prototypes in general.A Better Language
JavaScript is both a functional programming language and a prototype-based language. If we get rid of
this, we are left with JavaScript as a functional programming language. That is even better.At the same time, without
this, JavaScript offers a new, unique way, of doing Object Oriented Programming without classes and inheritance.Object Oriented Programming without this
The questions is how to build objects without
this.There will be two kind of objects:
- pure data objects
- behavior objects
Pure Data Objects
Pure data objects contain only data and have no behavior.
Any computed field will be fill-in at creation.
Pure data objects should be immutable. We need to
Object.freeze()them at creation .Behavior Objects
Behavior objects will be collections of closures sharing the same private state.
Let’s create the Timer object in a
this-less approach.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();The
timerobject has two public methods:startandstop. Everything else is private. There are nothislosing context problems as there is nothis.Components without this
thismay be required by many components’ frameworks, like React or Vue for example.In React, we can create stateless functional components, without
this, as pure functions.function ListItem({ todo }){ return ( <li> <div>{ todo.title}</div> <div>{ todo.userName }</div> </li> ); }We can also create stateful components without
thiswith React Hooks. Take a look at the next example: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> ); };Removing arguments
If we get rid of
this, we should also get rid ofargumentsas they have the same dynamic binding behavior.Getting rid of
argumentsis pretty simple. We just use the new rest parameter syntax. This time the rest parameter is an array object:function addNumber(total, value){ return total + value; }function sum(...args){ return args.reduce(addNumber, 0); }sum(1,2,3); //6Conclusion
The best way to avoid
thisrelated problems is to not usethisat all.JavaScript without
thisis a better language. And more specifically, JavaScript withoutthisis a better functional programming language.We can build encapsulated objects, without using
this, as collections of closures.With React Hooks we can create
this-less stateful components.
Removing JavaScript’s “this” keyword makes it a better language. Here’s why.
 1 2