A deep dive into JavaScript OOP

How to create an object?

let user = {
  credentials: [
    'ahmedosama',
    'ahmedosama@sectheater.io',
    '123456789',
  ],

  signout() {
    /* Some business logic */
  },

  updateProfile() {
    // Some business logic
  },
}

How to create an object?

let user = Object.create({})

user.credentials = []

user.credentials.push(
  'ahmedosama',
  'ahmedosama@sectheater.io',
  '123456789',
)

user.register = function () {}
user.login = function () {}
user.logout = function () {}

console.log(user)

How to create an object?

let user = {...}

function createUser(username, email, password) {
  let user = {
    credentials: [],
  }

  user.credentials.push(username, email, password)

  user.register = function () {}
  user.login = function () {}
  user.logout = function () {}

  return user
}

let user = createUser(
  'ahmedosama',
  'ahmedosama@sectheater.io',
  '123456789',
)

console.log(user)

Prototypes

Prototypes

let user = {
  credentials: [
    'ahmedosama',
    'ahmedosama@sectheater.io',
    '123456789',
  ],

  signout() {
    /* Some business logic */
  },

  updateProfile() {
    // Some business logic
  },
}

Prototypes

function createUser(username, email, password) {
  let user = Object.create(userFunctions)

  user.credentials = []

  user.credentials.push(username, email, password)

  return user
}

const userFunctions = {
  login() {},
  logout() {},
  register() {},
}

const user1 = createUser(
  'ahmedosama',
  'ahmedosama@sectheater.io',
  '123456789',
)

const user2 = createUser(
  'mohamedosama',
  'mohamedosama@sectheater.io',
  'creative_password',
)

New keyword

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

let user = new UserFactory(
  'ahmedosama',
  'ahmedosama@sectheater.io',
  '123456789',
)

console.log(user)

Prototypes

function double(x) {
  return x * 2
}

double.y = 3

double(3) // 6

double.y // 3
double.prototype // {}

New keyword

function UserFactory(username, email, password) {...}

let user = new UserFactory(...)

new UserFactory()

Thread of execution

Local Memory

Return value

New keyword

function UserFactory(username, email, password) {...}

let user = new UserFactory(
  "ahmedosama",
  "ahmedosama@sectheater.io",
  "1234567890"
)

new UserFactory()

Thread of execution

Local Memory

Return value

username: 'ahmedosama'

email: 'ahmedosama@sectheater.io'

password: '123456789'

  • Create an empty object `this`

this: {}

New keyword

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

let user = new UserFactory(
  "ahmedosama",
  "ahmedosama@sectheater.io",
  "1234567890"
)
  • Create an empty object `this`
  • Invoke the function

new UserFactory()

Thread of execution

Local Memory

Return value

username: 'ahmedosama'

email: 'ahmedosama@sectheater.io'

password: '123456789'

this: {

    username: 'ahmedosama',

    email: 'ahmedosama@sectheater.io',

    password: '123456789',

}

Global Memory

UserFactory: => f() =>

                      +

{

     prototype: {} 

}

user:

New keyword

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

UserFactory.prototype.register = function () {}
UserFactory.prototype.login = function () {}
UserFactory.prototype.logout = function () {}

let user = new UserFactory(
  "ahmedosama",
  "ahmedosama@sectheater.io",
  "1234567890"
)
  • Create an empty object `this`
  • Invoke the function
  • Link __proto__ to fn's prototype

new UserFactory()

Thread of execution

Local Memory

Return value

username: 'ahmedosama'

email: 'ahmedosama@sectheater.io'

password: '123456789'

this: {

    username: 'ahmedosama',

    email: 'ahmedosama@sectheater.io',

    password: '123456789',

    __proto__: UserFactory

}

Global Memory

UserFactory: => f() =>

                      +

{

     prototype: {

        register: => f() =>

        login: => f() =>

        logout: => f() =>

     }

}

user:

New keyword

  • Create an empty object `this`
  • Invoke the function
  • Link __proto__ to fn's prototype
  • Return `this` if another object isn't returned 

new UserFactory()

Thread of execution

Local Memory

Return value

username: 'ahmedosama'

email: 'ahmedosama@sectheater.io'

password: '123456789'

this: {

    username: 'ahmedosama',

    email: 'ahmedosama@sectheater.io',

    password: '123456789',

    __proto__: UserFactory

}

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

UserFactory.prototype.register = function () {}
UserFactory.prototype.login = function () {}
UserFactory.prototype.logout = function () {}

let user = new UserFactory(
  "ahmedosama",
  "ahmedosama@sectheater.io",
  "1234567890"
)

Global Memory

UserFactory: => f() =>

                      +

{

     prototype: {

        register: => f() =>

        login: => f() =>

        logout: => f() =>

     }

}

user: {

    username: 'ahmedosama',

    email: 'ahmedosama@sectheater.io',

    password: '123456789',

    __proto__: UserFactory

}

