links: JS MOC


Definitions

Factory Function

A factory function is any function which is not a class or constructor that returns a (presumably new) object.

In JS any function can return an object. When it does so without the new keyword, it’s a factory function

Constructor Function

Constructor functions are like regular functions, but we use them with the new keyword. There are two types of constructor:

  • built-in constructors such as Array and Object which are available automatically in the execution environment at runtime.
  • custom constructors, which defines properties and methods for your own type of object.

A constructor is useful when you want to create multiple similar objects with the same properties and methods. It’s a convention to capitalize the name of constructor functions to distinguish them from regular functions

function Book(name, year) {
	this.name = name
	this.year = year
}
 
const ydkjs = new User('You don\'t know js', 2018)

When a function is executed with new, it does the following steps:

  1. A new empty object is created and assigned to this
  2. The function body executes. Usually it modifies this, adds new properties to it.
  3. The value of this is returned.

Read more about new keyword in JS

In other words, new Book(..) does something like:

function Book(name, year) {
	// this = {} (implicitly)
	
	// add properties to this
	this.name = name
	this.year = year
	
	// return this (implicitly)
}

So let book = new Book('ydkjs', 2018) gives the same result as:

let book = {
	name: 'ydkjs',
	year: 2018
}

Now if we want to create other books, we can call new Book('design patterns', 1998). Much shorter than using literals every time, and also easy to read.

The main purpose of constructor function is to implement reusable object creation code

Note: Technically any function can be a constructor function. That is; any function can be run with new, and it will execute the algorithm above (see new keyword explanation in constructor function)

Determining the type of an instance

To find out whether an object is an instance of another one, we use instanceof operator

book instanceof Book // true
book instanceof Book // false

Note that if the right side of the instanceof operator isn’t a function, it will throw an error

book instanceof {}
// TypeError: invalid instanceof operand({})

Another way to find type of an instance is to use constructor property. Consider the following

book.constructor === Book // true

Note that, using the constructor property to check the type of an instance is generally considered as bad practice because it can be overwritten

Advanced

Constructor Function

How to test a function is called with new?

Inside a function, we can check whether it is called with new or without it using a special new.target property

It is undefined (empty) for regular calls and equals the function if called with new:

function Book(name, year) {
	console.log(new.target)
}
 
// without "new":
Book('ydkjs', 2018) // undefined
 
// with "new":
new Book('ydkjs', 2018) // function Book(name, year) { .. }

That can be used inside the function to know whether it was called with new, “in constructor mode”, or without it, “in regular mode”.

We can also make new and regular calls to do the same like this:

function Book(name, year) {
	if(!new.target) { // if you run me without new
		return new Book(name, year) // ... I will add new for you
	}
	
	this.name = name
	this.year = year
}

var ydkjs = Book("ydkjs", 2018) // redirects call to new Book
console.log(ydkjs.year) // 2018

Return from constructors

Usually constructors do not have a return statement. Their task is to write all the necessary stuff into this, and it automatically becomes the result.

But, if there is a return statement the rule is simple

  • if return is called with an object, then the object is returned instead of this.
  • if return is called with a primitive, it’s ignored.

Example returning object:

function Book(name, year) {
	this.name = name
	this.year = year
	
	return {
		bookName: this.name
	} // returns this object
}
console.log(new Book('ydkjs', 2018).name) // undefined
console.log(new Book('ydkjs', 2018).bookName) // ydkjs

Now, only bookName will be available and name and year will not be available as return overridden this to another object

Example returning primitive:

function Book(name, year) {
	this.name = name
	this.year = year
	
	return // return this
}
 
console.log(new Book('ydkjs', 2018).name) // ydkjs
Omitting parentheses

We can omit parentheses after new, if it has no arguments:

let book = new Book / no parenthses
// same as
let book = new Book()

omitting parentheses is not considered as a “good style”, but the syntax is permitted by the specification.


tags: javascript , fundamentals , design-pattern