Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Built-in Browser Objects

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 built-in browser objects.

The window.location Property

The window.location property lets us get the URL of the page and redirect it to another one.

For instance, we can use location.hostname to get the hostname.

And href gets us the full path.

pathname gets us the segment before the query string.

port gives us the port.

search gives us a query string.

We can get all the properties of the location object with the loop:

for (const key in location) {
  if (typeof location[key] === "string") {
    console.log(key, location[key]);
  }
}

We loop through each property with the location object.

We set the location.href property to redirect to a new URL.

For example, we can write:

window.location.href = 'http://www.example.com';

Also, we can write:

location.href = 'http://www.example.com';
location = 'http://www.example.com';
location.assign('http://www.example.com');

replace is almost the same as assign , but it doesn’t create a new browser history entry.

We can use it by writing:

location.replace('http://www.example.com');

To reload a page, we can write:

location.reload();

We can also assign window.location.hre to itself to reload the page:

window.location.href = window.location.href;
location = location;

The window.history Property

The window.history property lets us access the previously visited pages of the same browser session.

For instance, we can see the number of pages visited before visiting the current page with window.history.length .

We can’t see the actual URLs to maintain privacy.

But we can navigate back and forth through the user’s session.

We can use:

history.forward();
history.back();

to move forward and back in the history respectively.

history.back() is also the same as history.go(-1); .

To go 2 pages back, we can write:

history.go(-2);

And we can reload the current page with:

history.go(0);

The HTML5 history API also lets us change the URL without reloading the page.

We can use the history.pushState method to change the page.

For instance, we can write:

history.pushState({foo: 1}, "", "hello");

The first argument is the value of the stte property.

The 2nd is the title, which isn’t used.

And the 3rd is the URL path to go to.

Then we can get the state with history.state .

The window.frames Property

The window.frames property is a collection of all frames in the current page.

It doesn’t distinguish between frames and iframes.

window.frames points to window no matter frames are present on the page or not.

So:

window.frames === window;

returns true .

If we have a frame like:

<iframe name="helloFrame" src="hello.html" />

Then frames.length is 1.

We can get the first frame, which is window , with:

window.frames[0];
window.frames[0].window;
window.frames[0].window.frames;
frames[0].window;
frames[0];

We can reload the frame with:

frames[0].window.location.reload();

And the frame’s parent is window , so:

frames[0].parent === window;

returns true .

We have the top property to get the topmost page, which is the page all the other frames from within the frame.

So all of these:

window.frames[0].window.top === window;
window.frames[0].window.top === window.top;
window.frames[0].window.top === top;

return true .

self is the same as window , so:

self === window

returns true .

Also, these also return true :

frames[0].self === frames[0].window;
window.frames['helloFrame'] === window.frames[0];
frames.helloFrame === window.frames[0];

Conclusion

The window.location property and window.frames lets us get and set the URL of our app and get the frames respectively.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Prototypes

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

Prototype

Every object has a prototype.

Objects inherit from objects call prototypes.

The Prototype Property

The prototype is located in the prototpe property for constructor functions.

Constructor functions have the prototype property that has the methods that returned with the constructor instance.

For instance, if we have a function:

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

Then we can get its prototype by writing:

console.log(foo.prototype)

It has the constructor property, which is the function itself.

This property is always available as long as we don’t specify otherwise.

Adding Prototype Methods and Properties

We can add properties and methods to the prototype, then they’ll be shared with all instances of the constructor.

For instance, we can write:

function Person(name) {
  this.name = name;
  this.whoAreYou = function() {
    return `I am ${this.name}`;
  };
}

Then we have the the name and whoAreYou instance properties.

This will create different copies of the properties for each instance.

But we can share whoAreYou between different instances since it’s the same method.

We can do that by putting it in the prototype property:

function Person(name) {
  this.name = name;
}

Person.prototype.whoAreYou = function() {
  return `I am ${this.name}`;
};

this.name should be unique between different instances, so it should be in the constructor.

But whoAreYou can be shared, so we add it as a property of the prototype .

Using the Prototype’s Methods and Properties

We can use the prototype’s methods and properties by instantiating the instance of the constructor.

For instance, we can write:

const jane = new Person('jane');

Then we can call whoAreYou by writing:

console.log(jane.whoAreYou())

Then we get:

'I am jane'

Own Properties vs Prototype Properties

Own properties are properties that are defined within the object itself rather than inherited.

Prototype properties are properties that are inherited from another object.

In the Person constructor, we have the prototype.whoAreYou method, which means it’s inherited from the prototype of the Person constructor.

