WDIM387 Week 3

JavaScript Objects


Instructor: Dan Muzyka

danmuzyka.ai@gmail.com

Object Literals


var dracula = {
name: "Dracula",
age: 582,
hungry: true,
bloodSupply: 0.2,
feedOn: function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
}
};

Object Literals

Handy for one-offs (singletons), but what if you need a bunch of similar objects?

var dracula = {
name: "Dracula",
age: 582,
hungry: true,
bloodSupply: 0.2,
feedOn: function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
}
};
var lestat = {
name: "Lestat",
age: 252,
hungry: true,
bloodSupply: 0.2,
feedOn: function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
}
};

Constructors

No classes in JavaScript, but you can use constructor functions.

var Vampire = function (name, age, hungry, bloodSupply) {
this.name = name || "Dracula";
this.age = age || 500;
this.hungry = hungry || true;
this.bloodSupply = bloodSupply || 0.2;
this.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};
};

var dracula = new Vampire();
var lestat = new Vampire("Lestat", 232);

What are some potential problems with this approach?

Prototypes

If you want to share a property or method across objects (instead of creating a new copy of it in memory each time), add to the prototype.

var Vampire = function (name, age, hungry, bloodSupply) {
this.name = name || "Dracula";
this.age = age || 500;
this.hungry = hungry || true;
this.bloodSupply = bloodSupply || 0.2;
};
Vampire.prototype.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};

var dracula = new Vampire();
var lestat = new Vampire("Lestat", 232);

Constructor Limitations

What happens if you forget the new keyword?

var Vampire = function (name, age, hungry, bloodSupply) {
this.name = name || "Dracula";
this.age = age || 500;
this.hungry = hungry || true;
this.bloodSupply = bloodSupply || 0.2;
};
Vampire.prototype.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};

var dracula = Vampire();
var lestat = Vampire("Lestat", 232);

console.log(dracula);
console.log(lestat);
console.log(age);
console.log(hungry);

Constructor Pattens Enforcing "new"

Factory function pattern:

var Vampire = function (name, age, hungry, bloodSupply) {
var that = {
"name": name || "Dracula",
"age": age || 500,
"hungry": hungry || true,
"bloodSupply": bloodSupply || 0.2
}
return that;
};
var dracula = Vampire();
var lestat = Vampire("Lestat", 232);

What are some potential problems with this approach?

Constructor Pattens Enforcing "new"

Prototyping issue with factory function approach:

var Vampire = function (name, age, hungry, bloodSupply) {
var that = {
"name": name || "Dracula",
"age": age || 500,
"hungry": hungry || true,
"bloodSupply": bloodSupply || 0.2
}
return that;
};
Vampire.prototype.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};
var dracula = Vampire();
var lestat = Vampire("Lestat", 232);
dracula.feedOn(lestat); // TypeError: dracula.feedOn is not a function

Constructor Pattens Enforcing "new"

Self-invoking constructor pattern:

var Vampire = function (name, age, hungry, bloodSupply) {
if (!(this instanceof Vampire)) {
return new Vampire(name, age, hungry, bloodSupply);
}
this.name = name || "Dracula";
this.age = age || 500;
this.hungry = hungry || true;
this.bloodSupply = bloodSupply || 0.2;
};

var dracula = new Vampire();
var lestat = Vampire("Lestat", 232);

Inheritance


var Undead = function() {
if (!(this instanceof Undead)) {
return new Undead();
}
this.alive = false;
this.animate = true;
// Dynamic Prototype Pattern
// This only evaluates to true the first time this function runs.
if (typeof this.attackHuman !== "function") {
Undead.prototype.attackHuman = function () {
console.log("Attacking a human!");
};
}
};
// Vampire constructor here...
// Create an Undead object and assign it as the prototype for Vampire
Vampire.prototype = new Undead();
var dracula = new Vampire();
console.log(dracula.alive);
dracula.attackHuman();

Inheritance


// Undead constructor...
// Vampire constructor...
Vampire.prototype.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};
Vampire.prototype = new Undead();
var dracula = new Vampire();
var lestat = Vampire("Lestat", 232);
dracula.feedOn(lestat); // TypeError: dracula.feedOn is not a function

Inheritance


// Undead constructor...
// Vampire constructor...
Vampire.prototype = new Undead();
Vampire.prototype.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};

var dracula = new Vampire();
var lestat = Vampire("Lestat", 232);
dracula.feedOn(lestat); // This works

Inheritance

Inheritance is dynamic:

// Undead constructor...
// Vampire constructor...
Vampire.prototype = new Undead();
var dracula = new Vampire();

Undead.prototype.moan = function() {
console.log("Mmmmwwaaaaa...!");
};

dracula.moan();

Inheritance

You can override inherited methods and properties:

// Undead constructor...
// Vampire constructor...
Vampire.prototype = new Undead();
var dracula = new Vampire();
Undead.prototype.moan = function() {
console.log("Mmmmwwaaaaa...!");
};
dracula.moan();

Vampire.prototype.moan = function() {
console.log("Blwa!");
};

dracula.moan();

Inheritance

Determine if an object property is on the object itself, or somewhere else up the prototype chain.

// Undead constructor...
// Vampire constructor...
Vampire.prototype = new Undead();
var dracula = new Vampire();

Undead.prototype.moan = function() {
console.log("Mmmmwwaaaaa...!");
};

Vampire.prototype.moan = function() {
console.log("Blwa!");
};

console.log(dracula.hasOwnProperty("moan")); // What do you think?
console.log("moan" in dracula);

Inheritance

Limitations of the factory function approach:

var Vampire = function (name, age, hungry, bloodSupply) {
var that = {
"name": name || "Dracula",
"age": age || 500,
"hungry": hungry || true,
"bloodSupply": bloodSupply || 0.2
}
return that;
};
Vampire.prototype.feedOn = function(person) {
if (this.bloodSupply < 1) {
person.bloodSupply -= 0.1;
this.bloodSupply += 0.1;
}
};
var dracula = Vampire();
var lestat = Vampire("Lestat", 232);
dracula.feedOn(lestat); // What happens here?

Inheritance

Problems with reference type properties on prototypes.

// Vampire constructor...
Vampire.prototype.victims = [];

var dracula = Vampire();
var lestat = Vampire("Lestat", 232);

dracula.victims.push("John");
dracula.victims.push("Jane");

console.log(dracula.victims);
console.log(lestat.victims);




Break

Video




Stop Nesting Functions (But Not All of Them)




Discuss Video

Lab Time

Start homework and reading for next week.


https://github.com/aipdx-wdim387/week3

WDIM387 Week 3JavaScript Objects

By danmuzyka

WDIM387 Week 3JavaScript Objects

  • 1,212