I'm creating a JavaScript library. I've been trying to implement chaining.
0: What I first came up with:
function V(p) {
return {
add : function(addend) { return V(p + addend); },
sub : function(subtra) { return V(p - subtra); },
};
}
Using this method I can chain easily:
V(3).add(7).sub(5) // V(5)
Unfortunately the result is always a wrapped V() function, I am unable to extract the resulting value this way. So I thought about this problem a bit and came up with two semi-solutions.
1: Passing flag to last method
function V(p, flag) {
if(flag)
return p;
else
return {
add : function(addend, flag) { return V(p + addend, flag); },
sub : function(subtra, flag) { return V(p - subtra, flag); }
};
}
Using this method I can end the chain by passing a flag to the last method I use:
V(3).add(7).sub(5, true) // 5
While this works just fine, it requires some code repetition and makes chaining less readable and my code less elegant.
2: Using start() and end() methods
_chain = false;
function V(p) {
function Wrap(w) {
return (_chain) ? V(w) : w;
}
return {
add : function(addend) { return Wrap(p + addend); },
sub : function(subtra) { return Wrap(p - subtra); },
start : function() { _chain = true; },
end : function() { _chain = false; return p; }
};
}
Using this method you can do single operations with no more code:
V(3).add(7) // 10
But chaining requires two more methods, making things a lot less readable:
V(3).start().add(7).sub(5).end() // 5
So basically I'm just searching for the best way to implement chaining into my library. Ideally I'm looking for something where I can use any number of methods and don't need to terminate the chain in inelegant ways.
V(3).add(7).sub(5) // 5, perfect chaining
I'm creating a JavaScript library. I've been trying to implement chaining.
0: What I first came up with:
function V(p) {
return {
add : function(addend) { return V(p + addend); },
sub : function(subtra) { return V(p - subtra); },
};
}
Using this method I can chain easily:
V(3).add(7).sub(5) // V(5)
Unfortunately the result is always a wrapped V() function, I am unable to extract the resulting value this way. So I thought about this problem a bit and came up with two semi-solutions.
1: Passing flag to last method
function V(p, flag) {
if(flag)
return p;
else
return {
add : function(addend, flag) { return V(p + addend, flag); },
sub : function(subtra, flag) { return V(p - subtra, flag); }
};
}
Using this method I can end the chain by passing a flag to the last method I use:
V(3).add(7).sub(5, true) // 5
While this works just fine, it requires some code repetition and makes chaining less readable and my code less elegant.
2: Using start() and end() methods
_chain = false;
function V(p) {
function Wrap(w) {
return (_chain) ? V(w) : w;
}
return {
add : function(addend) { return Wrap(p + addend); },
sub : function(subtra) { return Wrap(p - subtra); },
start : function() { _chain = true; },
end : function() { _chain = false; return p; }
};
}
Using this method you can do single operations with no more code:
V(3).add(7) // 10
But chaining requires two more methods, making things a lot less readable:
V(3).start().add(7).sub(5).end() // 5
So basically I'm just searching for the best way to implement chaining into my library. Ideally I'm looking for something where I can use any number of methods and don't need to terminate the chain in inelegant ways.
V(3).add(7).sub(5) // 5, perfect chaining
Share
Improve this question
asked Mar 20, 2011 at 18:34
user523285user523285
2
|
3 Answers
Reset to default 12Why not introducing a private variable and work on that? I guess that is even more convenient. Plus it's probably a good idea to have a pure "getter" that finally returns the computed value. This could look like this:
function V(p) {
var value = p;
return {
add: function(addend) {
value += addend;
return this;
},
sub: function(subtra) {
value -= subtra;
return this;
},
get: function() {
return value;
}
};
}
console.log(V(3).add(7).sub(5).get()); // 5
You cannot return the Object
in a getter function obviously. So you need some method where the chaining ends and returns a value.
In some cases it does need to have something similar to end
, but in your simple arithmetic example, it does not.
function V(initial_val){
if(!(this instanceof V)){
return new V(initial_val);
}
var num = initial_val || 0;
this.set = function(val){
num = val;
return this;
}
this.add = function(val){
num += val;
return this;
}
this.sub = function(val){
num -= val;
return this;
}
this.valueOf = function(){
return num;
}
this.toString = function(){
return ""+num;
}
}
By adding valueOf
and toString
functions to the object, you can access its primitive value. That is, you can do something like:
var num = V(0).add(1).sub(2), another_num = 3 + num; // num = -1 and another_num = 2;
I would amend Haochi's excellent answer as follows :
Using the prototype will be more efficient if you have many V objects and in the toString function I invoke the generic number toString with whatever arguments you care to give it.
function V (n) {
if (!(this instanceof V)) {
return new V (n);
}
this.num = +n || 0;
return this;
}
V.prototype = {
set: function (val) {
this.num = val;
return this;
},
add: function (val) {
this.num += val;
return this;
},
sub: function (val) {
this.num -= val;
return this;
},
valueOf: function () {
return this.num;
},
toString: function () {
return this.num.toString.apply (this.num, arguments);
}
}
underscore.js
implements your second one with .chain – Raynos Commented Mar 20, 2011 at 18:55