Assume the following class
class Car {
constructor() {
this.startEngine()
}
startEngine = () => {
console.log('BRRRM!')
}
}
How do I mock startEngine so I can test if it was called in the constructor?
I somewhere read that I could access startEngine by Car.prototype.startEngine, but this does not seem to be the case as (in my case) Webpack hides the method in the Car function and are only accessible after instantiation (maybe because I defined it as an arrow function).
Related
I am new to JS and was learning classes and the inheritance but I faced the confusion of why to use the super method in child class. Here is the code:
class Animal {
constructor(name, weight) {
this.name = name;
this.weight = weight;
}
eat() {
return `${this.name} is eating!`;
}
sleep() {
return `${this.name} is going to sleep!`;
}
wakeUp() {
return `${this.name} is waking up!`;
}
}
class Gorilla extends Animal {
constructor(name, weight) {
super(name, weight);
}
climbTrees() {
return `${this.name} is climbing trees!`;
}
poundChest() {
return `${this.name} is pounding its chest!`;
}
showVigour() {
return `${super.eat()} ${this.poundChest()}`;
}
dailyRoutine() {
return `${super.wakeUp()} ${this.poundChest()} ${super.eat()} ${super.sleep()}`;
}
}
console.log((new Gorilla("Jonas", 12)).dailyRoutine());
As you can see the gorilla class uses super method and if I remove that super method I get an error but WHY? I just cannot when to use super method and why?
The call to super() initializes the super, or parent, class. The parent class and it's members must be initialized first, as the child class usually depends on them to some extent. You cannot inherit a class and not initialize the parent because you may not be able to access members that the child class depends on.
super() inside the constructor allows you to pass arguments to the parent constructor.
class Parent {
constructor(weNeedThis) {
console.log(weNeedThis);
}
}
const parent = new Parent("When constructing, it's easy to pass the argument");
class Child {
constructor() {
// But how do we pass an argument to the parent's constructor here?
}
}
So in other words, we need super() sometimes to pass arguments to the parent constructor.
if I remove that super method I get an error but WHY?
That's for consistency. If some constructors would call super() and others wouldn't, it is unclear when and how the parents constructor runs. Therefore it is specified, that if you provide your own constructor, you have to call super before you can access this. When super() gets called, the parent constructor gets run with the arguments provided, this gets initialized and is accessible from then on.
super gives you access to the parent class. In your specific example, you need to call the Animal constructor and provide the parameters that needs. The reason that it throws the error without it is that the Animal portion of your object can't be initialized without running through the constructor.
super will also give you reference to the other functions and properties within the parent class.
Check out the official documentation to get more information.
Something that is puzzling me is why when I define a react component class, values contained in the this object are undefined in methods defined (this is available in lifecycle methods) within the class unless I use .bind(this) or define the method using an arrow function for example in the following code this.state will be undefined in the renderElements function because I did not define it with an arrow function and did not use .bind(this)
class MyComponent extends React.Component {
constructor() {
super();
this.state = { elements: 5 }
}
renderElements() {
const output = [];
// In the following this.state.elements will be undefined
// because I have not used .bind(this) on this method in the constructor
// example: this.renderElements = this.renderElements.bind(this)
for(let i = 0; i < this.state.elements; i ++){
output.push(<div key={i} />);
}
return output;
}
// .this is defined inside of the lifecycle methods and
// therefore do not need call .bind(this) on the render method.
render() {
return (
<div onClick={this.renderElements}></div>
);
}
}
Then in the following example I do not need to use .bind(this) or an arrow function, this is available as expected in speak function
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
speak() {
console.log(this.name + ' barks.');
}
}
var d = new Dog('Mitzie');
d.speak();
http://jsbin.com/cadoduxuye/edit?js,console
To clarify, my question is two part. One) why in the second code example do I not need to call .bind(this) to the speak function, but I do in the React component for the renderElements function and Two) why do the lifecycle methods (render, componentDidMount, etc) already have access to the class' this object, but renderElements does not.
In the React docs it says the following
[React Component Class] Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance.
But clearly they do, as the second code example I've posted shows.
Update
Both links in the first two comments show a working example of React classes NOT using .bind(this) on class methods and it works fine. But still in the docs is explicitly says you need to bind your methods, or use an arrow function. In a project using gulp and babel I can reproduce. Could it mean browsers have updated things?
Update 2
My initial code example had this.renderElements() called directly in the render function. That would work as expected without binding the function, or defining it with an arrow function. The issue occurs when I put the function as an onClick handler.
Update 3
The issue occurs when I put the function as an onClick handler.
In fact it is not an issue at all. The context of this changes when passed to the onClick handler, so that's just how JS works.
The value of this primarily depends on how the function is called. Given d.speak();, this will refer to d because the function is called as an "object method".
But in <div>{this.renderElements}</div> you are not calling the function. You are passing the function to React which will call it somehow. When it is called, React doesn't know which object the function "belonged" to so it cannot set the right value for this. Binding solves that
I actually think what you really want is
<div>{this.renderElements()}</div>
// call function ^^
i.e call the function as an object method. Then you don't have to bind it.
Have a look at MDN to learn more about this.
Event handlers in the component will not be bound automatically to the component instance like other methods ( life cycle methods...).
class MyComponent extends React.Component {
render(){
return (
<div onClick={this.renderElements}>
{this.renderElements()} <-- `this` is still in side the MyComponent context
</div>
)
}
}
//under the hood
var instance = new MyComponent();
var element = instance.render();
//click on div
element.onClick() <-- `this` inside renderElements refers to the window object now
Check this example to understand this more :
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
run(){
console.log(this.name + ' runs');
}
speak() {
console.log(this.name + ' barks.');
this.run(); <-- `this` is still in the Dog context
return {onRun : this.run};
}
}
var d = new Dog('Mitzie');
var myDog = d.speak();
myDog.onRun() <-- `this` is now in the global context which is the `window` object
You can check this article for more information.
Functions in ES6 Classes - the case is explained very well by #Felix Kling. Every time you call a function on an object, this points to the object.
Lifecycle methods in React.Component - whenever React instantiates your component like myComponent = new MyComponent() it knows which object to call the lifecycle methods on, namely myComponent. So a simple call myComponent.componentDidUpdate() makes this available in the componentDidUpdate lifecycle method. Same for the other lifecycle methods.
Handlers & Bound in React.Component - this.state is undefined, because this is actually window - log it and see. The reason is that React invokes handlers on the global context, unless you have the handler bound to another context which overrides window (see #Phi Nguyen's answer also).
I think they have done that to allow you more flexibility, because in complex applications your handler may come from another component passed through props and then you would like to have the possibility to say: "Hey, React - this is not my component, but it's parent."
React's documentation is a bid misleading when it says
Methods follow the same semantics as regular ES6 classes, meaning that
they don't automatically bind this to the instance.
What they mean is that
var dog = new Dog('Mitzie');
speak = d.speak;
dog.speak() // this will be dog, because the function is called on dog
speak() // this will be window, and not dog, because the function is not bound
1.
Arrow Functions:
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous. These function expressions are best suited for non-method functions and they can not be used as constructors.
Function.prototype.bind():
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
2.Component Specs and Lifecycle
To be absolutely clear: Most lifecycle methods are not bound, but called on an instance using the dot notation (true for componentWillMount, componentWillUnmount, componentWillReceiveProps and so on...), except componentDidMount which is bound to the instance since it gets enqueued into the transaction.
Just always put the autoBind(this); code in your constructor and never worry about method pointers.
npm install --save auto-bind-inheritance
const autoBind = require('auto-bind-inheritance');
class Animal {
constructor(name) {
autoBind(this);
this.name = name;
}
printName() { console.log(this.name); }
...
}
let m = new Animal('dog');
let mpntr = m.printName;
m.printName() //> 'dog'
mpntr() //> 'dog', because auto-bind, binds 'this' to the method.
I have following code:
class BaseClass {
constructor() {
// BELOW LINE CALLS OVERRIDDEN FUNCTION BUT WITH `this` OF BaseClass
this.preHook();
}
// THIS FUNCTION IS BEING OVERRIDDEN IN CHILD CLASS
preHook(data) {
return data;
}
}
class ChildClass extends BaseClass {
constructor() {
super();
this.someKey = 'someValueFromChild';
}
preHook(data) {
console.log('this.someKey', this.someKey); //IT PRINTS undefined
}
}
var c = new ChildClass();
OUTPUT:
this.someKey undefined
Expecting
this.someKey someValueFromChild
So in BaseClass's constructer i am calling a function this.preHook() which is getting overridden in ChildClass, when we initialise ChildClass it calls BaseClass's constructer and then it calls this.preHook's overridden version , but instead of printing someValueFromChild which is defined in child class, it is printing undefined (it is using this of parent class instead of child`.
How can i make it to use this of child class.
How can i make it to use this of child class.
There is no "this of child class." There is only one object (backed by its prototype objects). this within BaseClass and this within ChildClass both refer to the same object.
The reason accessing this.someKey results in undefined in that code is that the child constructor doesn't set someKey until after the parent constructor is finished — and the parent constructor calls preHook, so preHook is called before the child constructor sets someKey. So there is no someKey property at all as of when preHook tries to read it from the object. You'd see this if you stepped through the code with a debugger. Here's an annotated version showing what happens when:
class BaseClass {
constructor() {
console.log("BaseClass constructor start");
this.preHook();
console.log("BaseClass constructor finish");
}
// THIS FUNCTION IS BEING OVERRIDDEN IN CHILD CLASS
preHook(data) {
console.log("BaseClass.preHook called");
return data;
}
}
class ChildClass extends BaseClass {
constructor() {
console.log("ChildClass constructor start");
super();
console.log("ChildClass constructor setting `someKey`");
this.someKey = 'someValueFromChild';
console.log("ChildClass constructor finish");
}
preHook(data) {
console.log("ChildClass.preHook called");
console.log('this.someKey', this.someKey);
}
}
var c = new ChildClass();
.as-console-wrapper {
max-height: 100% !important;
}
Calling overrideable methods from a constructor is a well-established anti-pattern — and this is part of the reason. :-) Instead, don't have the parent constructor call preHook. Do it after construction.
There are workarounds, but the best option by far is to not have the BaseClass constructor call preHook at all. How you do that in your code obviously depends on the greater context of your code, so I can't give you an example.
There is race condition that the consequence of natural limitations of ES6 classes. this should always appear after super() in child constructor. Since this.preHook() is called in parent constructor (super), there's no way how this.someKey can be assigned in constructor and appear during this.preHook() call.
Otherwise this.preHook() call should happen in child constructor only, this defies the concept of hooks and prevents child class from being properly extended.
In order to avoid race condition, someKey should be defined on class prototype, thus super and this precedence is no longer a problem:
class ChildClass extends BaseClass {
constructor() {
super();
}
get someKey() {
return 'someValueFromChild';
}
...
}
The alternative that allows to maintain proper class lifecycle is to implement hooks on application level, like it's usually done in JS frameworks. Hookable classes should be instantiated through certain interface and never directly, i.e:
function instantiateWithHooks(cls, args) {
const instance = new cls(...args);
instance.preHook();
return instance;
}
As explained in another answer, when child class is instantiated, there is only one this, which is (usually) an instance of child class.
I see this a lot in ES6 React code
class Foo extends React.Component {
bar = () => {
console.log("bar")
}
baz() {
console.log("baz")
}
}
Seems like they both define methods bar and baz on Foo but how are they different.
The declarations differ in how the function are written and the context of this,
In the first syntax
bar = () => {
console.log("bar")
}
the function is written using Arrow function syntax.
An arrow function does not have its own this; the this value of
the enclosing execution context is used. Hence this keyword inside
this function will refer to the context of the React class
However the second declaration
baz() {
console.log("baz")
}
is a simple function and this keyword in this function refers to the context of the function itself.
So when you try to access React class Properties/functions like this.state or this.setState you will get an error in the second case(if you haven't used binding anywhere else for this function(example constructor)) whereas it would work in the first case since for an arrow function, this means the same thing within the function body as it does outside of it. Which means that if you use arrow functions within your component’s custom functions, they can use this and this.setState with no surprises.
Check this answer on why you need to bind functions in React classes
The previous answers are absolutely correct and are why you would want to be using arrow functions in react classes.
I just wanted to also point out a subtle difference that is a potential pitfall of their use in a class to avoid surprises...
Arrow functions defined on a class are added to the instance as a property that just so happens to be a function, whereas defining not as an arrow function will add the function as a method to the class's prototype.
In React components that will never be extended this is fine, but if the case arises you want to subclass a component, you will not be able to override an arrow function expecting to be able to call the base class via super, you can only overwrite it in its entirety
class Button extends React.PureComponent {
// class method - overridable in subclass
pressMethod: function(): void {
console.log('beep')
}
// instance property functions - only overwriteable in subclass
pressArrow = (): void => {
console.log('boop')
}
pressArrowTwo = (): void => {
console.log('blip')
}
}
class BigRedButton extends Button {
// class method - overides subclass one,
// can call superclass method via `super`
pressMethod: function(): void {
super.pressMethod() // prints 'beep' to console
console.log('BOOOOOOOOM!!!')
}
// instance property function
// cannot call superclass via `super` as lambda's have no prototype
pressArrow = (): void => {
super.pressArrow() // TypeError - not a function
console.log('BOOOOOOOOM!!!')
}
// completely overwrites instance property of same name in subclass
// doesn't try to access prototype so won't error but is limited
pressArrowTwo = (): void => {
console.log('BOOOOOOOOM')
}
}
For simplicity, both are equal:
bar = () => { ... }
this.bar =this.bar.bind(this)
I am in the process of learning ES2015 and I'm not fully understanding everything about classes. To my understanding you define a class like this:
class Parent {
constructor() {
// ...
}
}
and a subclass like this:
(where super() must be invoked to initially run the constructor from the parent class).
class subClass extends Parent {
constructor(name, description, url) {
// subclass must call super to invoke the parent's constructor method
super();
}
}
What exactly is the constructor method, why is it important, and why does a parents constructor needs to be invoked when creating an instance of a subclass?
When you create a new instance of a class, the constructor method will be called with the arguments you passed through. In this function, you put any code to construct the instance of your class, like initializing properties.
class Person{
constructor(name){
this.name = name;
}
sayHi(){
alert(`${this.name} says hello!`);
}
}
let person = new Person('Jerry');//this param will be send to the constructor method
person.sayHi();
The result of this code will be an alert saying "Jerry says hello!".
Although, the constructor method is not required. The following code will work too.
class Person{
sayHi(){
alert("Someone says hello!");
}
}
let person = new Person();
person.sayHi();
In case you have a subclass, you might want to call the constructor method of the parent class. This is also not required, but in most cases will be done.
class Person{
constructor(name){
this.name = name;
}
sayHi(){
alert(`${this.name} says hello!`);
}
}
class SophisticatedPerson extends Person{
constructor(name){
super(name);//this will call the constructor method of the Person class
}
sayHi(){
alert(`${this.name} says: Top of the hat to you, sir!`);
}
oldSayHi(){
super.sayHi();//call sayHi of the Person class
}
}
let person = new SophisticatedPerson('Jerry');
person.sayHi();//calls the overidden sayHi method of SophisticatedPerson class
person.oldSayHi();//calls the oldSayHi of SophisticatedPerson class which calls sayHi of Person class
With 'super' you can call the constructor via 'super()' or any other methods via 'super.methodName()' of the parent class as illustrated above.
Extra notice: if you don't provide a constructor method on your child class, the parent constructor method will be called.
The constructor method is the method that's invoked when you construct an object with the new keyword. It's used to initialize fundamental properties of your object.
The reason you must call the parent constructor (other than the simple fact that this is how "the language rules" are defined) is that you need to allow the parent class to do its initialization.
These are fairly basic, common concepts in many OOP languages.