I don't know JS/ES6 well enough to describe my question in code. So most of this question is conceptually and in pseudo code.
Say I have a Contractor class like this:
class Contractor {
constructor(jobFn) {
// save jobFn;
}
dailyRoutine() {
// let result = DriveToWork()
const result = 6
DoTheJob(result)
DriveBackHome()
}
}
The problem is, what the DoTheJob() does might be different things in different places.
So in place A, it could be
he = new Contractor(write_front_end(with, this, and that))
And in place B, it could be
he = new Contractor(fix_backend_node(with, express))
I.e., the behavior need to be passed in during the constructor, and the action might need to take different kind and different amount of parameters.
Would such thing be possible with ES6?
Please show ES6 code that can pass function with different kind and different amount of parameters through the constructor to DoTheJob().
Further, the challenge is that the jobFn need to be a Curried function, meaning there is one or more parameter missing to do the DoTheJob job. Say if the jobFn is passed with Curried add(3), then DoTheJob will do UncurriedAdd of add(3, 6); if then jobFn is passed with Curried multiple(5), then DoTheJob will do Uncurried of multiple(5, 6);
Just assign the passed function to this.DoTheJob, and then call this.DoTheJob inside dailyRoutine:
class Contractor {
constructor(jobFn) {
this.DoTheJob = jobFn;
}
dailyRoutine() {
// DriveToWork()
this.DoTheJob();
// DriveBackHome()
}
}
const c1 = new Contractor(() => console.log('doing job A'));
c1.dailyRoutine();
const c2 = new Contractor(() => console.log('doing job B'));
c2.dailyRoutine();
// c1 again:
c1.dailyRoutine();
// feel free to reference any in-scope variables in the passed function,
// no need to pass the variables as additional parameters
const data = 'data';
const c3 = new Contractor(() => console.log('data is', data));
c3.dailyRoutine();
If dailyRoutine needs to be invoked with data that needs to be sent to the passed doTheJob function, just define the needed arguments in the function you pass, there's no need for actual currying here:
class Contractor {
constructor(jobFn) {
this.DoTheJob = jobFn;
}
dailyRoutine(doJobArg) {
this.DoTheJob(doJobArg);
}
}
// feel free to reference any in-scope variables in the passed function,
// no need to pass the variables as additional parameters
const data = 'data';
const c3 = new Contractor((arg) => console.log('data is', data, 'and arg is', arg));
c3.dailyRoutine('argDoTheJobIsCalledWith');
In my case, I may advise you that it's better to give the predicate to dailyRoutine, because this way you'll be able to reuse the same instance and give different predicates.
Anyway, there's a pure OOP solution for this, using method polymorphism, the JavaScript way (aka duck typing):
class Contractor {
driveBackHome() {}
dailyRoutine() {
const result = 6
this.doTheJob(result)
this.driveBackHome()
}
}
class SpecializedContractorA extends Contractor {
doTheJob(result) {
console.log('aaaaaaaaaaaaaaaaaaaaa', result)
}
}
class SpecializedContractorB extends Contractor {
doTheJob(result) {
console.log('bbbbbbbbbbbbbbbb', result)
}
}
const a = new SpecializedContractorA()
a.dailyRoutine()
const b = new SpecializedContractorB()
b.dailyRoutine()
Related
I was learning from an ES6 essential course and trying default parameters and rest operator for functions.
I have defined a function sayHi as below with default parameters and then rest operator which does not gives the desired output.
const sayHi = (greetings, ...names) => {
names.forEach(item => {
console.log(`${greetings} ${item}`);
});
}
sayHi('Hi', 'Ahsan', 'Awais', 'Haseeb');
The above snippet works as desired. but when I tried to set a default parameter value for greetings variable it works but gives unwanted result i.e. value 'Ahsan' is taken by the greetings variable.
const sayHi = (greetings = ' Hi', ...names) => {
names.forEach(item => {
console.log(`${greetings} ${item}`);
});
}
sayHi('Ahsan', 'Awais', 'Haseeb');
Is there a way I can set default parameters in function like above before rest operator?
You can't, no. The rest parameter only picks up the "rest" of the parameters not consumed by any previous formal parameters, so greeting will always receive the first argument's value.
Separately, since both the names and the greeting have the same type, you can't provide a default at all if you want to accept the names that way.
A couple of options for you:
A curried function
You could have a function that accepts the greeting and returns a function that uses it with whatever you pass it:
const greet = (greeting = "Hi") => (...names) => {
for (const name of names) {
console.log(`${greeting} ${name}`);
}
};
greet()("Ahsan", "Awais", "Haseeb");
greet("Hello")("Ahsan", "Awais", "Haseeb");
Note how we called that:
greet()("Ahsan", "Awais", "Haseeb");
greet() creates the function using the default greeting. Then we call that function by using ("Ahsan", "Awais", "Haseeb") on it. greet("Hello") creates a function that uses the greeting "Hello" (and then we call it).
(I also took the liberty of using for..of rather than forEach, but it's a matter of style.)
Take names as an array
Another option is to accept the names as an array. That way, we can tell inside the function whether we got a greeting or not:
const greet = (greeting, names) => {
if (Array.isArray(greeting)) {
names = greeting;
greeting = "Hi";
}
for (const name of names) {
console.log(`${greeting} ${name}`);
}
};
greet(["Ahsan", "Awais", "Haseeb"]);
greet("Hello", ["Ahsan", "Awais", "Haseeb"]);
you just have to pass undefined in the 1st parameter to skip optional arg
think of it like this greetings = typeof greetings != 'undefined' ? greetings : "hi" which means that check the value of greetings and if it's undefined (not provided) use the default value
edit: here is the code snippet
const sayHi = (greetings = ' Hi', ...names) => {
names.forEach(item => {
console.log(`${greetings} ${item}`);
});
}
sayHi(undefined, 'Ahsan', 'Awais', 'Haseeb');
since JS does not support abstract classes or inheritance, every time we want to add a new type to be created when using factory pattern, we will have to modify the code which mean we violate the open-close principle. for example, in the snapshot bellow - if we want to add a new employee type like Marketing, we will have to update the switch statement which is violation of the open-close principle. Is there any workaround to use the factory pattern without violation open-close principle?
function Accountant(){
console.log('I am accountant');
}
function Developer(){
console.log('I am developer');
}
function Sales(){
console.log('I am sales');
}
function CreateEmployee(employee){
switch(employee){
case('accountant'): return new Accountant();
case('developer'): return new Developer()
case('sales'): return new Sales();
}
}
if we want to add a new employee type, we will have to update the switch statement which is violation of the open-close principle.
No, it doesn't. The OCP is not about forbidding to update code. If we want to implement a new feature, we of course need to touch the code of the program. The OCP is about designing your interfaces so that your program can be easily extended without changing code all over the place - ideally you only have to provide the new code, and change the configuration of the program to make use of it - or if this is not configurable, change only the high-level building blocks.
I would argue that the factory pattern even facilitates application of the OCP - don't think about changes to the factory function, think about the modules that use it. Instead of changing all the code in all the modules that instantiates employee objects, all you need to do is to supply a different factory to them.
A step in the right direction is to create an employeeType object that holds the constructors:
const employeeType = {
Accountant,
Developer,
Salesperson
};
// console.log(new (employeeType["Accountant"])());
// Abstract away the hard-coded type above; the below randomization is strictly for demo purpose
const employeeTypeKeys = Object.keys(employeeType);
const employeeTypeIndex = Math.floor(employeeTypeKeys.length * Math.random());
console.log(new (employeeType[employeeTypeKeys[employeeTypeIndex]])());
function Accountant(){
console.log('I am an accountant');
}
function Developer(){
console.log('I am a developer');
}
function Salesperson(){
console.log('I am a salesperson');
}
I would argue the pattern isn't exactly something I'd like to extend but it's possible.
Say you have access to the CreateEmployee function alone and you'd want to extend it so you can also add Engineers.
import CreateEmployee from "./employee.js";
function Engineer(){
console.log("I'm an Engineer");
}
function CreateEmployeeAndEngineer(employeeType){
if(employeeType === 'Engineer') return new Engineer();
else {
return CreateEmployee(employeeType);
}
}
Simple (ehh... not really) function composition.
However, there's very little value for it in Javascript since it's untyped. Then of course functions, and therefore constructors, are first-class citizens and can be easily passed down to the new operator.
since JS does not support abstract classes or inheritance
Javascript does support inheritance through it's concept of prototype chain.
You could implement the Factory method pattern if you'd want.
If you want new instances to be created on the fly, you should use Object literals. Review the following design for an idea on how you may, or may not, want to go about this:
function ucFirst(string){
const s = string.split('');
return s.shift().toUpperCase()+s.join('');
}
function InstanceController(){
this.instances = [];
this.add = (name, obj)=>{
this.instances.push({name:name, obj:obj});
return this;
}
this.get = name=>{
for(let o of this.instances){
if(o.name === name){
return o.obj;
}
}
return false;
}
this.remove = name=>{
for(let i=0,a=this.instances,l=a.length; i<l; i++){
if(a[i].name === name){
a.splice(i, 1);
break;
}
}
return this;
}
}
const ic = new InstanceController;
const data1 = {
data:'could be from database',
more:'sure there can be more data',
numberTest: 2
}
const data2 = {test:'just a test'};
ic.add('developer', data1).add('accountant', {testing:'see'});
let dev = ic.get('developer'), aco = ic.get('accountant');
if(dev)console.log(dev);
if(aco)console.log(aco);
console.log(ic.get('nope'));
ic.remove('accountant'); aco = ic.get('accountant');
console.log(aco);
I'm using array value as variable and then call the function N method, how I get them in function N.
I really want to simulate the Javascript array method, I don't want to use parameters to achieve it. For example,
var p1 = [1,2,3,4,5]; p1.push(6);
function _Array() {
this._this = this;
}
_Array.prototype.show = function () {
this._this.forEach(function(item){alert(item);}) //how to print 1,2,3,4,5
};
var p1 = [1,2,3,4,5];
p1 = new _Array();
//p1._Array.call(p1); //not work
// new _Array().show.call(p1); //not work
// p1.show(); //not work
You have to store that in the instance
function N(arr) {
this._this = arr
}
N.prototype.say = function () {
this._this.forEach(function (item) {
console.log(item)
})
}
p1 = new N([1, 2, 3, 4, 5])
p1.say()
If you are insistent on wanting to write a method that takes the array by reference, you can modify the array prototype like so:
Array.prototype.show = function() {
this.forEach(item => alert(item));
}
However, it is a VERY BAD IDEA to modify the built in object prototypes, as this can cause conflicts with external libraries implementing their own "show" function that is being used differently, or cause incompatibilities with future versions of JS that implements this method.
It would be far more prudent in most situations to pass the array as a parameter, unless you have a very specific reason why you're not doing so. In that case, you should at least prefix the method with some sort of project identifier to minimize the chances of conflicts occurring.
I am a relative beginner in Angular, and I am struggling to understand some source I am reading from the ng-bootstrap project. The source code can be found here.
I am very confused by the code in ngOnInit:
ngOnInit(): void {
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
const results$ = letProto.call(inputValues$, this.ngbTypeahead);
const processedResults$ = _do.call(results$, () => {
if (!this.editable) {
this._onChange(undefined);
}
});
const userInput$ = switchMap.call(this._resubscribeTypeahead, () => processedResults$);
this._subscription = this._subscribeToUserInput(userInput$);
}
What is the point of calling .call(...) on these Observable functions? What kind of behaviour is this trying to achieve? Is this a normal pattern?
I've done a lot of reading/watching about Observables (no pun intended) as part of my Angular education but I have never come across anything like this. Any explanation would be appreciated
My personal opinion is that they were using this for RxJS prior 5.5 which introduced lettable operators. The same style is used internally by Angular. For example: https://github.com/angular/angular/blob/master/packages/router/src/router_preloader.ts#L91.
The reason for this is that by default they would have to patch the Observable class with rxjs/add/operators/XXX. The disadvantage of this is that some 3rd party library is modifying a global object that might unexpectedly cause problems somewhere else in your app. See https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#why.
You can see at the beginning of the file that they import each operator separately https://github.com/ng-bootstrap/ng-bootstrap/blob/master/src/typeahead/typeahead.ts#L22-L25.
So by using .call() they can use any operator and still avoid patching the Observable class.
To understand it, first you can have a look at the predefined JavaScript function method "call":
var person = {
firstName:"John",
lastName: "Doe",
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var myObject = {
firstName:"Mary",
lastName: "Doe",
}
person.fullName.call(myObject); // Will return "Mary Doe"
The reason of calling "call" is to invoke a function in object "person" and pass the context to it "myObject".
Similarly, the reason of this calling "call" below:
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is providing the context "this._valueChanges", but also provide the function to be called base on that context, that is the second parameter, the anonymous function
value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
}
In the example that you're using:
this._valueChanges is the Input Event Observerable
The _do.call is for doing some side affects whenever the event input happens, then it returns a mirrored Observable of the source Observable (the event observable)
UPDATED
Example code: https://plnkr.co/edit/dJNRNI?p=preview
About the do calling:
You can call it on an Observable like this:
const source = Rx.Observable.of(1,2,3,4,5);
const example = source
.do(val => console.log(`BEFORE MAP: ${val}`))
.map(val => val + 10)
.do(val => console.log(`AFTER MAP: ${val}`));
const subscribe = example.subscribe(val => console.log(val));
In this case you don't have to pass the first parameter as the context "Observable".
But when you call it from its own place like you said, you need to pass the first parameter as the "Observable" that you want to call on. That's the different.
as #Fan Cheung mentioned, if you don't want to call it from its own place, you can do it like:
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
I suppose
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is equivalent to
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
In my opinion it's not an usual pattern(I think it is the same pattern but written in different fashion) for working with observable. _do() in the code is being used as standalone function take a callback as argument and required to be binded to the scope of the source Observable
https://github.com/ReactiveX/rxjs/blob/master/src/operator/do.ts
Background
We have a request object that contains information. That specific object has a field called partnerId which determines what we are going to do with the request.
A typical approach would be a gigantic if/then/else:
function processRequest( request ){
if( request.partnerId === 1 ){
//code here
}else if( request.partnerId === 23 ){
//code here
}
//and so on. This would be a **huge** if then else.
}
This approach has two main problems:
This function would be huge. Huge functions are a code smell (explaining why next) but mainly they become very hard to read and maintain very quickly.
This function would do more than one thing. This is a problem. Good coding practices recommend that 1 function should do only 1 thing.
Our solution
To bypass the previous problems, I challenged my co-worker to come up with a different solution, and he came up with a function that dynamically builds the name of the function we want to use and calls it. Sounds complicated but this code will clarify it:
const functionHolder = {
const p1 = request => {
//deals with request
};
const p23 = request => {
//deals with request
};
return { p1, p23 };
};
const processRequest = request => {
const partnerId = request.partnerId;
const result = functionHolder[`p${partnerId}`](request);
return result;
};
Problems
This solution has advantages over the previous one:
There is no main function with an huge gigantic if then else.
Each execution path is not a single function that does one thing only
However it also has a few problems:
We are using an object functionHolder which is in reality useless. p1 and p23 don't share anything in common, we just use this object because we don't know how else we can build the function's name dynamically and call it.
There is no else case. If we get an incorrect parameter the code blows.
Out eslint with rule non-used-vars complains that p1 and p23 are not being used and we don't know how to fix it ( https://eslint.org/docs/rules/no-unused-vars ).
The last problem, gives us the impression that perhaps this solution is not so great. Perhaps this pattern to avoid an if then else has some evil to it that we are yet to find.
Questions
Is there any other pattern we can use to avoid huge if then else statements ( or switch cases )?
Is there a way to get rid of the functionHolder object?
Should we change the pattern or fix the rule?
Looking forward to any feedback!
You can get rid of the unused variables by never declaring them in the first place:
const functionHolder = {
p1: request => {
//deals with request
},
p23: request => {
//deals with request
};
};
const processRequest = request => {
const partnerId = request.partnerId;
const method = functionHolder[`p${partnerId}`]
if(method) // Is there a method for `partnerId`?
return method(request);
return null; // No method found. Return `null` or call your default handler here.
};
To answer your points:
Yeap, as shown above.
Not without some kind of object.
That's up to you. Whatever you prefer.
Perhaps I'm not understanding the question properly, but why not an object to hold the methods?
const functionHolder = {
1: function(request) {
// do something
},
23: function(request) {
// do something
},
_default: function(request) {
// do something
}
}
function processRequest(request) {
(functionHolder[request.partnerId] || functionHolder._default)(request)
}
Explanation:
The object functionHolder contains each of the methods used to deal with a given request.
The keys of functionHolder (e.g. 1) correspond directly to the values of request.partnerId, and the values of these members are the appropriate methods.
The function processRequest "selects" the appropriate method in functionHolder (i.e. object[key]), and calls this method with the request as the parameter (i.e. method(parameter)).
We also have a default method, under the key _default, if request.partnerId does not match any existing key. Given a || b; if a is "falsy", in this case undefined (because there is no corresponding member of the object), evaluate to b.
If you are concerned about making functionHolder "bloated", then you can separate each of the methods:
const p1 = request => {
// do something
}
const p23 = request => {
// do something
}
const _default = request => {
// do something
}
And then combine them into a "summary" object of sorts.
const functionHolder = {
1: p1,
23: p23,
_default: _default
}
processRequest remains the same as above.
This adds a lot of global variables though.
Another advantage is you can import / change / declare methods on the fly. e.g.
functionHolder[1] = p1b // where p1b is another request handler (function) for `request.partnerId` = 1
functionHolder[5] = p5 // where p5 is a request handler (function) that has not yet been declared for `request.partnerId` = 5
Combining the above, without having to declare many global variables while also being able to separate the declaration of each method:
const functionHolder = {}
functionHolder._default = request => {
// do something
}
functionHolder[1] = request => {
// do something
}
functionHolder[23] = request => {
// do something
}
processRequest remains the same as above.
You just have to be sure that the methods are "loaded in" to functionHolder before you call processRequest.