1

Writing some code, I wanted to do something like:

var text = Text('hello').trim().bold();

So, I did the following.

function Text(text) {
    this.value = text;

    this.trim = function() { /* trim code */ return Text(this.value); }
    this.bold = function() { /* bold code */ return Text(this.value); }

    return this;
}

Is this any different to the following?

function Text(text) {
    this.value = text;

    this.trim = function() { /* trim code */ return new Text(this.value); }
    this.bold = function() { /* bold code */ return new Text(this.value); }
}

And using the following to invoke it?

var text = new Text('hello').trim().bold();

Or are they effectively equivalent?

Frank
  • 664
  • 5
  • 15

5 Answers5

2

The fundamental point here is that this line is wrong for either given implementation of Text:

var text = Text('hello').trim().bold();

...because within Text you're storing properties on this, but when you call Text the way you have above, this is either the global object (in loose mode) or undefined (in strict mode). In loose mode, you're creating/overwriting globals called value, trim, and bold (because you're writing properties to the global object, and all properties of the global object are globals); in strict mode, you'd get an exception because you can't assign properties to undefined.

Instead, you'd have to call it like this:

var text = new Text('hello').trim().bold();
// Note ---^^^

And that should answer your question about trim and bold (e.g., you need the second version, the version that uses new). Separately, the return this; at the end is unnecessary if the function is called with new.

If you want to call Text without using new, you can, but the implementation has to be different — it has to create the object it returns, since new isn't doing that for it:

function makeText(text) {
    var obj = {};

    obj.value = text;

    obj.trim = function() { /* trim code */ return makeText(this.value); };
    obj.bold = function() { /* bold code */ return makeText(this.value); };

    return obj;
}

or more concisely:

function makeText(text) {
    return {
        value: text,
        trim: function() { /* trim code */ return makeText(this.value); },
        bold: function() { /* bold code */ return makeText(this.value); }
    };
}

Note I changed the name so it no longer started with a capital letter; in JavaScript, the overwhelming convention is that functions starting with capital letters are constructor functions (functions you call via new in the normal case).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thank you for taking the time to type this out. Is it a better idea when chaining calls to simply return `this` over `makeText(this.value);`? – Frank Feb 13 '15 at 13:10
  • @Frank: There's no general answer there, it depends on whether the method is a mutator method (one that modifies the object you call it on) or not. Let's take your `bold` example and let's say it puts `` around the text (I know, but it's just an example). If `bold` modifies `this.value` directly (`this.value = '' + this.value + '';`), then it's a mutator and returning a new `Text` object probably doesn't make sense. But if `bold` leaves the original instance's state unchanged, then you'd want something like `return makeText('' + this.value + '');` – T.J. Crowder Feb 13 '15 at 13:56
1

There is a fundamental difference. The first method is using a shared instance.

function Text(text) {
    this.value = text;

    this.trim = function() { /* trim code */ return Text(this.value); }
    this.bold = function() { /* bold code */ return Text(this.value); }

    return this;
}
var text = Text('hello').trim().bold();
var text2 = Text('hello2').trim().bold();

alert(text.value);

This will output 'hello2' (jsfiddle)

while:

function Text(text) {
    this.value = text;

    this.trim = function() { /* trim code */ return new Text(this.value); }
    this.bold = function() { /* bold code */ return new Text(this.value); }
}

var text = new Text('hello').trim().bold();
var text2 = new Text('hello2').trim().bold();

alert(text.value);

Will output 'hello' as one would expect. (jsfiddle)

I think what you really intend to do is this: (jsfiddle)

function Text(text) {
    this.value = text;

    this.duplicate = function() { 
         this.value = this.value+this.value; 
         return this; 
    }
    this.bold = function() { 
         this.value = "<b>"+this.value+"</b>"; 
         return this; 
    }
}

var text = new Text('hello').duplicate().bold();
var text2 = new Text('hello2').duplicate().bold();

alert(text.value);
Torge
  • 2,174
  • 1
  • 23
  • 33
  • Thank you. I stuck it into a [Fiddle](http://jsfiddle.net/ncmofvza/1/) to test it and what you about shared instances say is correct. – Frank Feb 13 '15 at 13:07
  • I added the fiddles to my answer as well as a way you maybe ment to solve your problem. – Torge Feb 13 '15 at 13:23
0

I think it would be better to add this method to standart Javascript String object like here for example javascript: add method to string class

Community
  • 1
  • 1
zim32
  • 2,561
  • 1
  • 24
  • 31
  • Thank you, I realize that I could have done that. However, this was merely an example for the concept I am asking about. – Frank Feb 13 '15 at 12:48
0

When invoked with new there is no difference, a constructor function implicitly returns this.

If these functions are call without new, the first one will return the global context whereas the second will return undefined.

helpermethod
  • 59,493
  • 71
  • 188
  • 276
0

What you will want to do is following, if you want to achieve chaining for the subsequent calls.

function Text(text) {
    this.value = text;

    this.trim = function() { /* trim code */ return this; }
    this.bold = function() { /* bold code */ return this; }
}

Having said that, I will want to explain what your both the codes are doing.

In your first code. You are calling the constructor function without new keyword, this makes the default context passed as this which is window. so effectively you are adding properties on the window, and the second method .bold() called will be on the window since you are returning the same object from .trim().

In your second code, you are actually creating new object when you say new Text so you are losing your previous object on which you have called .trim() method and second call to .bold is made on the new object returned from .trim().

Vishwanath
  • 6,284
  • 4
  • 38
  • 57