Reputation:
I ran across some code I'm trying to refactor where they were writing JavaScript as a string and putting it inside HTML script tags, then writing it to the DOM. Very ugly and not maintainable. But one of the things this allowed them to do was build a function call by appending to a string.
var methods = '';
for (key in obj) {
methods += 'func1("'+key+'", "'+obj[key]+'").';
}
var scriptString = '<script>func2().' + methods + 'func3();</script>'
The result could be:
'<script>func2().func1("key1", "value1").func1("key2", "value2").func3();</script>'
So, since I really disapprove of writing JavaScript inside an HTML string inside of JavaScript ... Does anyone know how to accomplish the same result with pure JavaScript? Is there a way to continuously append methods to a function call by iterating over an object?
Upvotes: 1
Views: 2120
Reputation: 4773
Array.reduce()
should do most of what you need.
The tricky part is the .func1()
method call chain, where you depend on an object's key/value pairs. If you're not used to working with the Array.reduce()
method, I would suggest reading through the MDN documentation, but it basically loops through an array, performing a transformation on the previous result until it reaches the end, where it returns the final result. This can be used to our advantage since method chaining is just a method call on the previous method call's return value. But, since that's an array method, we need to get the Object's entries into an array first...and that's where Object.entries()
comes in.
Note that much of my syntax here involves new features, which may or may not be supported by your target browsers. Be sure to use a transpiler and polyfills to handle going backwards, if needed.
See below for an example:
const wrapperFunc = (obj) => {
// Start with func2()
const func2Result = func2()
// Chain to func1() for each entry in obj (the tricky part)
const func1Result = Object.entries(obj).reduce(
// Call func1 method on previous result to get the next result
(prevResult, [ key, val ]) => prevResult.func1(key, val),
// Initial "prevResult" value used above
func2Result
)
// Chain to func3()
return func1Result.func3()
}
// Inject object to be used for func1() calls
wrapperFunc({
key1: 'value1',
key2: 'value2'
})
Also, here's a second, more complex example with some implemented methods. Unlike the example above, this one actually runs.
class MyObject {
constructor() {
this.innerString = ''
}
// Chainable method (returns this)
func1(key, val) {
this.innerString += `${key}, ${val} `
return this
}
func3() {
return this.innerString.trim()
}
}
const func2 = function () {
return new MyObject()
}
const wrapperFunc = (obj) => {
// Start with func2()
const func2Result = func2()
// Chain to func1() for each entry in obj (the tricky part)
const func1Result = Object.entries(obj).reduce(
// Call func1 method on previous result to get the next result
(prevResult, [ key, val ]) => prevResult.func1(key, val),
// Initial "prevResult" value used above
func2Result
)
// Chain to func3()
return func1Result.func3()
}
// Inject object to be used for func1() calls
console.log(wrapperFunc({
key1: 'value1',
key2: 'value2'
}))
Upvotes: 2
Reputation: 33486
You can create a function that calls the func1
method repeatedly for all key/value pairs in the object.
var script = function(obj) {
var value = func2();
for (var key in obj) {
value = value.func1(key, obj[key]);
}
value.func3();
};
Upvotes: 0