8

After searching for quite some time, I still haven't found what I'm looking for.

There's a fair amount of examples that either require creating a new instance, or only have functions that don't return anything (which means the problem can be solved with returning this).

I hope the following example illustrates my point well:

// Say I have these functions
function aNumber(){
    var max = 100, min = 0;
    return (Math.floor(Math.random() * (max - min + 1)) + min);
}
function divideBy(_number, _divider){
    return (_number / _divider);
}
function multiplyBy(_number, _multi){
    return (_number * _multi);
}
function add(_number, _add){
    return (_number + _add);
}
function subtract(_number, _sub){
    return (_number - _sub);
}

// #########################################################

// I can do this with them
var test = aNumber();
test = divideBy(aNumber, 2);
test = add(aNumber, 5);
test = multiplyBy(aNumber, 3);
test = subtract(aNumber, 10);

// I would like to do this however:
var test = aNumber().divideBy(2).add(5).multiplyBy(3).subtract(10);

What would be the most efficient way to make the last line work?

Am I misinformed that this is possible without creating a new instance of something?

Venkat.R
  • 7,420
  • 5
  • 42
  • 63
KJdev
  • 723
  • 1
  • 9
  • 20
  • There's nothing wrong with creating new instances, not sure why you would want to avoid that? – Bergi Jan 27 '16 at 02:09
  • You can also read this: [Chaining Methods in JavaScript](https://medium.com/@blacksrc/chaining-methods-in-javascript-4748e57ad20e) – Siamak Motlagh May 26 '19 at 01:13

6 Answers6

6

Yes, this requires changing the Prototype of an Object. Objects are instances. So you need to create an object to do this kind of thing.

function MyNum(value) {
  this._val = value;      // Having _variable is for denoting it is a private variable.
}

Initialize objects using:

var myNum = new MyNum(5);

And now using this, define these:

MyNum.prototype.divideBy = function () {}
MyNum.prototype.multiplyBy = function () {}

Don't forget to use return this; inside these functions.

Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
3

Try like below for creating without instance and prototype keyword.

One more method is been added here you can set number or random number by default. if the number not specified.

  var Calculator = {

setNumber: function(givenNumber) {
  var max = 100,
      min = 0;

  this.number = (givenNumber) ? givenNumber : (Math.floor(Math.random() * (max - min + 1)) + min);
  return this;
},

divideBy: function(_divider) {
  this.number = (this.number / _divider);
  return this;
},

multiplyBy: function(_multi) {
  this.number = (this.number * _multi);
  return this;
},

add: function(_add) {
  this.number = (this.number + _add);
  return this;
},

subtract: function(_sub) {
  this.number = (this.number - _sub);
  return this;
},

result: function () {
  return this.number;
}
  }

  document.write('<pre>');
  document.writeln(Calculator.setNumber(2).divideBy(2).add(5).multiplyBy(3).subtract(10).result());
  document.writeln(Calculator.setNumber(4).divideBy(2).add(5).multiplyBy(3).subtract(10).number);
  document.writeln(Calculator.setNumber().divideBy(2).add(5).multiplyBy(3).subtract(10).result());
  document.write('</pre>');
Venkat.R
  • 7,420
  • 5
  • 42
  • 63
1

Yes, you do need to create an instance of something. This can be a simple object literal, function constructor, etc...

The idea is that all of your methods are stored on some object, right? The only way to access those methods is to access them through that object. With this in mind, each function must RETURN the object that holds all of these methods.

A quick example

var myMethods = {
  one: function() {
    console.log('one');
    // You can return 'this' or reference the object by name
    return this;
    // or 
    // return myMethods;
  },

  two: function() {
    console.log('two');
    return this;
  }
};

myMethods.one().two().one().two();
//=> 'one', 'two', 'one', 'two'

Watch out when you reference the method directly, like so

var someMethod = myMethods.one;
someMethod() //=> undefined

This is because 'this' is now referencing the global object, which is another story for another day. Just watch out if you reference a method in this way.

Dustin Stiles
  • 1,414
  • 9
  • 12
0

Although it is generally not recommended to add functions to the prototype of JavaScript primitives, you can do what you are looking for by doing so.

function aNumber(){
    var max = 100, min = 0;
    return (Math.floor(Math.random() * (max - min + 1)) + min);
}

function divideBy(_number, _divider){
    return (_number / _divider);
}
function multiplyBy(_number, _multi){
    return (_number * _multi);
}
function add(_number, _add){
    return (_number + _add);
}
function subtract(_number, _sub){
    return (_number - _sub);
}

Number.prototype.divideBy = function(_divider){
 return divideBy(this, _divider);
};

Number.prototype.multiplyBy = function(_multi){
 return multiplyBy(this, _multi);
};

Number.prototype.add = function(_add){
 return add(this, _add);
};

Number.prototype.subtract = function(_sub){
 return subtract(this, _sub);
};

var test = aNumber().divideBy(2).add(5).multiplyBy(3).subtract(10);
Travis Schettler
  • 844
  • 1
  • 6
  • 10
  • Those extra functions look like a lot of boilerplate fluff. Why not just put the logic in the methods? – Bergi Jan 27 '16 at 02:13
  • 1
    You could, but my intention was to show a way that would work in both of the methods the OP listed. You'll see that I started with what was in the original post, then just added the functions to the primitive type. If you want to use a function in more than one way, it is more DRY to have one just reference to the function that does the work. – Travis Schettler Jan 27 '16 at 02:29
0

Just like Praveen and Venkatraman said, I found the following posts about chaining, but there all have to declare a new instanse before accessing any methods for changing method-chaining-in-javascript and beautiful-javascript-easily-create-chainable-cascading-methods-for-expressiveness

or you can use this implementation https://jsfiddle.net/ayinloya/zkys5dk6/

function aNumber() {
  var max = 100;
  var min = 0;
  this._number = (Math.floor(Math.random() * (max - min + 1)) + min);
  console.log("a init", this._number)
}

aNumber.prototype.divideBy = function(_divider) {
  this._number = (this._number / _divider)
  return this;
}

aNumber.prototype.multiplyBy = function(_multi) {
  this._number = (this._number * _multi);
  return this;
}

aNumber.prototype.add = function(_add) {
  this._number = (this._number + _add);
  return this;
}

aNumber.prototype.subtract = function(_sub) {
  this._number = (this._number - _sub);
  return this;
}
aNumber.prototype.ans = function() {
  return this._number;
}

var a = new aNumber()

alert(a.add(2).subtract(1).ans())
ayinloya
  • 134
  • 2
  • 6
0

If you don't want to pull in a library and want to have functions that are reusable (and not bind to a specific class, e.g. a Calculator). What you can do is to wrap the input into an array and then pass it through a series of map functions. In the end just take the first element and you will have your result.

function aNumber(){
  var max = 100, min = 0;
  return (Math.floor(Math.random() * (max - min + 1)) + min);
}
function divideBy(_number, _divider){
  return (_number / _divider);
}
function multiplyBy(_number, _multi){
  return (_number * _multi);
}
function add(_number, _add){
  return (_number + _add);
}
function subtract(_number, _sub){
  return (_number - _sub);
}

// #########################################################

var result = [aNumber()]
  .map(item => divideBy(item, 2))
  .map(item => add(item, 5))
  .map(item => multiplyBy(item, 3))
  .map(item => subtract(item, 10))
  [0];

console.log(result);

This probably is not the most efficient way but usually speed is "good enough".

Dharman
  • 30,962
  • 25
  • 85
  • 135
Baki
  • 564
  • 2
  • 10
  • 22