Крокфорд "новый" метод

Надеюсь, кто-нибудь может помочь мне разобрать фрагмент кода из 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;
});

часть, которую я не понимаю, - это когда он использует шаблон вызова apply для создания объекта:

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

Как выполнение этой функции создаст новый объект?

Если функция будет:

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

Как звонить:

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

создает объект?

2 ответа

Решение

Во-первых, обратите внимание Function.method не является встроенным методом JS. Это то, что сделал Крокфорд:

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

Поэтому, что Function.method вызов метода в основном делает это:

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

Затем, когда вы используете его как

f.new("my name");

это делает это:

  1. Во-первых, он создает объект, который наследует от f.prototype (пример).
  2. Затем он вызывает f передавая этот экземпляр как this значение.
    • В этом случае это установит name свойство к экземпляру.
    • Этот шаг не создает новый экземпляр, экземпляр был создан на шаге 1.
  3. Если звонок f возвратил некоторый объект, тот объект возвращен.
    В противном случае возвращается экземпляр, созданный на шаге 1.

переписан с описательными именами

Именование Крокфорда немного запутывает вещи, так что вот та же функциональность:

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; 
  }
};

что, надеюсь, яснее, чем this, other, а также that,


Разработка и пример:

// 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"
//   ]
// }
Другие вопросы по тегам