Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Errors and Iterables

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 error object and iterables.

Error Objects

JavaScript throw Error objects when an error is encountered.

They include various constructors.

They include EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError.

All of these constructors inherit from Error .

We can put code that may throw errors within the try block.

And we can catch the error in the catch block.

For instance, we can write:

try {
  foo();
} catch (e) {
  //...
}

If foo throws an error, then the catch block will catch the error.

e has the error object thrown.

We can get the name with e.name and the message with e.message .

We can add a finally clause to run code regardless of whether an error is thrown.

For instance, we can write:

try {
  foo();
} catch (e) {
  //...
} finally {
  console.log('finally');
}

ES6 Iterators and Generators

Iterators and generators are new constructs with ES6.

They let us create various types of iterable objects and use them.

For…of Loop

The for…of loop lets us iterate through any kind of iterable objects.

For instance, we can loop through an array with it by writing:

const iter = [1, 2];
for (const i of iter) {
  console.log(i);
}

Then we get:

1
2

We used const in the loop heading so that we can’t reassign the loop variable i to something else.

We can do the same thing for other iterable objects like strings:

for (const i of 'foo') {
  console.log(i);
}

then we get the individual characters as the values of i .

The for…of loop is used for iteration.

Iterators and Iterables

Iterators are objects that expose the next to get the next entry of a collection.

We can create an iterator with a generator function.

For instance, we can write:

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

yield returns the next item we want to return in the iterator by calling next.

Once we called next , then the generator function will be paused.

We can use it by writing:

const gen = genFunc();
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}

We get each item that’s yielded with each next call.

The value has the yielded value, and done tells us whether the generator is done generating values.

An iterable is an object that defines its iteration behavior.

They can used with the for…of loops for iteration.

Built-in iterables include arrays and strings.

All iterable objects have the @@itrerator method, which has the property with Symbol.iterator as the key.

It must return an iterator with the next method.

So the simplest way is to use generators so that we can call next and return the values.

We can create an iterable by writing:

const obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
}

Then we can use the for…of loop by writing:

for (const i of obj) {
  console.log(i);
}

Then we get:

1
2
3

logged.

Conclusion

ES6 iterable objects like arrays and strings use iterators to return the values that it has.

They can also be iterated through with a for…of loop.

Error objects can be thrown by any code and we can catch them with try-catch-finally.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Doing Things with Functions

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 functions.

Running a Function

We can run a function by adding parentheses after its name.

It works regardless of how a function is defined.

For instance, if we have a function:

const sum = function(a, b) {
  return a + b;
};

Then we run it by writing:

sum(1, 2);

We pass in the numeric arguments sum expects.

Anonymous Functions

Anonymous functions are functions that have no name.

The sum function we had before had a function that had no name assigned to a variable.

This way, we can call it anywhere.

Callback Functions

Callback functions are functions that are called by other functions.

For instance, we can write:

function runAdd(a, b) {
  return a() + b();
}

The runAdd function calls the a and b functions.

Therefore, we have to pass 2 functions to it.

We can use it by writing:

const sum = runAdd(() => 1, () => 2)

We passed in a function that returns 1 and one that returns 2 as arguments.

Then we get 3 returned.

So sum is 3.

Callbacks are useful when we run async code.

We can call callback when the async code has its results.

We can also have synchronous callbacks to do repeated operations like with array methods like map , filter and reduce .

We can make our own map method by writing:

function map(arr, callback) {
  const result = []
  for (const a of arr) {
    result.push(callback(a));
  }
  return result;
}

Then we can use it by writing:

const result = map([1, 2, 3], (x) => x * 2);

Then result would be [2, 4, 6] .

map takes an array arr , loops through each entry with the for-of loop, and call callback on each entry with each entry.

Immediate Functions

Immediate functions are functions that are called immediately after they’re defined.

For instance, we can write:

(
  function() {
    console.log('foo');
  }
)();

Then we created a function and called it immediately.

We should surround it with parentheses so we know we’re calling it.

We can also write:

(function() {
  console.log('foo');
})();

And we can get the returned value and write:

const result = (function() {
  //...
  return {
    //...
  }
}());

We return some result in the function and assigned that to result .

Inner (Private) Functions

Functions are like any other variable, so we can put them in another function.

For instance, we can write:

function outer(param) {
  function inner(a) {
    return a * 100;
  }
  return inner(param);
}

