康乐福 "new" 方法

Crockford "new" method

希望有人能帮我分解 Crockford 的 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;
});

我不明白的部分是当他使用应用调用模式创建对象时:

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

如何执行 this 函数来创建新对象?

如果函数将是:

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

调用方式:

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

创建对象?

首先注意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;
};

因此,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 中创建的实例。

用描述性名称重写

Crockford 的命名有点混淆,所以这里有相同的功能:

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

希望比 thisotherthat 更清楚。


详细说明和示例:

// 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"
//   ]
// }