Javscript
When to use =>
The arrow function is very helpful to write consise code. They are called "Fat arrow". They are:
- Anonymous, basically it could be compared to a lambda
- They change the way
thisbinds to functions
So with arrow functions, we do not have to type the function and the return keyword.
The fat arrow cannot blindly replace function declarations. Basically everytime you need to access this of the current environement which is traditionally passed down with const self = this you can replace it by an arrow function.
const obj = {
prop: 'test',
foo: function () {
const self = this
bar()
function bar() {
console.log(self)
}
}
}
obj.foo() // { prop: 'test', foo: [Function: foo] }
There are three JavaScript methods that can be use to bind the context call(), apply() and bind(). They are quite similar. The difference is that unlike the two others bind() is not called immediately but returns a closure.
Same result but with the fat arrow.
const obj = {
prop: 'test',
foo: function () {
const bar = () => {
console.log(this)
}
bar()
}
}
obj.foo()
// { prop: 'test', foo: [Function: foo] }
this did behave like we expected. Additionnaly, arrow function cannot
- be called with
new - be called with
bind()orcall()
Arrow functions are only callable and class functions are only constructors while function expression are both.
We have seen that arrow function cannot use bind(). When Vuejs creates a component instance, it uses bind() behind the scene. Therefore we need to declare methods with function expressions:
methods: {
someFunction() {
// at the moment, `this` points to the `methods` object that the function is defined in.
},
someOtherFunction() {
this.someFunction(); // this should give an error
}
}
But when the component is instanciated, this is bound to the component instance and not anymore to the methods object wich allows to call this.someFunction() or this.nameOfAVariable()
The this Context
MDN In an object, a method will behave has we expect
In objects, this
var obj = {
foo: function() {
return this;
}
};
obj.foo() === obj; // true
On the other hand a function using this uses the global context:
function foo() {
console.log(this);
}
// window
But you can "instantiate" a class from a function, therefore using the above function with the new keyword:
new foo()
// foo
Notice that this is correctly bound to the newly created object. It works like creation of the object in the first example. Like in other language, the new keyword calls a constructor. Therefore we could:
function Foo(bar) { // it's a class so we use uppercase
this.bar = bar
}
I can instantiate many objects:
const obj1 = new Foo('test');
const obj2 = new Foo('test2');
And if I call a method on obj1:
console.log(obj1.method())
// TypeError: obj1.method is not a function
Of course it crashes, we did not define it. Let's add it to the object
obj1.method = function () {
return 'method called';
}
obj1.method();
// method called
obj2.method();
// TypeError: obj2.method is not a function
The method does not exist on obj2 it was added to the instance of obj1
So we coud try to add it to Foo our class
Foo.method2 = function() {
return 'method2 called',
}
obj1.method2();
obj2.method2();
// TypeError: obj1.method2 is not a function
// TypeError: obj2.method2 is not a function
Foo.method2();
// method2 called
So method 2 has been added to the instance the function object Foo but it did not spread to the instatiated objects. This is because JavaScript is a language with prototypes. If we want to modifiy the Class we actually need to modify the prototype of the function.
So let's modify the prototype:
Foo.prototype.method3 = function() {
console.log(this);
return 'hello method 3';
}
Now both objects have a method available.
console.log(obj2.method3())
// Foo {}
// hello method 3
This in the context of method3() would be exactly what we expect this would reference Foo
Members or properties of a Class work in the exact same manner.
Foo.prototype.color = 'black'
And both object would print black if obj1.color or obj2.color was called.
But if I set a new color to obj1:
obj1.color = 'white';
If we log it, it correctly prints white and this happens:
console.log(obj1.__proto__.color) // black
console.log(obj2.__proto__.color) // black
console.log(obj1.color) // white
console.log(obj2.color) // black
But wait... did we not declare obj1 as constant? Yes we did... The linter should pick it up. But we will speak more on that.
We digressed slightly on prototypes but what we saw so far is that the this keywords defaults to the global scope (window in the browser) when called as an unbound function.
This means that:
const obj3 = {
prop: 'test',
foo: function () {
bar()
function bar() {
console.log(this)
}
}
}
obj3.foo() // window
Yes, again the function bar() is unbound, this means that this refers to the window object. One of the method bind the context is to create a variable called by convention self and pass it down as a closure.