Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Generators and Maps

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at generators and maps.

Generators

Generator functions let us create generator objects.

They run line by line, returns the result with yield , and then paused until it’s invoked again.

We can use them to write infinite loops since they’ll pause when they aren’t needed.

For instance, we can create a generator function by writing:

function* generatorFunc() {
  yield 1;
  yield 2;
  yield 3;
}

We have the generatorFunc generator function.

It’s indicated by the function* keyword.

The yield keyword lets us return the value and the pause the value.

Then we can use it by creating a generator with it and then call next :

const gen = generatorFunc();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

Then we get:

{value: 1, done: false}
{value: 2, done: false}
{value: 3, done: false}
{value: undefined, done: true}

next returns an object with the value and done properties.

value has value that’s returned and done indicates whether there’s any other value to return.

done is true indicates there’s no other value to return.

If we use yield without an argument, then we can pass a value into next and get the value.

For instance, we can write:

function* generatorFunc() {
  console.log(yield);
  console.log(yield);
  console.log(yield);
}

const gen = generatorFunc();
console.log(gen.next());
console.log(gen.next('foo'));
console.log(gen.next('bar'));
console.log(gen.next('baz'));

We console log yield in the generatorFunc then when we call next with an argument, it’ll be logged.

The first one should be called with nothing since it’ll just start the generator function instead of running the body.

It’ll return:

{"done": false,"value": undefined}

Generator objects conform to the iterator contract.

So we can get the Symbol.iterator method with it.

If we have:

console.log(gen[Symbol.iterator]() === gen);

we log true .

Iterating Over Generators

Generators are iterators.

Anything that supports iterables can be used to iterate over generators.

For instance, we can use the for-of loop:

function* genFunc() {
  yield 1;
  yield 2;
}

for (const i of genFunc()) {
  console.log(i)
}

Then we get:

1
2

We can also use the destructuring syntax with generators.

For instance, we can write:

function* genFunc() {
  yield 1;
  yield 2;
}

const [x, y] = genFunc();

Then x is 1 and y is 2.

Collections

JavaScript provides us with various kinds of collections like Map , WeakMap , Set , and WeakSet .

Now we don’t have to create hacks to create these data structures.

Map

Map allows us to store key-value pairs.

They let us have fast access to values.

For instance, we can write:

const m = new Map();
m.set('first', 1);

Then we can get it by writing:

console.log(m.get('first'));

and get the value by the key.

We can remove the entry with the given keys with the delete method:

m.delete('first');

And we can set the size with the size property:

console.log(m.size);

We can also create a map with an array of key-value arrays:

const m = new Map([
  ['one', 1],
  ['two', 2],
  ['three', 3],
]);

Conclusion

We can use generators to create iterables.

Also, we can use maps to store key-value pairs.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Class Inheritance

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at JavaScript subclasses, mixins, and multiple inheritance.

Subclassing

We can create subclasses from a JavaScript class.