New keyword

  • Create an empty object `this`
  • Link __proto__ to fn's prototype
  • Return `this` if an another object isn't returned 

new UserFactory()

Thread of execution

Local Memory

Return value

username: 'ahmedosama'

email: 'ahmedosama@sectheater.io'

password: '123456789'

this: {

    username: 'ahmedosama',

    email: 'ahmedosama@sectheater.io',

    password: '123456789',

    __proto__: UserFactory

}

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

UserFactory.prototype.register = function () {}
UserFactory.prototype.login = function () {}
UserFactory.prototype.logout = function () {}

let user = new UserFactory(
  "ahmedosama",
  "ahmedosama@sectheater.io",
  "1234567890"
)

Global Memory

UserFactory: => f() =>

                      +

{

     prototype: {

        register: => f() =>

        login: => f() =>

        logout: => f() =>

     }

}

user: {

    username: 'ahmedosama',

    email: 'ahmedosama@sectheater.io',

    password: '123456789',

    __proto__: UserFactory

}

Object default proto

const obj = {
  num: 3,
  getNum: function () {
    return this.num
  },
}

console.log(obj.getNum()) // 3
console.log(obj.hasOwnProperty('num')) // true

This keyword

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

UserFactory.prototype.register = function () {}
UserFactory.prototype.login = function () {}
UserFactory.prototype.logout = function () {}

let user = new UserFactory(
  "ahmedosama",
  "ahmedosama@sectheater.io",
  "1234567890"
)

This keyword

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

...

What is `this`?

What isn't `this`?

Self referencing fn

Fn's lexical scope

This keyword

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

...

What is `this`?

Self referencing fn

Contextual binding

This keyword

function getName() {
  console.log(this.first_name)
}

var first_name = "ahmed"

getName() // ahmed

What is `this`?

Self referencing fn

Default binding

This keyword

function getName() {
  console.log(this.first_name)
}

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
  getName,
}

user.getName() // ahmed

What is `this`?

Self referencing fn

Implicit binding

This keyword

function getName() {
  console.log(this.first_name)
}

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
  getName,
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
  getName,
}

user.getName() // ahmed
anotherUser.getName() // mahmoud

What is `this`?

Self referencing fn

Implicit binding

This keyword

function getName() {
  console.log(this.first_name)
}

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
  getName,
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
  getName,
}

setTimeout(user.getName, 1000) // undefined
anotherUser.getName() // mahmoud

What is `this`?

Self referencing fn

Losing implicit context

This keyword

const getName = () => console.log(this.first_name)

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
  getName,
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
  getName,
}

setTimeout(user.getName, 1000) // undefined
anotherUser.getName() // undefined

What is `this`?

Self referencing fn

Losing implicit context

This keyword

function getName() {
  console.log(this.first_name)
}

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
}

getName.call(user) // ahmed
getName.apply(anotherUser) // mahmoud

What is `this`?

Self referencing fn

Explicit binding

This keyword

function getName() {
  console.log(this.first_name)
}

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
}

const getUserName = getName.bind(user)
const getAnotherUserName = getName.bind(anotherUser)

getUserName() // ahmed
getAnotherUserName() // mahmoud

What is `this`?

Self referencing fn

Hard binding

This keyword

function getName() {
  console.log(this.first_name)
}

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
}

const getUserName = getName.bind(user)
const getAnotherUserName = getName.bind(anotherUser)

setTimeout(getUserName, 1000) // ahmed
getAnotherUserName() // mahmoud

What is `this`?

Self referencing fn

Hard binding

This keyword

function User(first_name, last_name) {
  this.first_name = first_name
  this.last_name = last_name
}

User.prototype.getName = function () {
  return `${this.first_name} ${this.last_name}`
}

let firstUser = new User('ahmed', 'osama')
let secondUser = new User('mahmoud', 'khaled')

console.log(firstUser.getName()) // ahmed osama
console.log(secondUser.getName()) // mahmoud khaled

What is `this`?

Self referencing fn

New binding

This keyword

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
  getName: function getName() {
    console.log(`${this.first_name} ${this.last_name}`)
  },
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
}

new (user.getName.bind(anotherUser))() // getName instance
user.getName() // ahmed osama
user.getName.bind(anotherUser)() // mahmoud khaled
user.getName.call(anotherUser) // mahmoud khaled

Binding precedence

Self referencing fn

New binding

This keyword

let user = {
  first_name: 'ahmed',
  last_name: 'osama',
  getName: function getName() {
    console.log(`${this.first_name} ${this.last_name}`)
  },
}

let anotherUser = {
  first_name: 'mahmoud',
  last_name: 'khaled',
}

new (user.getName.bind(anotherUser))() // undefined undefined
user.getName() // ahmed osama
user.getName.bind(anotherUser)() // mahmoud khaled
user.getName.call(anotherUser) // mahmoud khaled

Self referencing fn

  1. New keyword?
  2. Explicit/Hard binding
  3. Implicit binding
  4. Default binding

Binding precedence

This keyword