We have a inner function that returns its parameter multiplied by 100.

Then we return the result.

The benefit of having inner functions is that the variables inside the function can’t be accessed from the outside.

But the inner function can access variables from the outer function.

This is a big benefit since we don’t have to define variables at the global scope.

Conclusion

There’re many benefits to defining private functions.

We can also define immediately invoked functions to return something.

Functions can be anonymous and can be parameters of another function.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Date

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 Date object.

Date

The Date constructor lets us create date objects.

We can create a new Date instance by passing in nothing, a date string, values for day, month, time, etc., or a timestamp.

For instance, to create the date with the current date, we can write:

new Date();

Also, we can pass in a date string:

new Date('2020 1 1');

and we get:

Wed Jan 01 2020 00:00:00 GMT-0800 (Pacific Standard Time)

or:

new Date('1 1 2020');

and we get:

Wed Jan 01 2020 00:00:00 GMT-0800 (Pacific Standard Time)

Or:

new Date('1 jan 2020');

and we get:

Wed Jan 01 2020 00:00:00 GMT-0800 (Pacific Standard Time)

The Date constructor can figure out the date from different strings.

We can also pass in numeric values to the the Date constructor to represent the:

  • year
  • month, with 0 representing January to 11 for December
  • day — 1 to 31
  • hour — 0 to 23
  • minutes — 0 to 59
  • seconds — 0 to 59
  • milliseconds — 0 to 999

For example, we can write:

new Date(2020, 0, 1, 17, 05, 03, 120);

And we get:

Wed Jan 01 2020 17:05:03 GMT-0800 (Pacific Standard Time)

If we pass in a number that’s out of range, it’ll be adjusted to be in range.

For instance, if we have:

new Date(2020, 11, 32, 17, 05, 03, 120);

Then we get:

Fri Jan 01 2021 17:05:03 GMT-0800 (Pacific Standard Time)

We can also pass in a timestamp.

For instance, we can write:

new Date(1557027200000);

And we get:

Sat May 04 2019 20:33:20 GMT-0700 (Pacific Daylight Time)

If we called Date without new , then we get a string representing the current date.

It doesn’t matter whether we pass in the parameters.

So if we have:

Date();

We get:

"Mon Aug 03 2020 15:02:32 GMT-0700 (Pacific Daylight Time)"

Date Methods

We can adjust the date with some instance methods.

For instance, we can use getMonth(), setMonth(), getHours(), setHours(), etc. to set the parts of a date.

To return a string, we call toString :

const d = new Date(2020, 1, 1);  
d.toString();

Then we get:

"Sat Feb 01 2020 00:00:00 GMT-0800 (Pacific Standard Time)"

To set the month, we call setMonth :

d.setMonth(2)

This returns the new timestamp:

1583049600000

To get a human-readable date, we can call toString :

d.toString();

and get:

"Sun Mar 01 2020 00:00:00 GMT-0800 (Pacific Standard Time)"

We can get the timestamp of a date with Date.UTC :

Date.UTC(2029, 0, 1);

And we can pass that into the Date constructor:

new Date(Date.UTC(2029, 0, 1))

The now method also returns the current timestamp, so we write:

Date.now();

and we get:

1596492422408

We can do the same with valueOf or the + operator:

new Date().valueOf();  
+new Date();

and they both return the current timestamp.

Conclusion

Dates can be manipulated with the Date constructor with JavaScript.

We can convert between timestamps, date objects, and string.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Constructors and Shorthands

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 constructors and object shorthands.

The constructor Property

The constructor property is part of an instance object.

It contains the reference to the constructor function.

So if we have:

function Person(name, occupation) {
  this.name = name;
  this.occupation = occupation;
  this.whoAreYou = function() {
    return `${this.name} ${this.occupation}`
  };
}

const jane = new Person('jane', 'writer');

Then:

jane.constructor

is the Person constructor function.

The instanceof Operator

The instanceof operator lets us check whether the object is created with the given constructor.

For instance, we can write:

jane instanceof Person

then we get true .

And if we write:

jane instanceof Object

and that’s also true .

Functions that Return Objects

A function that returns an object is called a factory function.

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

function factory(name) {
  return {
    name
  };
}

Then we can call it by writing:

const james = factory('james');

Then we get james.names is 'james' .

Constructors can also be written to return an object instead of return an instance of this .

For instance, we can write:

