mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 21:07:49 +00:00
docs: rename article, finish initial draft
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
BIN
content/blog/javascript-bind-usage/component_this_explainer.png
Normal file
BIN
content/blog/javascript-bind-usage/component_this_explainer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
@@ -370,7 +370,7 @@ class HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addEventListener(name, fn) {
|
addEventListener(name, fn) {
|
||||||
for (let event of events) {
|
for (let event of this.events) {
|
||||||
fn(event)
|
fn(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -383,16 +383,153 @@ Let's chart out what's happening behind-the-scenes:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
To fix this, we can do one of two things:
|
# Fixing the problem with arrow functions
|
||||||
|
|
||||||
1) Assign
|
To fix the issues with `this` usage in event listeners, we can do one of two things:
|
||||||
|
|
||||||
|
1) **`.bind` the usage of `.add` in the event listener:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// This code doesn't work either
|
||||||
|
class MainButtonElement {
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
constructor(parent) {
|
||||||
|
this.el = document.createElement('button');
|
||||||
|
this.updateText();
|
||||||
|
this.addCountListeners();
|
||||||
|
parent.append(this.el);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateText() {
|
||||||
|
this.el.innerText = `Add: ${this.count}`
|
||||||
|
}
|
||||||
|
|
||||||
|
add() {
|
||||||
|
this.count++;
|
||||||
|
this.updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
addCountListeners() {
|
||||||
|
this.el.addEventListener('click', this.add.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.el.remove();
|
||||||
|
// This won't remove the listener properly
|
||||||
|
this.el.removeEventListener('click', this.add.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
However, this has some problems, as two `.bind` functions are not referentially stable:
|
||||||
|
```javascript
|
||||||
|
function test() {}
|
||||||
|
|
||||||
|
console.log(test.bind(this) === test.bind(this)); // False
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This means that we instead have to bind `add` at the function's base:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class MainButtonElement {
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
constructor(parent) {
|
||||||
|
this.el = document.createElement('button');
|
||||||
|
this.updateText();
|
||||||
|
this.addCountListeners();
|
||||||
|
parent.append(this.el);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateText() {
|
||||||
|
this.el.innerText = `Add: ${this.count}`
|
||||||
|
}
|
||||||
|
|
||||||
2.
|
// 😖
|
||||||
|
add = (function() {
|
||||||
|
this.count++;
|
||||||
|
this.updateText();
|
||||||
|
}).bind(this)
|
||||||
|
|
||||||
|
addCountListeners() {
|
||||||
|
this.el.addEventListener('click', this.add);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.el.remove();
|
||||||
|
this.el.removeEventListener('click', this.add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, we can...
|
||||||
|
|
||||||
|
2. **Use an arrow function rather than a class method**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class MainButtonElement {
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
constructor(parent) {
|
||||||
|
this.el = document.createElement('button');
|
||||||
|
this.updateText();
|
||||||
|
this.addCountListeners();
|
||||||
|
parent.append(this.el);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateText() {
|
||||||
|
this.el.innerText = `Add: ${this.count}`
|
||||||
|
}
|
||||||
|
|
||||||
|
add = () => {
|
||||||
|
this.count++;
|
||||||
|
this.updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
addCountListeners() {
|
||||||
|
this.el.addEventListener('click', this.add);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.el.remove();
|
||||||
|
this.el.removeEventListener('click', this.add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This works because arrow functions behave differently from `function` keyword functions.
|
||||||
|
|
||||||
|
**Arrow functions do not allow `this` to be rebound, even with `bind` or `call` usage**. This means that when `this` is set to `MainButtonElement` in the class, it will never rebind again, even when called inside of `HTMLElement`'s `addEventListener` usage.
|
||||||
|
|
||||||
|
We can see this in action in the demo from before:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class Cup {
|
||||||
|
contents = "water";
|
||||||
|
|
||||||
|
// Notice the arrow functions, `this` won't rebind
|
||||||
|
consume = () => {
|
||||||
|
console.log("You drink the ", this.contents, ". Hydrating!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bowl {
|
||||||
|
contents = "chili";
|
||||||
|
|
||||||
|
consume = () => {
|
||||||
|
console.log("You eat the ", this.contents, ". Spicy!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cup = new Cup();
|
||||||
|
bowl = new Bowl();
|
||||||
|
|
||||||
|
cup.consume = bowl.consume;
|
||||||
|
|
||||||
|
cup.consume();
|
||||||
|
```
|
||||||
|
|
||||||
|
Which will now output:
|
||||||
|
|
||||||
|
> You eat the chili. Spicy!
|
||||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Reference in New Issue
Block a user