Crockford的"new"方法

3

希望有人能够帮助我分解来自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;
});

我不太理解的是当他使用apply调用模式来创建一个对象时:

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

执行this函数将如何创建新对象?
如果该函数是:
var f = function (name) {
   this.name = "name";
};

如何进行函数调用:

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

创建对象?


由于我们不知道函数的名称,因此我们不知道this将引用什么。 - Felix Kling
Function.method是什么?它有什么作用? - Joseph
2个回答

5

首先需要注意的是,Function.method并不是JS内置方法,而是Crockford发明的一种方式

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步中创建的实例。

1
我发现Crockford使用“that”很令人困惑,他肯定可以想到更具描述性的词语吧?“other”也同样糟糕。 - RobG
非常感谢@Oriol。所以,如果提供一个对象,构造函数可以在没有new运算符的情况下被调用(在上面的例子中是this.apply(that, arguments);)?我记得Crokford的书(The Good Parts)中说过(Crockford)构造函数永远不应该在没有new运算符的情况下被调用。 - DanielYoshua
@DanielYoshua 嗯,当你使用 new 时,JS 调用内部的 [[Construct]] 方法;而当你调用时,JS 调用内部的 [[Call]] 方法。对于标准函数,[[Construct]] 的行为类似于 Crockford 的 Function.prototype.new(但对于宿主函数可能会有所不同)。调用构造函数(而不是实例化)的问题在于,在怪异模式下 this 值将是全局对象,而在严格模式下将是 undefined。在这种情况下,由于使用 apply,因此 this 值被设置为实例。 - Oriol
嗨@Oriol,我测试了所有内容,现在对我来说似乎很清楚。此外,在您的解释中,当说:“然后,它调用f并将该实例作为this值传递。”时,您是指构造函数而不是f,对吗? - DanielYoshua
@DanielYoshua 你只有一个名为 f 的函数。该函数旨在用作构造函数,因此惯例认为最好将其命名为 F。但这不是必需的,由于你将其命名为 f,所以我也称之为 f - Oriol
@Oriol 你完全正确!!非常感谢你的帮助 J - DanielYoshua

3

使用描述性名称重写

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

希望这个比“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"
//   ]
// }

你应该澄清 that 不是 this.prototype 的副本,而是一个新的空对象,它继承自 this.prototype 对象。 - user1106925
你使用的命名确实使事情更清晰了。我担心的是它何时使用应用程序调用模式,但现在一切都清楚了。非常感谢。 - DanielYoshua

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接