Created
September 4, 2020 20:22
-
-
Save YozhEzhi/fe587368283c79eabc6d4a98a79de9fd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Factory Functions. | |
*/ | |
/* | |
* ES6 class vs Factory function: | |
* With class -- Be wary | |
*/ | |
class Dog { | |
constructor() { | |
this.sound = 'woof'; | |
} | |
talk() { | |
console.log(this.sound); | |
} | |
} | |
const sniffles = new Dog(); | |
sniffles.talk(); // Outputs: 'woof' | |
/* | |
* Here's the issue. | |
* This will not work since - the `this` in talk() now refers to the DOM | |
* element selected by $(button) and not sniffles. | |
*/ | |
$('button').click(sniffles.talk); | |
// Workaround -- explicit binding. | |
$('button').click(sniffles.talk.bind(sniffles)); | |
/* | |
* Or in ES6 -- `this` inside an arrow function is always inherited | |
* from the enclosing scope. | |
*/ | |
$('button').click(() => sniffles.talk()); | |
// Factory function: | |
const dog = () => { | |
const sound = 'woof'; | |
return { | |
// We are not using `this` at all. | |
talk: () => console.log(sound), | |
}; | |
}; | |
const sniffles = dog(); | |
sniffles.talk(); // Outputs: 'woof' | |
$('button').click(sniffles.talk); // Works -- Outputs: 'woof' | |
/* | |
* Constructor function vs Factory function. | |
* The basic difference is that a constructor function is used with the `new` keyword: | |
*/ | |
const objFromConstructor = new ConstructorFunction(); | |
// A factory function is called like a "regular" function: | |
const objFromFactory = factoryFunction(); | |
/* | |
* But for it to be considered a "factory" it would need to return a new instance | |
* of some object: you wouldn't call it a "factory" function if it just returned | |
* a boolean or something. | |
* This does not happen automatically like with new, but it does allow more | |
* flexibility for some cases. In a really simple example the functions | |
* referenced above might look something like this: | |
*/ | |
function ConstructorFunction() { | |
this.someProp1 = '1'; | |
this.someProp2 = '2'; | |
} | |
ConstructorFunction.prototype.someMethod = function () { | |
/* whatever */ | |
}; | |
function factoryFunction() { | |
return { | |
someProp1: '1', | |
someProp2: '2', | |
someMethod() { | |
/* whatever */ | |
}, | |
/* | |
* someMethod() inside obj would lead to each object returned hold | |
* a different copy of someMethod which is something that we might not want. | |
* This is where using `new` and `prototype` inside the factory function would help. | |
*/ | |
}; | |
} | |
// Factory functions: Encapsulation using private properties. | |
function Car() { | |
// private variable | |
const location = 'Denver'; // PRIVATE | |
function year() { | |
// PRIVATE | |
self.year = new Date().getFullYear(); | |
} | |
var self = { | |
make: 'Honda', | |
model: 'Accord', | |
color: '#cc0000', | |
paint(color) { | |
self.color = color; | |
}, | |
}; | |
if (!self.year) { | |
year(); | |
} | |
return self; | |
} | |
const myCar = Car(); | |
/* | |
* Factory functions: Dynamic objects. | |
* Since we can have public/private functions we can use if/else statements | |
* to easily manipulate our object structure. | |
* This gives ultimate flexibility to allow the root function ambiguity | |
* and allow parameters to determine what the object returned should be. | |
*/ | |
function Address(param) { | |
let self = {}; | |
if (param === 'dev') { | |
self = { | |
state: 'Colorado', | |
saveToLog() { | |
// write info to a log file | |
}, | |
}; | |
} else { | |
self = { | |
state: 'Colorado', | |
}; | |
} | |
return self; | |
} | |
const devAddress = Address('dev'); | |
const productionAddress = Address(); | |
/** | |
* Getters and Setters. | |
*/ | |
/* | |
* Using getters and setters -- Using Object.defineProperty | |
* When you define a property this way, you can do much more than just define a setter or getter. You may also pass following keys: | |
* configurable (false by default): if this is true, the property's configuration will be modifiable in future. | |
* enumerable (false by default): if true, the property will appear when looping over the object (for (var key in obj)). | |
*/ | |
(function () { | |
const person = { | |
firstName: 'Jimmy', | |
lastName: 'Smith', | |
}; | |
Object.defineProperty(person, 'fullName', { | |
get() { | |
return `${firstName} ${lastName}`; | |
}, | |
set(name) { | |
const words = name.split(' '); | |
this.firstName = words[0] || ''; | |
this.lastName = words[1] || ''; | |
}, | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment