JavaScript extending types return

1.6k views Asked by At

I'm actually studying Crockford's Javascript: the good parts. I am new to JavaScript so I'm having a difficult time to understand how this code works:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

Here's what I think:

Being a method (a function inside an object), this points to the Function object, but why the need to return the object since i'm having access on it from inside the method? If i am right, this is a reference, not a local copy, so:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
};

Should work as well.

From the other side, in JavaScript a function without a return statement returns undefined and assign it to Function.prototype.method.

The Question

What's the point of returning this?


Working example #1

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};
var add = function(a, b) {
    return a+b;
};

Function.method('add', add);
var f = function() {};

print(f.add(1,2));

Number.method('integer', function () {
        return Math[this < 0 ? 'ceil' : 'floor'](this);
        });

print((-10/3).integer());

Output:

-3 3


Working Example #2

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
};

var add = function(a, b) {
    return a+b;
};

Function.method('add', add);
var f = function() {};

print(f.add(1,2));

Number.method('integer', function () {
        return Math[this < 0 ? 'ceil' : 'floor'](this);
        });

print((-10/3).integer());

Output:

-3 3

3

There are 3 answers

5
Maxym On BEST ANSWER

let me try to explain it. I didn't read that book, but an article Classical Inheritance in JavaScript of Douglas Crockford has one important sentence, related to that example about Function.prototype.method:

It returns this. When I write a method that doesn't need to return a value, I usually have it return this. It allows for a cascade-style of programming.

actually I am not familiar with that term, I think well known term is "Fluent Interface" or "Method Chaining", read that wiki page, there are examples in different languages, so you will understand it..

PS. @Gianluca Bargelli was a bit faster to provide example of using Function.prototype.method this way, so I don't post it in my answer

ADDON: how you can use it in terms of your example:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
}

Number.method('integer', function () {  // you add 'integer' method
        return Math[this < 0 ? 'ceil' : 'floor'](this);
        })
      .method('square', function () {  // you add 'square' method with help of chaining
        return this * this;
        });

console.info( (-10/3).integer().square() ); // <- again chaining in action

you see, integer() returns Number object, so you can call another method, instead of writing:

var a = (-10/3).integer();
console.info( a.square() ); 

and few words about my way of using it, most of time I prefer to write "each method - new line with indent, for me this way is more readable:

Function.method('add', add)
        .method('sub', sub)
        .method('mul', mul)
        .method('div', div);

this way I see where I start, and "new line/indent" tell me that I still modify that object. Compare it to long line:

Function.method('add', add).method('sub', sub).method('mul', mul).method('div', div);

or typical approach:

Function.method('add', add);
Function.method('sub', sub);
Function.method('mul', mul);
Function.method('div', div);

ADDON2: Usually I use this approach (Fluent interface pattern) when I work with entities e.g. Java code:

public class Person {
  private String name;
  private int age;
  ..

  public String getName() {
    return this.name;
  }

  public Person setName( String newName ) {
    this.name = newName;
    return this;
  }

  public int getAge() {
    return this.age;
  }

  public Person setAge( int newAge ) {
    this.age = newAge;
    return this;
  }

  ..
}

it allows me to construct Person object in easy way:

Person person = new Person().setName("Leo").setAge(20);

Some people make it a bit different, they add new kind of methods to set/get, and call it with :

public class Person {
  private String name;
  private int age;
  ..

  public String getName() {
    return this.name;
  }

  public void setName( String newName ) {
    this.name = newName;
  }

  public Person withName( String newName ) {
    this.setName( newName ); // or this.name = newName; up to you
    return this;
  }

  public int getAge() {
    return this.age;
  }

  public void setAge( int newAge ) {
    this.age = newAge;
  }

  public Person withAge( int newAge ) {
    this.setAge( newAge ); // or this.age = newAge; up to you
    return this;
  }
  ..
}

Now my previous example looks like:

Person person = new Person().withName("Leo").withAge(20);

This way we don't change meaning of set method (I mean we do not enhance it, so it work as most of developers expect... at least that people do not expect that set method can return anything ;) ). One interesting thing about these special methods - they can loose their self-documenting, but they improve readability when you use them (like in example with Person creation, withName tells very well what exactly we are doing..

to read more:
FluentInterface - Martin Fowler's description of that pattern
Fluent Interfaces in PHP
The Weekly Source Code 14 - Fluent Interface Edition - as for me short and good enough to see pros and cons (as well as links to other resources)

3
zozo On

I don't understand exactly what you ask but if you don't return anything you will assign nothing to Function.prototype.method, witch kind of make that sentence useless don't you think?

0
Gianluca Bargelli On

I have sent an email to Douglas Crockford this afternoon with this question and his reply was:

F.method(a).method(b).method(c)

I'm not joking. This was the only thing he wrote.

Anyway, my personal intepretation of his (cryptic) answer is Chain method creation:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this; //This returns the same Function object into the chain below
};

var add = function (a, b) { return a+b; };
var sub = function (a, b) { return a-b; };
var mul = function (a, b) { return a*b; };
var div = function (a, b) { return a/b; };

Function.method('add', add).method('sub', sub).method('mul', mul).method('div', div);

That is, instead of creating new methods using one line at time, it is possible to re-apply the next method in the chain on the return object of the previous, Function.

In this example, the chain goes from left to right:

|Function|--method-->|add|--returns-->|Function|--method-->|sub|--returns-->|Function|--method-->|mul|--returns-->|Function|--method-->|div|-->returns-->|Function|