this
ES6 Classes
Key Questions
- What is `this`?
- What is "prototypal inheritance"?
- How does `this` relate to inheritance?
- What are "classes" in JavaScript?
- How does `this` come into play in classes?
'this'
possibly the most misunderstood keyword in javascript
No accepted answers 😢
simple definition
this equals the context of the nearest parent object from which this was evaluated at execution time
simpler definition
In JavaScript, "this" refers to the object invoking the function that is being executed
So, lets see some
"toy examples"
to test that definition
//in global scope
console.log(this);
function foo(){
console.log(this);
}
foo();
const foo = {
bar: function(){
console.log(this)
}
};
foo.bar();
const clickHandler = function(evt) {
console.log(this);
};
document
.querySelector('#element')
.addEventListener('click', clickHandler);
const that = this;
const clickHandler = function(evt) {
console.log(that);
};
document
.querySelector('#element')
.addEventListener('click', clickHandler);
let clickHandler = function(evt) {
console.log(that);
};
clickHandler = clickHandler.bind(this)
document
.querySelector('#element')
.addEventListener('click', clickHandler);
x
const person = {
greet: function(){
console.log(this);
}
}
const mygreet = person.greet;
mygreet();
function baz(){
console.log(this);
}
const foo = {
bar: function(){
baz();
}
}
foo.bar();
const clickHandler = (evt) => {
console.log(that); // window
};
document
.querySelector('#element')
.addEventListener('click', clickHandler);
Arrow Function
this differences
An arrow function does not create its own this context, so this has its original meaning from the enclosing context.
-https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
What is prototypal inheritance?
Let's answer that question by comparing and contrasting Classical Inheritance
Classical
Classes define "patterns" or "templates" which are not instanciated
Prototypal
No classes, only actual objects and instanciated things
Different types of inheritance: single, multi-level, multiple, hybrid
Children only inherit from their prototype object
Both are a system for sharing/reusing functionality/behavior
Ok, so how does it actually work?
There is no such thing as a class (nothing abstract)
There are several global objects in every JavaScript environment. They are actual objects.
- Object
- Function
- Boolean
- Symbol
- Error
- ...
These are the fundamental, basic objects upon which all other objects are based. This includes objects that represent general objects, functions, and errors.
The Object object has methods which can be called
Object.freeze()
Object.create()
Object.keys()
The Object object also has prototype methods which other objects inherit
Object.prototype.toString()
Object.prototype.valueOf()
Every object contains a prototype property, which is a pointer (link) to the actual object that it "inherits" from
When a method not defined on an object is called, the prototype "chain" is crawled until the method is found, or not.
const person = { name: 'Bob' };
person.toString() // [object Object]
person
- name
- prototype
- __proto__
Object
- keys
- ...
- prototype
- __proto__
- ...
- toString
Cat
- name
- prototype
- __proto__
Object
- keys
- prototype
- toString
- __proto__
Animal
- Genus
- prototype
- speak
-__proto__
Cat
- name
- prototype
- __proto__
Object
- keys
- prototype
- toString
- __proto__
Animal
- Genus
- prototype
- speak
- toString
-__proto__
Overriding prototype methods
How does this relate to prototypal inheritance
The new keyword executes the function in a new context. This equals a new object
const Person = function({name, age}) {
this.name = name;
this.age = age;
this.greet = function() { return "hey"; }
}
const Bob = new Person({name: 'Bob', age: 35});
This pattern is called a "constructor function"
const Person = function({name, age}) {
this.name = name;
this.age = age;
this.greet = function(){ return "hey"; }
}
const Bob = new Person({name: 'Bob', age: 35});
After declaration, properties can be added the to "constructor" function
const Person = function({name, age}) {
this.name = name;
this.age = age;
}
Person.prototype.doWork = function(){ }
const Bob = new Person({name: 'Bob', age: 35});
Bob.doWork()
const Person = function({name, age}) {
this.name = name;
this.age = age;
}
Person.prototype.doWork = function(){ }
const Bob = new Person({name: 'Bob', age: 35});
Bob.doWork()
Bob
- name
- age
- prototype
- __proto__
Person
- prototype
- doWork
Enter ES6 Classes
Managing inheritance over multiple objects and prototype chains can be complex and difficult
ES6 classes make creating objects with prototypal inheritance easier
ES6 classes are "syntactic sugar" to make constructor functions look more approachable
JavaScript classes introduced in ECMAScript 2015 are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.
-https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
ES6 classes are not classical OOP classes
class Person{
constructor({name, age}){
this.name = name;
this.age = age;
}
}
const Bob = new Person({name: "Bob", age: 35});
const Person = function({name, age}) {
this.name = name;
this.age = age;
}
const Bob = new Person({name: "Bob", age: 35});
Equivalent
class Person{
constructor({name, age}){
this.name = name;
this.age = age;
}
}
const Bob = new Person({name: "Bob", age: 35});
class keyword
opening and closing curly brace
constructor method
Must use new keyword
class Person{
constructor({name, age}){
this.name = name;
this.age = age;
}
}
const Bob = new Person({name: "Bob", age: 35});
extends
classes can use the extends keyword to create a prototypal inheritance link
class Animal{
constructor({name}){
this.name = name;
}
}
class Cat extends Animal{
constructor({name, color}){
super({name});
this.color = color;
}
}
Animal extends Cat & passes Cat's constructor some properties
class Animal{
constructor({name}){
this.name = name;
}
}
class Cat extends Animal{
constructor({name, color}){
super({name});
this.color = color;
}
}
Cat
- color
- prototype
- __proto__
Object
- ...
- prototype
- __proto__
- ...
Animal
- name
- prototype
-__proto__
class Cat extends Animal{
constructor({name, color}){
super({name});
this.color = color;
}
speak(){
return "🐱 Meow."
}
}
const Spot = new Cat({name: "Spot", color: "#d89400"});
Spot.speak(); //"🐱 Meow."
Classes can contain "methods". In reality, a method is just a function attached to the object prototype
class Cat extends Animal{
constructor({name, color}){
super({name});
this.color = color;
}
speak(){
return `${this.name} says "Meow" 🐱`;
}
}
const Spot = new Cat({name: "Spot", color: "#d89400"});
Spot.speak(); // 'Spot says "Meow" 🐱';
Since we are using this in a function, it refers to the object which called it -- Spot in this case. this.name is resolved by following the prototype chain up to Animal
class ButtonWidget{
constructor({alertValue}){
this.alertValue=alertValue;
}
clickHandler(){
console.log(this.alertValue);
}
render(){
const btn = document.createElement('button');
btn.textContent = "Click Me!";
btn.addEventListener('click', this.clickHandler);
return btn;
}
}
Spot the bug:
class ButtonWidget{
constructor({alertValue}){
this.alertValue=alertValue;
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler(){
console.log(this.alertValue);
}
render(){
const btn = document.createElement('button');
btn.textContent = "Click Me!";
btn.addEventListener('click', this.clickHandler);
return btn;
}
}
Now clickHandler works as expected
Applying stuff we learned to React Components
class SimpleButton extends React.Component{
constructor(props){
super(props);
this.clickHandler = this.clickHandler.bind(this)
}
clickHandler(evt){
// "this" is now properly bound
// to the context of the class
}
render(){
return(
<div>
<button onClick={this.clickHandler}> Click Me! </button>
</div>
)
}
}
class SimpleButton extends React.Component{
constructor(props){
super(props);
}
clickHandler = (evt) => {
// using an arrow function, `this` doesn't get
// assigned to it's callers context (button)
}
render(){
return(
<div>
<button onClick={this.clickHandler}> Click Me! </button>
</div>
)
}
}
Arrow functions as class properties are a proposal for ES7 that is likely to be adopted. This syntax is supported in the React Starter Kit
this is great.
Prototypal inheritance is great.
React is great.
You're great, too :)
THE END
this, prototypes, es6 classes, & react
By Michael Jasper
this, prototypes, es6 classes, & react
- 838