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'
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"
)
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"
)
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
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
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
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
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() {}
}
Raise up your techinal skills