let workshop = {
  instructor: 'ahmed osama',
  course: 'OOP, the hard way',
  students: ['mahmoud khaled', 'ismael mostafa', 'amira hany'],

  printStudents: function () {
    this.students.forEach(function (student) {
      console.log(
        `${student} is taking "${this.course}" with [${this.instructor}]`,
      )
    })
  },
}

workshop.printStudents() // mahmoud khaled is taking "undefined" with [undefined]

Self referencing fn

  1. New keyword?
  2. Explicit/Hard binding
  3. Implicit binding
  4. Default binding

Lexical this

This keyword

let workshop = {
  instructor: 'ahmed osama',
  course: 'OOP, the hard way',
  students: [
    'mahmoud khaled',
    'ismael mostafa',
    'amira hany',
  ],

  printStudents: function () {
    this.students.forEach(
      function (student) {
        console.log(
          `${student} is taking "${this.course}" with [${this.instructor}]`,
        )
      }.bind(this),
    )
  },
}

workshop.printStudents()

Self referencing fn

Lexical this

This keyword

let workshop = {
  instructor: 'ahmed osama',
  course: 'OOP, the hard way',
  students: [
    'mahmoud khaled',
    'ismael mostafa',
    'amira hany',
  ],

  printStudents: function () {
    this.students.forEach((student) =>
      console.log(
        `${student} takes "${this.course}" with [${this.instructor}]`,
      ),
    )
  },
}

workshop.printStudents()

Self referencing fn

Lexical this

Inheritance/Sub classing

UserFactory

PremiumUserFactory

DiamondUserFactory

Inheritance/Sub classing

function UserFactory(username, email, password) {
  let newUser = Object.create(userFunctions)

  newUser.email = email
  newUser.username = username
  newUser.password = password

  return newUser
}

const userFunctions = {
  login: function () {},
  logout: function () {},
  register: function () {},
}

Inheritance/Sub classing

function PremiumUserFactory(
  username,
  email,
  password,
  balance,
) {
  let newPremiumUser = Object.create(premiumUserFunctions)

  newPremiumUser.email = email
  newPremiumUser.username = username
  newPremiumUser.password = password
  newPremiumUser.balance = balance

  return newPremiumUser
}

const premiumUserFunctions = {
  accessCourses: function () {},
  addToBookmark: function () {},
}

Object.setPrototypeOf(premiumUserFunctions, userFunctions)

Inheritance/Sub classing

function PremiumUserFactory(
  username,
  email,
  password,
  balance,
) {
  let newPremiumUser = userFactory(
    username,
    email,
    password,
  )

  Object.setPrototypeOf(
    newPremiumUser,
    premiumUserFunctions,
  )

  newPremiumUser.balance = balance

  return newPremiumUser
}

const premiumUserFunctions = {
  accessCourses: function () {},
  addToBookmark: function () {},
}

Object.setPrototypeOf(premiumUserFunctions, userFunctions)

Inheritance/Sub classing

function UserFactory(username, email, password) {
  this.username = username
  this.email = email
  this.password = password
}

UserFactory.prototype.login = function () {}
UserFactory.prototype.logout = function () {}
UserFactory.prototype.register = function () {}

Inheritance/Sub classing

function PremiumUserFactory(
  username,
  email,
  password,
  balance,
) {
  UserFactory.call(this, username, email, password)
  // UserFactory.apply(this, [username, email, password])

  this.balance = balance
}

PremiumUserFactory.prototype = Object.create(UserFactory.prototype)

PremiumUserFactory.prototype.accessCourses = function () {}

Inheritance/Sub classing

class UserFactory {
  constructor(username, email, password) {
    this.username = username
    this.email = email
    this.password = password
  }

  register() {}
  login() {}
  logout() {}
}

Inheritance/Sub classing

class UserFactory {
  constructor(username, email, password) {
    this.username = username
    this.email = email
    this.password = password
  }

  register() {}
  login() {}
  logout() {}
}
class PremiumUserFactory extends UserFactory {
  constructor(username, email, password, balance) {
    super(username, email, password)

    this.balance = balance
  }

  accessCourses() {}
}

Wrapping Up

Wrapping Up

  • Objects in JS are not class instances
  • A prototype is where objects' functions are stored
  • A __proto__ is a reference to objects' functions
  • A function is both a callable and an object
  • The new keyword does four things
    • Creates an empty object `this`
    • Invoke the function with `this` as its context
    • Links function's prototype to this's __proto__
    • Returns `this` if another object isn't returned.
  • Contextual -this- binding
  • Inheritance using the prototypal model

What's next?

  • Deep dive into OOP pillars
  • Software design patterns & principles
    • SOLID principles
    • Design patterns
  • Data structures & Algorithms
  • Testing

What's next?

SecTheater

  • Deep dive into OOP pillars
  • Software design patterns & principles
    • SOLID principles
    • Design patterns
  • Data structures & Algorithms
  • Testing

SecTheater

Raise up your techinal skills

SecTheater

Made with Slides.com