Reputation: 3937
I have reason to believe what I want is possible, but I'm not articulating correctly, and having trouble searching for it. What I am trying to achieve is as follows:
Imagine I have a generic Object obj
like so:
const obj={
job:function(){},
age:function(){},
school:function(){}
}
I want to be able to call it as chained list of object properties with variable length and then access that chain within a final method. Some examples:
js
obj.job.school.age.job.school() // in the school body, I should know 'job.school.age.job' and that I am `school`
obj.job.school() // in the school body, I should know 'job' and that I am `school`
obj.job.job.job() // in the job body, I should know 'job.job' and that I am `job`
The formatting doesn't matter. It doesn't have to be a string. The main point is that within the final method, I should know the property chain that got to me on this instance. A property should be able to be chained as a function or as a plain property.
The way I see it, there are two problems to solve. The first is the chaining part. I think I solved that part with: js
Object.setPrototypeOf(obj.job,obj)
Object.setPrototypeOf(obj.school,obj)
Object.setPrototypeOf(obj.age,obj)
That makes the object dynamically chainable at every key. So, for example it allows for:
obj.job.job.school.job.age.age.school.job.school.age.age.job()
But I'm stuck on the second part. That is to know how we got to where we are inside of the method body. So that in the above, the final job
method, should know that it is job
and on this instance the properties that preceeded it were: job.job.school.job.age.age.school.job.school.age.age
.
I hope this isn't too muddled. I'll edit if anything is unclear.
Upvotes: 2
Views: 574
Reputation: 51
I realize this question is old, but I feel like this deserves an answer - does this accomplish what you were trying to do?:
function the_obj() {
console.log(...arguments)
console.log(the_obj.accessStack)
the_obj.accessStack.splice(0, the_obj.accessStack.length)
}
the_obj.accessStack = []
Object.defineProperty(the_obj, 'job', {
get: function() {
the_obj.accessStack.push('job')
return the_obj
},
})
Object.defineProperty(the_obj, 'school', {
get: function() {
the_obj.accessStack.push('school')
return the_obj
},
})
Object.defineProperty(the_obj, 'age', {
get: function() {
the_obj.accessStack.push('age')
return the_obj
},
})
the_obj.job.school.age.job.school() // ['job', 'school', 'age', 'job', 'school']
Upvotes: 0
Reputation: 1548
As per your problem, it seems like you want to keep track of function calls along with implementing chaining. Here is a possible solution to your problem. Maybe it can be improved but it will provide you basic idea.
We need chaining behaviour, so here is how we do it.
const email_validator = {
tld: function(){ return this;},
min: function(length){ return this;},
max: function(length){ return this; }
}
Make sure to use regular functions, because they will work as expected, arrow functions will cause issues in case of web browsers.
Keep track of our chain.
const email_validator = {
// Regular functions
tld: function(){ this.addToStack(this.tld); return this;},
min: function(length){ this.addToStack(this.min); return this;},
max: function(length){ this.addToStack(this.max); return this; },
// We will keep track of chain.
stack: [],
// The function needs to be after each function call.
addToStack: function(func){ this.stack.push(func); }
}
The above code will store each function call in an array, Just make sure to call addToStack
function after every function, before returning this.
You can pass string instead of function, if you want to keep track of strings, instead of functions. Or change it as per your need.
I hope the above provides you some idea of how to do it.
Here is how to use the above code:
const email_validator = {
// Regular functions
tld: function(){ this.addToStack(this.tld); return this;},
min: function(length){ this.addToStack(this.min); return this;},
max: function(length){ this.addToStack(this.max); return this; },
// We will keep track of chain.
stack: [],
// The function needs to be after each function call.
addToStack: function(func){ this.stack.push(func); }
}
// This will contain stack of functions.
const history = email_validator.tld().tld().max().tld().stack;
// Print in format: tld.tld.max
const message = history.map(item => item.name).join(".");
console.log(message); // tld.tld.max
Upvotes: 0
Reputation: 5591
Chaining isn't magic. When you say
a.b.c()
it's really the same as
let x = a.b
x.c()
So in your case, obj
has 3 keys in it each being a function. If each function returns this
then you can chain them, but you also have to call the function - not just give its key:
const obj={
job: () => {/* do something */ return obj},
age: () => {/* do something */ return obj},
school: () => {/* do something */ return obj}
}
// you can now chain them because each returns 'obj'
obj.job().job().school().job().age().age().school().job().school().age().age().job();
// but doing this won't work since obj.job returns the job function itself, not obj
obj.job.job();
// gives: Uncaught TypeError: obj.job.job is not a function
Upvotes: 0