最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Best way to implement Javascript chaining in a library - Stack Overflow

programmeradmin1浏览0评论

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
  • 1 I'd suggest looking at the way jQuery has implemented chaining. – Livingston Samuel Commented Mar 20, 2011 at 18:48
  • 1 You'll find that underscore.js implements your second one with .chain – Raynos Commented Mar 20, 2011 at 18:55
Add a comment  | 

3 Answers 3

Reset to default 12

Why 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);
  }
}
发布评论

评论列表(0)

  1. 暂无评论