When we get a property or call a method, the JavaScript engine looks through all the properties of the objects in the prototype chain to get the property.

We can’t tell the difference just by looking at the invocation.

So jane.name looks like jane.whoAreYou() but the first is an own property and the 2nd is a prototype property.

We can check by using the constructor property.

For instance, we can write:

console.log(jane.constructor.prototype);

We see the whoAreYou method in the logged object.

Overwriting a Prototype’s Property with an Own Property

We can overwrite a prototype’s property with an own property.

For instance, we can write:

function Person(name) {
  this.name = name;
}

Person.prototype.name = 'no name';

Then if we create a Person instance:

const jane = new Person('jane');

We see that jane is:

{name: "jane"}

We can determine if a property is an own property with the hasOwnProperty method.

For instance, if we write:

console.log(jane.hasOwnProperty('name'));

We get true .

We can delete a property with the delete operator.

For instance, we can write:

delete jane.name

to remove it.

Conclusion

An object’s prototype is what an object inherits from.

We can override its properties.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Prototype Catches

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

Enumerating Properties

We can enumerate properties of an object with the for…in loop.

For example, if we have:

const obj = {
  a: 1,
  b: 2
};

We can write:

const obj = {
  a: 1,
  b: 2
};

for (const key in obj) {
  console.log(key, obj[key]);
}

Then we get:

a 1
b 2

The key has the key and obj[key] has the value.

Not all properties are enumerable, we can set the ebumerable property descriptor to make a property not enumerable.

We can check with the propertyIsEnumerable() method to check if a property is enumerable.

We can do the check with:

console.log(obj.propertyIsEnumerable('a'))

then we should get true since object properties are enumerable unless it’s specified otherwise.

Using isPrototypeOf() Method

Objects also has the isPrototypeOf method that tells whether the specific object is used as a prototype of another object.

For instance, if we have:

const monkey = {
  hair: true,
  feeds: 'bananas',
  breathes: 'air'
};

function Person(name) {
  this.name = name;
}

Person.prototype = monkey;

const james = new Person('james');
console.log(monkey.isPrototypeOf(jane));

Then the console log should log true since we set monkey to the prototype of the Person constructor.

proto Property

Since ES6, the __proto__ property is a standard property to let us set the prototype of an object.

It can also be used as a getter to get the property.

For instance, we can write:

const monkey = {
  hair: true,
  feeds: 'bananas',
  breathes: 'air'
};

function Person(name) {
  this.name = name;
}

Person.prototype = monkey;

const james = new Person('james');
console.log(james.__proto__ === monkey);

Then we get true since we have the prototype of the james object is monkey as we set with:

Person.prototype = monkey;

We can get the same result with:

james.constructor.prototype

We can also set the prototype of an object with it, so we can write:

const monkey = {
  hair: true,
  feeds: 'bananas',
  breathes: 'air'
};

const person = {
  name: 'bob',
  __proto__: monkey
}

Then we’ll get the prototype with the __proto__ property or we can use Object.getPrototypeOf() to check:

Object.getPrototypeOf(person)

then we get the monkey object.

Augmenting Built-in Objects

We can add functionality to built-in objects.

We can use polyfills to add standard functionalities to objects that aren’t available in some environments.

Issues with Prototype

We should know that the prototype chain is live.

It’ll change when we change the proottype object.

The prototype.constructur property isn’t available.

We can set the constructor top whatever we want.

If we change the prototype, then we have to change the constructor.

For instance, we can write:

function Dog() {}

Dog.prototype = {
  paws: 4,
  hair: true
};

const dog = new Dog();
console.log(dog.constructor === Dog);

then we get false .

We’ve to set the constructor to the Dog to make it true so it reports correctly:

Dog.prototype.constructor = Dog;

Now:

console.log(dog.constructor === Dog);

logs true like we expect.

Conclusion

We’ve to be careful when working with prototypes.

We can set it and we may sometimes get results we may not expect.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Promises

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

Promises

A promise is an alternative to callbacks.

Promises let us retrieve the results of an async function calls.

Promises are easier than callbacks and gives us more readable code.

They can take on a few states.

A promise can be pending, which means the result isn’t ready yet.

This is the initial state.

A promise is fulfilled when the result is ready.

If there’s an error, then the result is rejected.

When a pending promise is fulfilled or rejected, associated callbacks that are queued up by then are run.

This is better than async callbacks.

If we have many async functions we want to run, then we’ll have to nest them repeatedly.

