let harry = { name: 'Harry Smith', salary: 90000 }
harry = { name: 'Harry Smith', salary: 90000, raiseSalary: function(percent) { this.salary *= 1 + percent / 100 } }
harry.raiseSalary(10)
this
is set to harry
in the call.harry = { ... raiseSalary(percent) { this.salary *= 1 + percent / 100 } }
function createEmployee(name, salary) { return { name, salary, raiseSalary: function(percent) { this.salary *= 1 + percent / 100 } } }
raiseSalary
property.
const employeePrototype = { raiseSalary: function(percent) { this.salary *= 1 + percent / 100 } }
function createEmployee(name, salary) { const result = { name, salary } Object.setPrototypeOf(result, employeePrototype) return result }
harry.raiseSalary(5)
harry.raiseSalary = function(rate) { this.salary = Number.MAX_VALUE }
new
operator.function Employee(name, salary) { this.name = name this.salary = salary }
Employee.prototype.raiseSalary = function(percent) { this.salary *= 1 + percent / 100 }
const harry = new Employee('Harry Smith', 90000)
new
operator:
.prototype
this
Employee
isn't a class—it's a function.class Employee { constructor(name, salary) { this.name = name this.salary = salary } raiseSalary(percent) { this.salary *= 1 + percent / 100 } }
Employee
still isn't a class—it's a function.constructor
get
:
class Person { constructor(last, first) { this.last = last; this.first = first } get fullName() { return `${this.last}, ${this.first}` } }
const harry = new Person('Smith', 'Harry') const harrysName = harry.fullName // 'Smith, Harry'
set
:
class Person { ... set fullName(value) { const parts = value.split(/,\s*/) this.last = parts[0] this.first = parts[1] } }
harry.fullName = 'Smith, Harold'
class BankAccount { balance = 0 deposit(amount) { this.balance += amount } ... }
#
class BankAccount { #balance = 0 deposit(amount) { this.#balance += amount } ... }
#
.class BankAccount { ... static percentOf(amount, rate) { return amount * rate / 100 } ... addInterest(rate) { this.balance += BankAccount.percentOf(this.balance, rate) } }
BankAccount.percentOf = function(amount, rate) { return amount * rate / 100 }
class BankAccount { static OVERDRAFT_FEE = 30 ... withdraw(amount) { if (this.balance < amount) { this.balance -= BankAccount.OVERDRAFT_FEE } ... } }
BankAccount.OVERDRAFT_FEE = 30
class BankAccount { ... static get OVERDRAFT_FEE() { if (this.overdraftFee === undefined) this.overdraftFee = 10 return this.overdraftFee // this is the constructor } static set OVERDRAFT_FEE(newValue) { if (newValue > this.overdraftFee) { this.overdraftFee = newValue } }
class Employee { constructor(name, salary) { ... } raiseSalary(percent) { ... } ... } class Manager extends Employee { getSalary() { return this.salary + this.bonus } ... }
const boss = new Manager(...)
boss.raiseSalary(10) // Calls Employee.prototype.raiseSalary
class Employee { ... getSalary() { return this.salary } } class Manager extends Employee { ... getSalary() { return this.salary + this.bonus } }
const empl = ... const salary = empl.getSalary()
class Manager { ... getSalary() { return super.getSalary() + this.bonus } }
super
starts lookup with the parent of the prototype object in which the method was defined. getSalary
defined in Manager.prototype
Employee.prototype
class Manager extends Employee { ... get salary() { return super.salary + this.bonus } }
class Manager extends Employee { constructor(name, salary, bonus) { super(name, salary) this.bonus = bonus } ... }
this
is valid after call to super
class Manager extends Employee {
// No constructor
getSalary() { ... }
}
const boss = new Manager('Mary Lee', 180000)
// Calls Employee('Mary Lee', 180000)
const Employee = class { constructor(name, salary) { ... } raiseSalary(percent) { ... } }
function withToString(base) { return class extends base { toString() { let result = '{' for (const key in this) { if (result !== '{') result += ', ' result += `${key}=${this[key]}` } return result + '}' } } }
const BetterEmployee = withToString(Employee) e = new BetterEmployee('Mad Hatter', 90000) console.log(e.toString()) // Prints{name=Mad Hatter, salary=90000}
, not[object Object]
this
Referencenew ClassName
sets this
to the new object.object.method(arguments)
sets this
to the object on which the method is invoked.new
class
syntax.this
is undefined
with old-style constructor.Number('3')
/new Number('3')
new
const harrysAccount = new BankAccount()
const action = harrysAccount.deposit
action(1000) // Error—this
doesn't point to any bank account
this
Referenceclass BankAccount { ... spreadTheWealth(accounts) { accounts.forEach(function(account) { account.deposit(this.balance / accounts.length) // Error—this
undefined inside nestedfunction
}) this.balance = 0 } }
this
is statically captured in arrow function.
spreadTheWealth(accounts) {
accounts.forEach(account => {
account.deposit(this.balance / accounts.length) // this
correctly bound
})
this.balance = 0
}
this
Referencethis
Referenceconst observers = [] function notifyObservers() { for (let i = 0; i < observers.length; i++) { observers[i]() } } class BankAccount { constructor() { this.balance = 0 } deposit(amount) { this.balance += amount notifyObservers() } ... }
obj.method(args)
is the same as obj['method'](args)
obj[index](args)
, this
is obj
this
Referencethis
is the array of observers!
class UserInterface { log(message) { ... } start() { const acct = new BankAccount() observers.push(function() { this.log('More money!') }) acct.deposit(1000) } }
this
is problematic.