Object Creation Patterns Flashcards
2 disadvantages of factory functions
Each object created by the factory function has a copy of all the methods, which can be redundant and memory intensive.
(basically the benefit of prototypes)
There is no way to tell which factory function created an object, so there’s no way to be sure that you’re working with the right kind of object.
(which is the benefit of constructors)
Problems 3 -5
https://launchschool.com/lessons/e3c64e3f/assignments/bf77a962
https://launchschool.com/lessons/e3c64e3f/assignments/bf77a962
The object oriented code makes these questions easier to answer (5 things, 3 chunks)
How do we create that object?
Where should we add new properties and methods?
What are the properties of any given object?
What operations can I perform on that vehicle?
What are the important concepts in the program?
Describe the constructor algorithm (5 things)
1 It creates an entirely new object.
2 It sets Constructor.prototype as the prototype for the new object. That is, the new object inherits from the object referenced by Constructor.prototype.
3 It sets the value of this for use inside the function to point to the new object.
4 It invokes the function. Since this refers to the new object, we use it within the function to set the object’s properties and methods.
5 Finally, once the function finishes running, new returns the new object even though we don’t explicitly return anything. If something is returned the object is returned instead. If ANOTHER object is returned, that OTHER object is returned instead
What happens if you invoke a constructor function without the new keyword?
It executes the function body as normal. But without assigning this to a newly created object, “this” applies to the global object. So it puts a bunch of new properties on the global object.
What happens with this:
let apple = () => {
this.apple = “sauce”;
};
let applesauce = new apple();
Type Error
What happens with this:
let foo = {
Car: function(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
};
let car1 = new foo.Car(‘Toyota’, ‘Camry’, 2019);
car1.make;
//=> ‘Toyota’
What happens with this?
let foo = {
Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
};
new foo.Car();
//=> Uncaught TypeError: foo.Car is not a constructor
concise syntax (also called a concise method) won’t work.
ie. Car(make, model, year) vs Car: function(make, model, year)
new doesn’t work with these T or F
new console.log();
new Math();
new parseInt(“3”);
new Date();
T
//=> Uncaught TypeError: console.log is not a constructor
for the first 3,
but it works with Date();
//=> 2019-06-26T02:50:20.191Z
What happens with
function Cat(name, breed, weight) {
this.name = name;
this.breed = breed;
this.weight = weight;
return ‘a cat’;
}
let fluffy = new Cat(‘fluffy’, ‘Persian’, 15);
fluffy.weight;
// 15
The rule here is that if a constructor explicitly tries to return an object, then that object is returned instead of the new object created with the use of new. In all other situations, it returns the newly created object, provided no errors occur. In particular, the constructor ignores explicit return values that are primitives.
What happens with
function Cat(name, breed, weight) {
this.name = name;
this.breed = breed;
this.weight = weight;
return { foo: 1 };
}
let fluffy = new Cat(‘fluffy’, ‘Persian’, 15);
fluffy.weight;
fluffy.foo;
// undefined
// 1
The rule here is that if a constructor explicitly tries to return an object, then that object is returned instead of the new object created with the use of new. In all other situations, it returns the newly created object, provided no errors occur. In particular, the constructor ignores explicit return values that are primitives.
How to avoid this noise:
function Car(make, model, year, color, passengers, convertible, mileage) {
this.make = make;
this.model = model;
this.year = year;
this.color = color;
this.passengers = passengers;
this.convertible = convertible;
this.mileage = mileage;
this.started = false;
this.drive = function() {
this.started = true;
};
// rest of the methods
}
One common technique that we can use to manage our parameters better involves passing them to our constructor with an object argument:
let civicArgs = {
make: ‘Honda’,
model: ‘Civic’,
year: 2016,
color: ‘black’,
passengers: 5,
convertible: false,
mileage: 16000
}
let civic = new Car(civicArgs);
If we use an object as our constructor argument
How can we avoid this noise?
function Car(args) {
this.make = args.make;
this.model = args.model;
this.year = args.year;
this.color = args.color;
this.passengers = args.passengers;
this.convertible = args.convertible;
this.mileage = args.mileage;
this.started = false;
this.drive = function() {
this.started = true;
};
// rest of methods
}
Object.assign
function Car(args) {
Object.assign(this, args);
this.drive = function() {
this.started = true;
};
// rest of the methods
}
Issue with the object.assign method inside the constructor function
However, one drawback of the Object.assign approach is that the args object may contain properties that the Car object doesn’t need. Those additional properties will, nevertheless, get added to the Car object. Those properties may just be excess baggage for the objects to carry around, but they may also cause trouble.
Return boolean if an object was created using a specific constructor function
object instanceof Constructor
ex:
civic instanceof Car
What is the implicit execution context of a method inside an object created with a contstructor?
The new object
What happens with:
function Lizard() {
this.scamper = function() {
console.log(“I’m scampering!”);
};
}
let lizzy = Lizard();
lizzy.scamper(); // ?
Type error
undefined.undefined is what it’s tryin to do
Create a constructor function that defines the prototype to something othe than default
let DogPrototype = {
bark() {
console.log(this.weight > 20 ? ‘Woof!’ : ‘Yip!’);
}
};
function Dog(name, breed, weight) {
Object.setPrototypeOf(this, DogPrototype);
this.name = name;
this.breed = breed;
this.weight = weight;
// this.bark method removed.
}
Make a constructor function that “defines the prototype”, but use the native prototype setting functionality of constructor functions
function Dog(name, breed, weight) {
// deleted Object.setPrototypeOf(this, Dog.myPrototype);
this.name = name;
this.breed = breed;
this.weight = weight;
}
Dog.prototype.bark = function() {
console.log(this.weight > 20 ? ‘Woof!’ : ‘Yip!’);
};
let maxi = new Dog(‘Maxi’, ‘German Shepherd’, 32);
maxi.bark(); // ‘Woof!’
let biggie = new Dog(‘Biggie’, ‘Whippet’, 9);
biggie.bark(); // ‘Yip!’
What’s the difference between an object prototype and a function prototype?
The object prototype is just a prototype for an object.
Ex.
If bar is an object, then the object from which bar inherits is the object prototype.
The function prototype is the constructor’s prototype object, exists on ConstructorObject.prototype. Any objects created by this constructor have their prototype set to the constructor’s prototype object.
Ex:
if the constructor’s name is Foo, then Foo.prototype references the constructor’s prototype object
By default, constructor functions set the object prototype for the objects they create to Object.prototype.
T or F
F
By default, constructor functions set the object prototype for the objects they create to the constructor’s prototype object.
When does the function’s prototype get used
All functions in JavaScript (that are not arrow functions) have a prototype property. However, JavaScript only uses it when you call that function as a constructor, that is, by using the new keyword.
What does this do?
function ConstructorOfDog(breed) {
this.breed = breed;
this.apple = function() {
console.log(this);
};
}
let purple = new ConstructorOfDog(‘ads’);
let orange = purple.apple;
orange();
Logs the global object
Return an objects constructor
anObject.constructor
or the objects constructor.prototype
Object.getPrototypeOf(aDog).constructor
Is referencing a function’s .prototype.constructor always accurate?
no.
the constructor property can be reassigned like any property
Only constructor functions have prototypes.Regular functions do not have prototypes.
F
All functions in JavaScript (that are not arrow functions) have a prototype property. However, JavaScript only uses it when you call that function as a constructor, that is, by using the new keyword.
Rewrite this to work (and make sense):
let RECTANGLE = {
area: function() {
return this.width * this.height;
},
perimeter: function() {
return 2 * (this.width + this.height);
},
};
function Rectangle(width, height) {
this.width = width;
this.height = height;
this.area = RECTANGLE.area();
this.perimeter = RECTANGLE.perimeter();
}
let rect1 = new Rectangle(2, 3);
console.log(rect1.area);
console.log(rect1.perimeter);
let RECTANGLE = {
area: function() {
return this.width * this.height;
},
perimeter: function() {
return 2 * (this.width + this.height);
}
};
function Rectangle(width, height) {
this.width = width;
this.height = height;
this.area = RECTANGLE.area.call(this);
this.perimeter = RECTANGLE.perimeter.call(this);
}
let rect1 = new Rectangle(2, 3);
console.log(rect1.area); // => 6
console.log(rect1.perimeter); // => 10
What happens with this?
function Ninja() {
this.swung = true;
}
let ninja = new Ninja();
Ninja.prototype.swingSword = function() {
return this.swung;
};
console.log(ninja.swingSword());
true
What happens with this?
function Ninja() {
this.swung = true;
}
let ninja = new Ninja();
Ninja.prototype = {
swingSword: function() {
return this.swung;
},
};
console.log(ninja.swingSword());
Uncaught TypeError: ninja.swingSword is not a function
Despite the similarities to the code in the previous question, this code doesn’t work the same way. That’s because we’re reassigning Ninja.prototype to an entirely new object instead of mutating the original prototype object. The prototype for the ninja object doesn’t change; it’s still the original prototype defined during the constructor’s invocation. Thus, JavaScript can’t find the swingSword method in the prototype chain of ninja.
let ninjaA;
{
const Ninja = function() {
this.swung = false;
};
ninjaA = new Ninja();
}
// create a ninjaB
object here; don’t change anything else
console.log(ninjaA.constructor === ninjaB.constructor) // => true
let ninjaA;
{
const Ninja = function() {
this.swung = false;
};
ninjaA = new Ninja();
}
let ninjaB = new ninjaA.constructor();
console.log(ninjaA.constructor === ninjaB.constructor) // => true
Have both the constructor, factory function, and string return in the same function
function User(first, last) {
// …
}
let name = ‘Jane Doe’;
let user1 = new User(‘John’, ‘Doe’);
let user2 = User(‘John’, ‘Doe’);
console.log(name); // => Jane Doe
console.log(user1.name); // => John Doe
console.log(user2.name); // => John Doe
function User(first, last){
if (!(this instanceof User)) {
return new User(first, last);
}
this.name = first + ‘ ‘ + last;
}
let name = ‘Jane Doe’;
let user1 = new User(‘John’, ‘Doe’);
let user2 = User(‘John’, ‘Doe’);
console.log(name); // => Jane Doe
console.log(user1.name); // => John Doe
console.log(user2.name); // => John Doe
What do you call the object in relation to the constructor or factory that made it?
an instance of that constructor or factory
An instance is just another term for the objects created using any means of defining multiple objects of the same kind (e.g., dogs). The term object is more general, while instance is more specific.
What are the properties called on an instance
instance properties
What do we call the methods an instance may use (even if on a prototype)
instance methods
also instance properties but ‘methods’ to distinguish
What do we call properties or methods on the constructor?
Static properties
Status methods
What happens with this:
> new Array(3)
Creates an array of length 3
[ <3 empty items> ]
> new Array(3.1415)
=> RangeError: Invalid array length
> new Array(-5)
=> RangeError: Invalid array length
Make an array like this using Array as a constructor
an array with 3 elements that are stars
(new Array(3)).fill(‘*’)
Are the parenthesis necessary?
No. But maybe looks more clear.
> Array(1, 2, 3)
is the same as
new Array(1, 2, 3)
T or F
T
But always use new
True or false:
> let numbers = [1, 2, 3]
Object.getPrototypeOf(numbers) === Array.prototype
true
T
What happens with:
> Array.from({0: ‘a’, 1: ‘b’, 2: ‘c’, length: 3})
[‘a’, ‘b’, ‘c’]
> let str1 = ‘abc’
typeof str1
> let str2 = new String(‘xyz’)
typeof str2
‘string’
‘object’
> ‘abc’ === ‘abc’
true
> new String(‘abc’) === new String(‘abc’)
false
> let str = String(‘abc’)
typeof str
‘string’
String(‘abc’) and new String(‘abc’) are not the same!
(same with Number and Boolean)
Add a method to a built in constructor object (like Array, String, or Object)
Array.prototype.applesauce = function() {}
But avoid doing it
Call an Array prototype method on a string
let string = ‘EEE’;
Array.prototype.every.call(string, char => char === ‘E’); // => true
or
[].every.call(string, char => char === ‘E’); // => true
> new String(‘abc’) === new String(‘abc’)
String(‘abc’) === new String(‘abc’)
false
false
Have a constructor funtion create an object, but the body basically executes another contructor’s body
let Square = function(size) {
Rectangle.call(this, size, size);
}
Make a constructor function prototype, inherit from another constructor function prototype
Square.prototype = Object.create(Rectangle.prototype);
Why can’t you just go ThisConstructor.prototype = ThatConstructor.prototype
When you want some prototypal inheritance?
You can, but then ThatObject constructor doesn’t really have it’s own prototype.
Do all functions have a .prototype property?
Yes
What’s the difference between static and instance methods
Instance methods rely on each object’s specific data, while static methods must NOT rely on data from a specific object.
All objects of a particular type might have behavior that is not dependent at all on member variables; these methods are best made static. By being static, no instance of the class is required to run the method.