3

hope someone can help me to break down a snippet of code from Crockford's JS Good Parts:

Function.method('new', function ( ) {
  // Create a new object that inherits from the
  // constructor's prototype.
  var that = Object.create(this.prototype);
  // Invoke the constructor, binding –this- to
  // the new object.
  var other = this.apply(that, arguments);
  // If its return value isn't an object,
  // substitute the new object.
  return (typeof other === 'object' && other) || that;
});

the part that I don´t understand is when he uses the apply invocation pattern to create an object:

var other = this.apply(that, arguments);

How executing the this function will create the new object?

If the function will be:

var f = function (name) {
   this.name = "name";
};

How calling:

var myF = f.new("my name");

creates the object?

imtheman
  • 4,713
  • 1
  • 30
  • 30

2 Answers2

5

First, note Function.method isn't a built-in JS method. It's something Crockford made up:

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

Therefore, that Function.method method call basically does this:

Function.prototype.new = function() {
  var that = Object.create(this.prototype);
  var other = this.apply(that, arguments);
  return (typeof other === 'object' && other) || that;
});

Then when you use it like

f.new("my name");

it does this:

  1. First, it creates an object which inherits from f.prototype (instance).
  2. Then, it calls f passing that instance as the this value.
    • In this case, this will set the name property to the instance.
    • This step doesn't create any new instance, the instance was created at step 1.
  3. If the call to f returned some object, that object is returned.
    Otherwise, the instance created at step 1 is returned.
Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • 1
    I find Crockford's use of "that" confusing, surely he could have thought of something more descriptive? "other" is just as bad. – RobG Sep 10 '15 at 23:56
  • Thank you very much @Oriol. So, a constructor function can be called (in the above example `this.apply(that, arguments);`) without the `new` operator if one provides an object? I remember from the Crokford's book (The Good Parts) that he says (Crockford) that a constructor function never should be called without the `new` operator. – DanielYoshua Sep 11 '15 at 20:41
  • @DanielYoshua Well, when you use `new`, JS calls the internal [[Construct]] method; and when you call, JS calls the internal [[Call]] method. For standard functions, [\[\[Construct\]\]](http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2) behaves like Crockford's `Function.prototype.new` (but it might be different for host functions). The problem with calling a constructor (instead of instantiating) is that the `this` value will be the global object in quirks mode or `undefined` in strict mode. In this case there is no problem because with `apply` the `this` value is set to the instance – Oriol Sep 11 '15 at 21:03
  • Hi @Oriol, I tested all and now it seems clear to me. Also in your explanation,when saying: _Then, it calls f passing that instance as the this value._ You mean `F` (upper case) instead of `f`, refering to the constructor function, don't you? – DanielYoshua Sep 13 '15 at 15:58
  • @DanielYoshua You only have one function, called `f`. That function is intended to be used as a constructor, so the convention says it would be better to name it `F`. But it's not required, and since you named it `f`, I said `f`. – Oriol Sep 13 '15 at 16:19
  • @Oriol You were completely right!! Thank you very much for all J – DanielYoshua Sep 15 '15 at 17:33
3

rewritten with descriptive names

Crockford's naming obfuscates things a bit, so here's the same functionality:

Function.prototype.new = function ( ) {
  var theRealConstructor = this;
  var freshObj = Object.create(theRealConstructor.prototype);

  var freshObj_after_theRealConstructor = 
         theRealConstructor.apply(freshObj, arguments);

  if(typeof freshObj_after_theRealConstructor === 'object'){
     return freshObj_after_theRealConstructor;
  } else {            
     return freshObj; 
  }
};

which hopefully is clearer than this, other, and that.


Elaboration and example:

// this is a Waffle constructor
function Waffle(topping,ingredients){
  this.toppings = topping;
  this.ingredients = ['batter','eggs','sugar'].concat(ingredients);
}

// make the .new method available to all functions
// including our waffle constructor, `Waffle`
Function.prototype.new = function(){

  // inside `Waffle.new`, the `this` will be 
  // `Waffle`, the actual constructor that we want to use
  var theRealConstructor = this;

  // now we create a new object, a fresh waffle,
  // that inherits from the prototype of `Waffle`
  var freshObj = Object.create(theRealConstructor.prototype);

  // and call `Waffle` with it's `this` set to 
  // our fresh waffle; that's what we want the ingredients added to
  var freshObj_after_theRealConstructor = 
         theRealConstructor.apply(freshObj, arguments);

  // If we managed to make an object, return it!
  if(typeof freshObj_after_theRealConstructor === 'object'){
     return freshObj_after_theRealConstructor;

  // otherwise, just return the pre-constructor fresh waffle 
  } else {            
     return freshObj; 
  }
};

// And to try it out 
var myBreakfast = Waffle.new('syrup',['blueberries','chocolate']);

// and `myBreakfast` would look look like  ↓↓
// {
//   toppings: "syrup", 
//   ingredients:[
//     "batter", 
//     "eggs", 
//     "sugar", 
//     "blueberries", 
//     "chocolate"
//   ]
// }
user3276552
  • 1,074
  • 12
  • 15
  • You should clarify that `that` is not a copy of `this.prototype` but rather is a new empty object that inherits from the `this.prototype` object. –  Sep 11 '15 at 00:02
  • The naming you've used really make things more clear. The part that I was worry about was when it uses the apply invocation pattern, but now all is clear. Thank you very much. – DanielYoshua Sep 13 '15 at 15:54