Javascript Language Features
Author: Hao Luo (@howlowck)
Javascript is an object-oriented and functional language.
Types
Javascript has no classes. However it has two types:
Primative Types
Simple data types
- Boolean (
true
orfalse
) - null (
null
) - undefined (
undefined
) - number (
100
or12.2
) - string (
'hello'
) - Symbol (in ES2015)
let oldName = 'St Clair'
let newName = oldName
newName = 'Luo'
oldName
Reference Types
Reference Types are objects. An object is simply an unordered list of properties consisting with a name and a value. The value can be of either type (Primative or Reference).
let bot1 = {
name: 'Optimus Prime',
greet: function () {
return 'hi'
}
}
let bot2 = bot1
bot2.name = 'Bumblebee'
bot1
Functions
graph TB
subgraph objects
functions
arrays
...
end
subgraph primary types
strings
booleans
....
end
Defining a function in Javascript
Declaration:
function greet() {
return 'hello my name is Hao'
}
Function Expression:
var greet = function () {
return 'hello my name is Hao'
}
What's the difference?
Declarations are named functions, and function expressions can be anonymous functions.
Declarations are hoisted to the top of the function
greet()
function greet() {
return 'hello my name is Hao'
}
As oppose to:
anotherGreet()
var anotherGreet = function () {
return 'hello my name is Hao'
}
Function Expressions can't be hoisted because the the function can only be called or referenced through a variable.
Javascript is a functional language
Functions are treated as first-class citizens, which means they can do things like other types.
Functions can:
- be assigned to variables
- be parameters of another function
- be created and returned from another function
- have properties on the function like a regular class
In fact, functions are just objects. The only difference between a "function" and a regular object (ie {name: 'Kevin'}
),is that a function can be evoked (or called).
Function Overloading
Javascript cannot overload a function based on differing function signatures like you can with Java.
function haosGreet (name) {
return 'hello ' + name
}
function haosGreet () {
return 'good morning!'
}
Functions in an object (and this
keyword)
Remember functions can be defined/created anywhere. So let's create one in an object
var gabby = {
name: 'Gabby',
coffeeLeft: 5,
coffeeConsumed:0,
drinkCoffee: function () {
this.coffeeLeft--
this.coffeeConsumed++
return this.name + ' drank some coffee'
}
}
var kevin = {
name: 'Kevin',
coffeeLeft: 5,
coffeeConsumed: 0
}
// Can Kevin steal gabby's coffee by copying the way she drinks?
kevin.drink = gabby.drinkCoffee
Now what if gabby's drinkCoffee
function is not even in an object?
var standaloneDrinkCoffee = gabby.drinkCoffee
// what happens if this function is called?
this
keyword is the context of which the function is evoked. When you evoke a function as a object method, like so gabby.drinkCoffee()
, the context is implied as being gabby. When you evoke the function outside of the object, it loses its context.
Sidenote: There is also another way you can use the context, but let's see how to explicitly set the context.
bind
and call
function methods
Note: Remember functions are just objects which can have any kind of properties, and properties can other functions. So functions can have other functions.
var gabbyDrinksCoffee = standaloneDrinkCoffee.bind(gabby)
// gabbyDrinksCoffee()
Or you can immediately evoke the function with an explicit context
standaloneDrinkCoffee.call(gabby)
A function inside of a function inside of an object
var ron = {
name: 'Ron',
coffeeLeft: 3,
coffeeConsumed: 0,
drinkCoffee: function () {
function sendAlert () {
console.log(this.name + ', get more coffee')
}
this.coffeeLeft--
this.coffeeConsumed++
if (this.coffeeLeft == -1) {
setTimeout(sendAlert, 2000)
return this.name + ', you drank too much coffee'
}
return this.name + ' drank some coffee'
}
}
ron.drinkCoffee()
ron.drinkCoffee()
ron.drinkCoffee()
// ron.drinkCoffee()
// how would you fix the issue?
This is the other way you'd lose the context of a function.
Javascript is a Object Oriented Language
There is no classes in Javascript. Everything is an object (except for primitive types). So how can you make a new instance of something based on another thing?
Javascript uses constructors and prototypes.
Constructors
Constructors are simply javascript functions. As conventions, constructor functions are usually capitalized.
you create a new instance by using the new
keyword.
function Water() {
this.caffineLevel = 0
this.temperature = 0
this.heatUpInHeater = function () {
this.temperature = 50
}
}
var kevinsWater = new Water()
var gabbysWater = new Water()
var paulsWater = new Water()
kevinsWater.heatUpInHeater()
// is kevinsWater.heatUpInHeater the same function as gabbysWater.heatUpInHeater?
prototype
property on a constructor function
When you create a function, an property named prototype
is assigned to this function object with the value of an object. This object initially has a constructor
property on it.
Whatever is in this prototype
object, automatically gets shared across all of its instanciated objects.
function SmartWater() {
this.caffineLevel = 0
this.temperature = 0
}
SmartWater.prototype.heatUpInHeater = function () {
this.temperature = 60
}
var kevinsSmartWater = new SmartWater()
var gabbysSmartWater = new SmartWater()
kevinsSmartWater.heatUpInHeater === gabbysSmartWater.heatUpInHeater
You can dynamically change the behaviors of the a defined type.
kevinsSmartWater.heatUpInHeater()
// console.log('kevins water temp pre change', kevinsSmartWater.temperature)
SmartWater.prototype.heatUpInHeater = function () {
this.temperature = 100
}
kevinsSmartWater.heatUpInHeater()
// console.log('kevins water temp after change', kevinsSmartWater.temperature)
gabbysSmartWater.heatUpInHeater()
// console.log('gabbys water temp after change', gabbysSmartWater.temperature)
Inheritances
Javascript uses prototypes to create a prototypal chain which produces its inheritance structure.
- Every function has an property named
prototype
(as we saw in the previous section). - Every object has a
[[prototype]]
object on it (usually named__proto__
, but not a spec standard).
When you create a function...
Everytime you create a function, Javascript puts an object in the prototype
property onto that function.
This prototype
property object has a property called constructor
. this constructor simply references back to the function.
function Park(name) {
this.name = name
}
// Park.prototype
This prototype
property object in the function is referenced in the [[prototype]]
property of its instance objects.
var grantPark = new Park('Grant')
var proto = Object.getPrototypeOf(grantPark)
var proto2 = grantPark.__proto__
// how is this proto related to Park?
Object Inheritance
So every object has the [[prototype]]
property (commonly named __proto__
). You can actually "hijack" this property and reference this object to ANY object essentially changing the type of that object.
hijacking the __proto__
property:
var ironmanMk1 = {
name: 'Mk1',
altitude: 0,
takeOff: function () {
this.altitude: 100
}
}
var ironmanMk2 = {
name: 'Mk2',
autopilot: true
}
ironmanMk2.__proto__ = ironmanMk1
// ironmanMk2.takeOff
Object.create()
Object.create()
is a "static" method on the base Object
constructor function.
the function lets one quickly create an object by supplying the [[prototype]]
(or __proto__
) object, and the member properties (and its values).
var ironmanMk3 = Object.create(ironmanMk2, {
name: 'Mk3',
fireProjectile: function () {
return 'fired!'
}
})
// Object.getPrototypeOf(ironmanMk3)
How does an object find the value of a property?
Remember in every object there is a [[prototype]]
object.
When you try to access a property (could be a primitive, object, or function), and it's not on the actual object. Javascript will look to the [[prototype]]
object on the object, and see if the property is on there. If it's not on there, Javascript will look at the [[prototype]]
on there, until it finds the value.
// ironmanMk3.name
// Object.getPrototypeOf(ironmanMk3)
// show [[prototype]] of [[prototype]] of ironmanMk3
// ironmanMk3.takeOff
Constructor Inheritance
Sometimes you'd like to inherite from the constructor function instead of an object. This works pretty much the same way as object inheritance with a few more considerations.
1. Call the parent constructor function
Many language allow you to quickly get the parent's constructor function using keywords like super
or parent
. Unfortunately, Javascript does not have that feature (until ES6).
However, you can steal the parent function by setting the context with call
.
function CognitiveServiceClient(host, apiKey) {
this.host = host
this.apiKey = apiKey
}
CognitiveServiceClient.prototype.fetchGetJson = function (url) {
return fetch(this.host + url)
.then(function (res) {return res.json()})
}
function FaceApiClient(host, apiKey) {
CognitiveServiceClient.call(this, host, apiKey)
}
2. Assign the prototype
and reference back the constructor
Remember when you created a function, the prototype is automatically set, and there is a property of a constructor
that point itself back to the function. this constructor is then used to determine the typeof
of its instanciated object.
Because of this, if we want to instanciate a FaceApiClient by calling client
, and we want to setup proper prototypal chain then we have to set the constructor of FaceApiClient.prototype.constructo
back to FaceApiClient
.
FaceApiClient.prototype = Object.create(
CognitiveServiceClient.prototype, {
getPersonGroupList: function () {
return this.getFetchJson(this.host + '/person-groups')
},
//constructor: FaceApiClient
}
}
var client = new FaceApiClient()
Object.getPrototypeOf(Client)
Bundle all the concepts together...
To extend a constructor function you need to
- Create a function (studly case by convention)
- In the function, call the parent constructor function by explicitly setting the context
- Set the
prototype
of the function with an object that has the parent.prototype object as its[[prototype]]
, and theconstructor
member property reference back to the function.
ES6 Features
That was a lot of information.. and you have to understand a lot about how Javascript works to create a simple "class"-like construct. Also this
is often a pain point of development when you have to use call
or bind
all the time.
class
keyword
class FancyCogServiceClient {
constructor (host, apiKey) {
this.host = host
this.apiKey = apiKey
}
getFetchJson(path) {
return fetch(this.host + path)
.then((res) => {
return res.json
})
}
}
class FancyFaceApiClient extends FancyCogServiceClient {
constructor(host, apiKey) {
super(host, apiKey)
}
getPersonGroupList () {
return this.getFetchJson(this.host + '/person-groups')
}
}
var fancyFaceApiClient = new FancyFaceApiClient('https://cogservices.com/face/v1', 'secret')
arrow functions
Many say arrow functions are a shortcut to anonymous functions. However it is a little more than that.
Arrow functions do not have the concept of this
or arguments
, that this means is that the this
keyword will refer to the parent scope of where the arrow function is defined.