For instance, we write:

asyncFunction(arg, result => {
  asyncFunction(arg, result => {
    //...
  })
})

to run an async function more than once.

If we have to do that more, then there’s more nesting and it’ll be harder to read.

If asyncFunction is a promise, then we can just call then with a callback.

And the callback run with the promise is fulfilled:

asyncFunction(arg)
  .then(result => {
    //...
  });

We just keep calling then until we called all the promises we want:

asyncFunction(arg)
  .then(result => {
    //...
    return asyncFunction(argB);
  })
  .then(result => {
    //...
  })

The then callback returns a promise, so we can keep calling then until we’re done.

To catch promise errors, we can call the catch method with a callback.

For instance, we can write:

readFileWithPromises('text.json')
  .then(text => {
    //...
  })
  .catch(error => {
    console.error(error)
  })

Creating Promises

We can create promises with the Promise constructor.

For instance, we can write:

const p = new Promise(
  (resolve, reject) => {
    if (...) {
      resolve(value);
    } else {
      reject(reason);
    }
  });

We passed in a callback that has the resolve and reject parameters, which are all functions.

resolve fulfilled the promise with a given value.

reject rejects a promise with a value.

We can use than with then and catch as we did with the previous examples:

p
  .then(result => {
    //...
  })
  .catch(error => {
    //...
  })

When we throw an error in the then callback, it’ll also be caught with catch if it’s called after the then that throws the error:

readFilePromise('file.txt')
  .then(() => {
    throw new Error()
  })
  .catch(error => {
    'Something went wrong'
  });

Promise.all()

Promise.all lets us run an array of promises in parallel and returns a promise that resolves to an array of all the promises in the array.

If all of them are fulfilled, then an array of the results of the promises will be returned in then then callback’s parameter.

For instance, we can write:

Promise.all([
    p1(),
    p2()
  ])
  .then(([r1, r2]) => {
    //
  })
  .catch(err => {
    console.log(err)
    ''
  })

We call Promise with p1 and p2 which return promises.

Then r1 and r2 are the results.

catch catches error from any of the promises if any of them are rejected.

Conclusion

We can create promises with the Promise constructor.

Promise.all run multipole promises in parallel and returns a promise that resolves to an array of all the results,.

Categories
Object-Oriented JavaScript

Object-Oriented JavaScript — Parts of a Class

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 parts of a JavaScript class.

Constructor

The constructor is a special method that’s used to create and initialize the object we create with the class.

The class constructor can call its parent class constructor with the super method.

super is used for creating subclasses.

Prototype Methods

Prototype methods are prototype properties of a class.

They’re inherited by instances of a class.

For instance, we can write:

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

  get modelName() {
    return this.model
  }

  calcValue() {
    return "2000"
  }
}

We have 2 prototype methods within our Car class.

One is the modelName getter method.

And the other is the calValue method.

We can use them once we instantiate the class:

const corolla = new Car('Corolla', '2020');
console.log(corolla.modelName);
console.log(corolla.calcValue());

We created the Car instance.

Then we get the getter as a property.

And we called calcValue to get the value.

We can create class methods with dynamic names.

For instance, we can write:

const methodName = 'start';

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

  get modelName() {
    return this.model;
  }

  calcValue() {
    return "2000"
  }

  [methodName]() {
    //...
  }
}

We pass in the methodName variable to the square brackets to make start the name of the method.

Static Methods

We can add static methods that can be run directly from the class.

For instance, we can write:

class Plane {
  static fly(level) {
    console.log(level)
  }
}

We have the fly static method.

To run static methods, we can write:

Plane.fly(10000)

Static Properties

There’s no way to define static properties within the class body.

This may be added in future versions of JavaScript.

Generator Methods

We can add generator methods into our class.

For instance, we can make a class where its instances are iterable objects.

We can write:

class Iterable {
  constructor(...args) {
    this.args = args;
  }

  *[Symbol.iterator]() {
    for (const arg of this.args) {
      yield arg;
    }
  }
}

to create the Iterable class that takes a variable number of arguments.

Then we have the Symbol.iterator method to iterate through this.args and return the arguments.

The * indicates that the method is a generator method.

So we can use the class by writing:

const iterable = new Iterable(1, 2, 3, 4, 5);
for (const i of iterable) {
  console.log(i);
}

then we get:

1
2
3
4
5

We have created an instance of the Iterable class.

Then we looped through the iterator items by and logged the values.

Conclusion

A class can have a constructor, instance variables, getters, instance methods, static methods, and generator methods.