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 or false)
  • null (null)
  • undefined (undefined)
  • number (100 or 12.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

  1. Create a function (studly case by convention)
  2. In the function, call the parent constructor function by explicitly setting the context
  3. Set the prototype of the function with an object that has the parent.prototype object as its [[prototype]], and the constructor 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.

results matching ""

    No results matching ""