function C() {
  this.a = 1;
  return {
    b: 2
  };
}

Then if we invoke the construction:

const c = new C()

and then we get:

{b: 2}

Passing Objects

We can pass objects into a function.

For instance, we can write:

const reset = function(o) {
  o.count = 0;
};

Then we can use it by writing:

const obj = {
  count: 100
}
reset(obj);
console.log(obj);

And we get:

{count: 0}

from the console log.

Comparing Objects

We can’t compare objects with === since it only returns true if they have the same reference.

For instance, if we have:

const james = {
  breed: 'cat'
};
const mary = {
  breed: 'cat'
};

Then:

james === mary

returns false even if they have exactly the same properties.

The only way that it can return true is if we assign one to object to the other.

For instance, we write:

const james = {
  breed: 'cat'
};
const mary = james;

Then:

james === mary

returns true .

ES6 Object Literals

ES6 gives us a much shorter syntax for defining object literals.

For instance, if we have:

let foo = 1
let bar = 2
let obj = {
  foo: foo,
  bar: bar
}

Then we can shorten that to:

let foo = 1
let bar = 2
let obj = {
  foo,
  bar
}

We can also shorten methods.

For instance, instead of writing:

const obj = {
  prop: 1,
  modifier: function() {
    console.log(this.prop);
  }
}

We write:

const obj = {
  prop: 1,
  modifier() {
    console.log(this.prop);
  }
}

We can also use computed property keys.

For instance, we can write:

let vehicle = "car";
let car = {
  [`${vehicle}_model`]: "toyota"
}

We can do the same for methods.

Conclusion

Object literals can be written in various ways JavaScript.

We can check the constructor with the constructor property and instanceof .

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Closures

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 functions.

Functions that Return Functions

Fnctions can return functions.

For instance, we can write:

function a() {
  console.log('foo');
  return function() {
    console.log('bar');
  };
}

We return a function and then we can call the function we return.

For instance, we can write:

a()();

Then first call a , so 'foo' is logged.

Then we call the returned function with the second parentheses, so we get 'bar' logged.

Functions that Rewrites Itself

We should be aware that functions can rewrite themselves.

For instance, we can write:

function a() {
  console.log('foo');
  a = function() {
    console.log('bar');
  };
}

Then we get 'foo' logged.

The first console log runs, then the function is reassigned.

We shouldn’t do that and linters will alert us if we do this.

Closures

Closures are functions that have other functions inside it.

The inner functions can access the outer function’s scope.

For instance, if we have:

function outer() {
  let outerLocal = 2;

  function inner() {
    let innerLocal = 3;
    return outerLocal + innerLocal;
  }
  return inner();
}

We have the inner function with the innerLocal variable.

This is only available within the inner function.

It can also access the outer function’s outerLocal variable.

This is useful because we want to have private variables in our code.

We have outerLocal and innerLocal that are only available inner .

outer can’t access the variables of inner .

So we have different levels of privacy with these functions.

Closures in a Loop

If we have a loop that looks like:

function foo() {
  var arr = [],
    i;
  for (i = 0; i < 3; i++) {
    arr[i] = () => i
  }
  return arr;
}

Then if we call it:

const arr = foo();
for (const a of arr){
 console.log(a());
}

then we get 3 from all the console logs.

This is because i is 3 when we assigned the added our function to arr .

var isn’t block-scoped, so we would get the last value of i instead of what’s in the loop.

To get the value of i from the loop heading to the function we assign, we write:

function foo() {
  var arr = [],
    i;
  for (i = 0; i < 3; i++) {
    ((j) => {
      arr[i] = () => j
    })(i)
  }
  return arr;
}

Then we get 0, 1, and 2 as we expect.

We can also replace var with let to make i block-scoped and avoid this issue.

Getter and Setter

Getter functions return values and setters sets values.

We can put the getter and setter functions into a closure so that we can keep them private.

For instance, we can write:

let getValue, setValue;
(() => {
  let secretVal = 0;
  getValue = () => {
    return secretVal;
  };

  setValue = (v) => {
    if (typeof v === "number") {
      secretVal = v;
    }
  };
}());

And we run a function to assign the getValue and setValue functions with functions.

getValue returns the value of secretVal and setValue sets it.

This way, we keep secretVal secret.

Conclusion

Closures are great for various applications.

It’s mostly to keep variables private, and we can also use them to bind variables values the way we expect.