oopconstructorecmascript-5

Simulating an auxiliary constructor in ECMAScript 5


I was going through "JavaScript the definitive guide" and I encountered this auxiliary constructor (below their Set definition):

// Definition of Set, taken from chapter 9.6.1
function Set() {
    this.values = {};
    this.n = 0;
    this.add.apply(this, arguments);
}

Set.prototype.add = function() {
    for(var i = 0; i < arguments.length; i++) {
        var val = arguments[i];
        var str = Set._v2s(val);
        if (!this.values.hasOwnProperty(str)) {
            this.values[str] = val;
            this.n++;
        }
    }
    return this;
};

Set._v2s = function(val) {
    switch(val) {
        case undefined: return 'u';
        case null:      return 'n';
        case true:      return 't';
        case false:     return 'f';
        default: switch(typeof val) {
            case 'number': return '#' + val;
            case 'string': return '"' + val;
            default: return '@' + objectId(val);
        }
    }
    function objectId(o) {
        var prop = "|**objectid**|";
        if (!o.hasOwnProperty(prop))
            o[prop] = Set._v2s.next++;
        return o[prop];
    }
};
Set._v2s.next = 100;

// Taken from 9.6.7: auxiliary constructor
function SetFromArray(a) { 
    Set.apply(this, a); 
}

SetFromArray.prototype = Set.prototype; 
var s = new SetFromArray( [1,2,3] ); 
console.log(s instanceof Set); // => true

The text then goes further to say:

In ECMAScript 5, the bind() method of functions has special behavior that allows it to create this kind of auxiliary constructor.

What special behavior is being referred to here and how it can be used to create that kind of auxiliary constructor? Can an auxiliary constructor even be created in JavaScript?

I tried recreating this function using the bind() method:

function SetFromArray(a) {
    Set.bind(null).apply(this, a);
} 

But then, I realised that in order to leverage the bind() method's behavior in which the boundFunction inherits the prototype of the original when used as a constructor, I would have to use the boundfunction as the constructor in the constructor invocation, so the SetFromArray function I defined may not work the same way as the one defined without bind.


Solution

  • What special behavior is being referred to here and how it can be used to create that kind of auxiliary constructor?

    It is not evident from that single phrase we find at the end of that chapter, but I don't think the authors intended to use bind as you did in your attempt, because:

    Note how the authors wrote "...that allows it [i.e. bind] to create this ... constructor", with which they seem to suggest that you could call bind in a way to get the desired constructor as return value. So they hint at something like:

    var SetFromArray = <something>.bind(<something>);
    

    Although this sounds promising at first sight -- as bind returns a function -- there are several reasons why this is not possible, including:

    1. When the function returned by bind is called, an array argument is not spread into separate arguments, as apply does, yet that transformation is the whole point of having the SetFromArray function in the first place.

    2. If we wanted a workaround for the above issue, we would still need apply to be called, resulting in something like:

      var SetFromArray = Function.apply.bind(<something>);
      

      But apply is not a constructor, and so new SetFromArray([]) will raise an exception. And even if it were a constructor, it would not create an instance of Set.

    3. In strict mode (which in modern day developments should be turned on), bind returns a function that is not a constructor, trashing the whole idea that bind would create a constructor for us.

    Old book, quick revision

    The book was first published in 1996, while ECMAScript 5 only got published in 2009. The 6th edition of JavaScript: The Definitive Guide came out in 2011, having in its introduction

    The sixth edition covers HTML5 and ECMAScript 5

    The phrase you quoted from chapter 9 is such an addition that was made in that context. I doubt the authors/editors put that added claim to the test, and merely thought "hey, here we have another function that just passes on some arguments... let's add they can use bind to give it a modern ECMAScript 5 flavor!"

    Since 2011 a lot has changed:

    Auxiliary constructors

    The idea of providing auxiliary constructors through tampering with prototype chains looks contrary to good OOP design. If we look how ECMAScript itself solves this "problem", we can find the following examples:

    This suggests that in ECMAScript static functions are the standard way to provide alternative ways to create instances of your class.