Arithmetics
// + performs string or number addition
"hello" + 5 // hello5
// Rest of arithmetic operators perform number type casting
5 / '5' // 1
"hello" - 4 // NaN
// Unary + operator transforms value to number
+'-3' // -3
// The unary - operator transforms the value to number
// and performs a negation operation
-'3' // -3
[] + [] // '' [].toString()
[] - [] // 0 Number([])
Arithmetics
// Unary ! negates the boolean representation of the value
!false // true
!"hello" // false
// Falsey values are
NaN, undefined, 0, false, '', null
// ++ operator increments the value by one
var x = 5
var y = ++x // Increments and return the value
var z = y++ // Returns and increments the value
// -- operator decrements the value by one
Other
// typeof returns the type of the variable
typeof 2 // returns 'number'
typeof true // returns 'boolean'
// delete removes a property from an object
var a = { field: 'value' }
delete a.field // true
Comparission
// == performs a unestrict comparission
'5' == 5 // true
// === performs a strict comparission
'6' === 6 // false
spread operator
// can be used as an argument wherever
// array arguments are expected
const a = [1, 2, 3]
const b = [5, ...a, -3 ]
// Also in functions
function doSomething(first, ...rest) {}
doSomething(...a)
spread operator
// can be used as any pair
// key-value arguments
const a = {
property: 'one'
}
const b = {
...a,
other: 'test'
}
spread operator
// can be destructured
const a = [1,2,3]
const [first, ...rest] = a
const c = {
thing: 'test',
address: {
street: 'abc'
}
}
const {address, ...obj} = c
var thing = 5
function makeSumator(a) {
var x = 3;
return function(b) {
return b + x + a
}
}
const sumatorThree = makeSumator(3)
const sumatorSeven = makeSumator(7)
sumatorThree(5) // 5 + 3 + 3
sumatorThree(6) // 6 + 3 + 3
and primitive values are "autoboxed". Wrapped in a temporary reference object
var a = 'test'
'test'.contains('es')
var a = new Number(3)
var b = new Number(3)
a == b
3 == 3
`hello ${who}`
"hello"
'hello'
typeof "hello" // string
typeof 'hello' // string
var world = 'world'
var text = `hello
${world}`
var text = "this is a line \n" +
" this is another line"
var a = new String('test')
typeof a // 'object'
var b = new String('test')
a == b // false because they are different objects
a == 'test' // true
a.valueOf() // 'test'
var c = {
valueOf: function() {
return 'test'
}
}
c == 'test'
var a = 'test'
a.charAt(0) // 't'
a.indexOf('est') // 1
a.length // 4
a.toUpperCase() // 'TEST'
'TEST'.toLowerCase() // test
'TEST'.split('') // ['T', 'E', 'S', 'T']
function doSomething(arg, arg2) {
console.log(arg, arg2)
}
var myFunction = function() { /* do something * /}
Function expressions can have a named or anonymous function
They are not hoisted
var factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
};
console.log(factorial(3));
function a() {}
// Function.prototype.length
a.length // 0
// Function.prototype.name
a.name // 'a'
// Function.prototype.apply()
// Function.prototype.call()
// Function.prototype.bind()
function f1() {
return this
}
// In a browser:
f1() === window;
// In Node:
f1() === global;
the value of `this` is determined by how a function is called
function f1() {
'use strict';
return this
}
f1() // undefined
window.f1() // window
in strict mode, unless calling the method from an object, the value of this remains undefined
arrow functions retain the context of the enclosing scope
Normal functions in JavaScript bind their own this value, however the this value used in arrow functions is actually fetched lexically from the scope it sits inside. It has no this, so when you use this you’re talking to the outer scope.
function FooCtrl (FooService) {
this.foo = 'Hello';
FooService
.doSomething((response) => this.foo = response);
}
var obj = {
bar: function() {
var x = function() {
return this
};
return x;
}
};
var c = obj.bar()
c() // window
arrow functions retain the context of the enclosing scope
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
var c = obj.bar()
c() // obj
arrow functions retain the context of the enclosing scope
var obj = {
name: 'pepe',
getName: function() {
return this.name
}
};
when called in object methods, this points to the object
function Person(name) {
this.name = name
}
const person = new Person('juan')
person.name // 'juan'
when using the "new" keyword, this gets bound to the new instance that is being created
function setName(name) {
this.name = name
}
var person = {
name: 'peter'
}
setName.call(person, 'parker')
the value of `this` is determined by how a function is called
It can be set during execution and can be different each time the function is called
function setName(name) {
this.name = name
}
var person = {}
setName.call(person, 'pedro')
function setData(name, surname) {
this.name = name
this.surname = surname
}
var person = {}
setData.apply(person, ['pedro', 'blanco'])
function setData(name, surname) {
this.name = name
this.surname = surname
}
var person = {}
const setPeter = setData.bind(person, 'peter')
setPeter('parker')
setPeter('not parker')
Creates a new function bound to a context and some arguments
`arguments` is a reserved keyword that stands for an "array like" object (but not array) that represents the call arguments of the function
function setNameAndSurname(name, surname) {
this.name = name
this.surname = surname
}
var person = {
setData: function setData() {
setNameAndSurname.apply(this, arguments)
}
}
person.setData('hey', 'hello')
function setName(name, surname = 'pepe') {
this.name = name
this.surname = surname
}
function simpleJoin(stringArray) {
var accumulator = ‘’
for (var i=0, l=stringArray.length; i < l; i++) {
accumulator = accumulator + stringArray[i]
}
return accumulator
}
function simpleJoin(stringArray, acc = '') {
if(stringArray.length === 0) {
return acc
}
const [first, ...rest] = stringArray
return simpleJoin(rest, acc + first)
}
var newCars = cars.map(c => {
return {
...c,
clean: true
}
})
var newCars = cars.filter(c => c.clean === true)
var newCar = cars.find(c => c.name === 'Volvo FTurbo mix')
var carsWeight = cars
.reduce((acc, next) => acc + next.weight, 0)
square([1,2,3])
// [ 1, 4, 9 ]
sumAllElementsOfList([1,2,3])
// 6
sumList([1,2,3,4], [4, 54, 212, 213], [123,2312, 231])
// 3160
More on Modules
This is more obvious in the case of libraries (or modules)
var names = ["Sunday", "Monday",
"Tuesday", "Wednesday",
"Thursday", "Friday",
"Saturday"];
function dayName(number) {
return names[number];
}
dayName(0) // "Sunday"
functions are the only thing that will create a new scope, so functions are needed to create a module
var dayName = function() {
var names = ["Sunday", "Monday",
"Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
return function(number) {
return names[number];
};
}();
dayName(0) // "Sunday"
var weekDay = function() {
var names = ["Sunday", "Monday",
"Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
return {
name: function(number) { return names[number]; },
number: function(name) { return names.indexOf(name); }
};
}();
Allows to expose certain methods or properties and leave the rest hidden from the outside world
(function(exports) {
var names = ["Sunday", "Monday",
"Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
exports.name = function(number) {
return names[number];
};
exports.number = function(name) {
return names.indexOf(name);
};
})(this.weekDay = {});
For larger modules we can use another pattern
var counterModule = (function() {
let counter = 0;
const increment = () => ++counter;
const decrement = () => --counter;
return {
increment,
decrement
}
})()
Modules can have it's internal state and manage it without conflicting with the global state
define('myModule',
['math', 'graph'],
function ( math, graph ) {
return {
plot: function(x, y){
return graph.drawPie(math.randomGrid(x,y));
}
}
};
});
Defined for the browser, with an asynchronous behaviour in mind
function doSomething() {
// Do
}
function doAnother() {
// Do
}
module.exports = {
doSomething: doSomething,
doAnother: doAnother
}
var myModule = require('./myModule')
myModule.doSomething();
myModule.doAnother();
export const list = [1, 2, 3]
export const doSomething = (cb) => list.map(cb)
export function doAnother (cb) {
return list.filter(cb)
}
export default function() {
console.log('a')
}
With ES6 JavaScript for the first time has a built-in module system.
Each file is a module
import * as myModule from './mymodule'
import { doSomething, doAnother } from './mymodule'
import mything from './anothermodule'
Importing modules
https://github.com/rafinskipg/test-setup-webpack
$> mkdir usersManagementApplication
$> cd usersManagementApplication
$> npm init
// create main.js
// launch with node main.js
Use CommonJS for native node JS support.
npm install --save-dev babel-{core,loader,preset-es2015-native-modules} webpack
https://github.com/rafinskipg/test-setup-webpack
An object is a "box" that provides an interface for abstracting logic and hiding complexity.
var beers = {
amount: 50,
drinkBeer: function() { --this.amount },
buyBeer: function() { ++this.amount }
}
Methods are properties that hold a function value
function collide(wall) {
console.log(`the person ${this.name}
collided with the ${wall}`)
}
const personOne = {
name : 'petorski',
collide: collide
}
const personTwo = {
name : 'markantu',
collide: collide
}
personOne.collide('dirty ass')
personTwo.collide('green wall of doom')
var a = {}
a.toString
The prototypal inheritance forms a tree shaped structure.
Since everything "inherits" from Object.prototype
Functions derive from Function.prototype
Arrays from Array.prototype
Normal objects from Object.prototype
As we have seen we can create new instances of objects with function constructors
function Engine() {
this.started = false
}
function Car(model) {
this.model = model
this.engine = new Engine()
}
var tesla = new Car('tesla')
we can define the prototype of the object constructors
Car.prototype.startEngine = function() {
this.engine.started = true
}
Car.prototype.stopEngine = function() {
this.engine.started = false
}
var tesla = new Car('tesla')
the prototype property will be the prototype of instances created through it but is not its own prototype.
Object.getPrototypeOf(tesla)
{startEngine: ƒ, stopEngine: ƒ, constructor: ƒ}
Object.getPrototypeOf(Car)
ƒ () { [native code] }
Constructors owns prototype is the Function one
Changing a constructors prototype is going to change all the instances at the same time
// tesla
Car.prototype.color = 'red'
tesla.color // red
Prototypes are objects, they are shared by all the instances by reference.
Be carefull
As we said, the prototype forms a tree of "inheritance", and for knowing which properties has an object, JavaScript does this in this way:
1 - It checks the object methods
2 - It checks it's prototype methods
3 - It checks it's prototype's prototype methods
... repeat
For performance, avoid long prototype chains
Mutating Object.prototype can give us some good tools
Object.prototype.jotica = function() {
console.log('La virgen del pilar diceeeeee')
}
var a = {}
a.jotica()
Create a method that returns the first element of the array
Create a method that returns the last element of the array
Access to prototype is fast, and often objects are predictable. In that case the JIT compiler stores the instructions to reuse them.
But when we mutate a prototype, all the objects that used that prototype have to be checked again by the JIT compiler.
Also... all the code the object flows through has to be re-checked, because the optimization relies in the form of the prototypes
Clone
https://github.com/rafinskipg/test-object-prototype