For instance, we can write:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} speaks`);
  }
}

class Dog extends Animal {
  speak() {
    console.log(`${this.name} woofs`);
  }
}

const mary = new Dog('mary');

to create the Animal class.

The speak method is in the Animal class.

We created a subclass of Animal called Dog which also has the speak method.

If we create a Dog instance, then the speak method in Dog will be used.

So if we write:

mary.speak();

We see:

mary woofs

There are several ways to access the superclass of a class.

We can call super() to call the superclass and pass in the arguments of the parent constructor.

Also, we can call super.<parentClassMethod> to call a parent class’s method.

super.<parentClassProp> lets us access the parent class properties.

So if we have:

class Parent {}

class Child extends Parent {
  constructor(name) {
    this.name = name;
  }
}

const child = new Child('james')

We’ll get the error:

Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

this is because we need to call super first to invoke the parent constructor before we can set the properties of this .

This can be done implicitly if we omit the constructor in Child .

If we have:

class Parent {}

class Child extends Parent {}

const child = new Child()

Then we won’t get an error since a default constructor is provided.

The default is:

constructor(...args) {
  super(...args);
}

Mixins

JavaScript only supports single inheritance, so we can’t use the standard JavaScript syntax to inherit properties from multiple classes.

Instead, we have to compose multiple classes into one with functions.

We can write:

class Person {}

const BackgroundCheck = Tools => class extends Tools {
  check() {}
};

const Onboard = Tools => class extends Tools {
  createEmail() {}
};

class Employee extends BackgroundCheck(Onboard(Person)) {}

We have the BackgroundCheck class that returns a subclass of the Tools class.

So Person is Tools in the Onboard function,.

And Onboard(Person) is Tools in the BackgroundCheck function.

This way, we return a class that has both the check and createEmail methods and we can use the resulting class to create the Employee subclass.

Conclusion

We can create subclasses with the extends keyword.

Also, we can create functions that returns a class and compose them so that we can inherit from multiple classes.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Class Basics

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at the basics of classes.

Classes

ES6 brings many useful features that helps us create objects more easily.

If we’re familiar with class-based languages like Java, then JavaScript classes should look familiar.

But JavaScript classes are syntactic sugar over its prototypical inheritance model.

Before ES6, we can only create constructors.

function Person(name) {
  if (!(this instanceof Person)) {
    throw new Error("Person is a constructor");
  }
  this.name = name;
};

Person.prototype.eat = function() {
  //...
};

We have the Person constrictor with the this.name instance property.

And Person.prototype.eat is an instance method.

Then we can create a child constructor that inherits from the Person constructor by writing:

function Employee(name, job) {
  if (!(this instanceof Employee)) {
    throw new Error("Employee is a constructor");
  }
  Person.call(this, name);
  this.job = job;
};

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.work = function() {
  // ...
};

We create the Employee constructor that calls the Person constructor with the name argument.

Then we set this.job to job .

And then we create the prototype of Employee with Object.create .

We inherit the properties of the Person.prototype since we used the Object.create method with Person.prototype as the argument.

Then we can create our methods and set the constructor to Employee so using instanceof on an Employee instance would return true .

With the class syntax, we can make our lives much easier:

class Person {
  constructor(name) {
    this.name = name;
  }

  eat() {
    //...
  }
}

class Employee extends Person {
  constructor(name, job) {
    super(name);
    this.job = job;
  }

  work() {
    //...
  }
}

We create the Person class.

The constructor method has what was in the constructor function itself.

The prototype methods are in the class.

To create a child class, we use the extends keyword and then call super to call the Person constructor.

Then we have the work instance method.

The eat method is inherited from the Person instance also.

Defining Classes

We can define classes with the class keyword.

It’s very similar to class-based languages like Java or C#.

For instance, we can write:

class Car {
  constructor(model, year) {
    this.model = model;
    this.year = year;
  }
}

The Car class is still a function.

So if we log:

console.log(typeof Car);

we see 'function' .

Classes are different from normal functions.

They aren’t hoisted.

Normal functions can be declared anywhere in the scope and it’ll be available.

But classes can only be accessed if they’re defined.

So we can’t write:

const toyota = new Car();
class Car {}

because we’ll get the reference error:

Uncaught ReferenceError: Cannot access 'Car' before initialization

We can’t use commas while separating the members of a class, but we can add semicolons.

So we can’t write:

class C {
  member,
  method() {}
}

we’ll get a syntax error.

But we can write:

class C {
  method2() {};
}

Conclusion

The class syntax makes creating constructors easier.

We can do inheritance with extends and super .

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Async Programming

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at JavaScript async programming.

Async Programming

Async programming is an import part of JavaScript apps.

It’s different from the alternative mode, which is synchronous programming.

Synchronous programming is where we run code line by line.

And async programming is where we run some code, then wait for the result.

While we wait for the result, we let other pieces of code in the queue run.

Then once the result is obtained, then we run the code that’s waiting for the result.

This is different in that we aren’t running anything line by line.

We queued up the code and initialize them.

Then once the result is computed, we go back to run the code that’s initialized.

When the code is waiting, the latency isn’t predictable.

So we can’t just run code and then wait for the result without letting other pieces of code run.

JavaScript Call Stack

The call stack is a record of the functions that are called from latest to earliest.

For instance, if we have:

function c(val) {
  console.log(new Error().stack);
}

function b(val) {
  c(val);
}

function a(val) {
  b(val);
}
a(1);

Then we get:

Error
    at c ((index):37)
    at b ((index):41)
    at a ((index):45)
    at (index):47

from the console log.

We see that a is called first, then b , then c .

When c returns, the top of the stack is popped out.

And then b is popped out when it returns.

And finally a is popped.

Event Loop

A browser tab runs in a single thread.

It runs in the event loop.

The loop continuously picks messages from the message queue and runs callbacks associated with them.

The event loop keeps picking tasks from the message queues when other processes add tasks to the message queue.

Processes like timers and event handlers run in parallel and add tasks to the queue.

Timers

The setTimeout function creates a timer and wait until it fires.

When the timer is run, a task is added to the message queue.

It takes a callback and the duration in milliseconds.

Once the duration is reached, then the callback is run.

When the callback is run, then other interactions in the browser is blocked.

Callbacks

Node popularized its own style of callback.

The callback takes an err and data parameter.

err has the error object.

data has the result data.

For instance, we have:

fs.readFile('/foo.txt', (err, data) => {
  if (err) throw err;
  console.log(data);
});

which has a callback with err and data .

The fs.readFile reads a file asynchronously and gets the content.

Conclusion

JavaScript needs lots of async code because it only runs on one thread.

It needs to queue tasks and then run asynchronously.

This way, it won’t hold up the main thread.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Variables Best Practices

JavaScript is partly an object-oriented language.

To learn JavaScript, we got to learn the object-oriented parts of JavaScript.

In this article, we’ll look at the scope of variables.

Scope of Variables

JavaScript has block-scoped variables since ES6.

As long as we declare variables with let or const , they’ll be block-scoped.

We can write:

function foo() {
  let local = 2;
}

to create a block-scoped variable local .

This is only available within the block.

We can also cvrearte global variables.

For instance, we can write:

var x = 1;

at the top level to create a global variable.

It’s hoisted, which means that the declaration of it can be accessed from anywhere within the script.

But the value is only available after it’s assigned.

var used at the top level will be global.

We should avoid creating global variables to avoid naming collisions.

Block-scoped variables are easier to track since they’re only available within the block.

Variable Hoisting

Variable hoisting is done only with var variables.

For instance, we can write:

var a = 123;

Then if we use it as follows:

var a = 123;

function foo() {
  console.log(a);
  var a = 1;
  console.log(a);
}
foo();

then we get that the first log of a is undefined .

And the 2nd console log is 1.

This is because the console log takes the a from within the function.

var is function scoped, it the value will be taken from a function.

Therefore, we get undefined and 1 respectively.

Block Scope

Because function scoping is confusing, ES6 introduced block-scoped variables.

We can use let and const to declare variables.

They aren’t hoisted and const variables have to have an initial value assigned to it when it’s declared.

For instance, we can write:

var a = 2; {
  let a = 3;
  console.log(a);
}
console.log(a);

The a in the console log would be 3.

And the console log outside the block would log 2 from the var declaration.

Because it’s easier to reason with block-scoped variables, we should use them everywhere.

The rule for creating variables is that we consider const first we can’t assign them to a new value.

If we need to assign a new value to a variable, then we use let .

And we never use var .

Functions are Data

Functions can be assigned to variables.

For instance, we can write:

const foo = function() {
  return 1;
};

Then we assigned a function to the foo variable.

This is called a function expression.

And if we write:

const foo = function bar() {
  return 1;
};

then we assigned a function declaration to a variable.

They’re the same, except that we can access the original name within the function.

So we can get the name bar from within the bar function’s body.

But we call it with foo outside it.

Using the typeof operator, we return the type 'function' as the value.

If we have:

typeof foo

We get 'function' .

Conclusion

We should use let or const to declare variables.

Also, functions can be assigned to variables.