如何从仅调用另一个构造函数的函数实例化新对象?

How is a new object instantiated from a function that only invokes another constructor function?

function Product(name, price) {
    this.name = name;
    this.price = price;
}
  
function Food(name, price) {
    Product.call(this, name, price); 
}

const item = new Food('cheese', 50)

console.log(item.name)

我不明白。当新对象 item 不是构造函数时,它如何从 Food 函数实例化,但它只包含另一个函数,然后使用传递 thiscall() 方法调用哪个指的是新实例化的对象? Product 函数如何成为 item 对象的构造函数?我不明白传输是如何发生的,因为 Food 不是构造函数,并且 Product.call() 不 return Product 函数的内容Food 函数构造函数。

有人可以给我解释一下吗?

当您使用 new 关键字调用函数时,它会创建一个对象实例。让我们称之为 this。在函数内部,我们可以将内容分配给 this,因此它将成为 this.

的属性

每个函数本质上都可以用作构造函数,但如果我们不向 this 分配任何内容,它就不会有任何属性。因此,在您的情况下,我们使用 Food 函数作为构造函数(在 new 关键字的帮助下),而不是在函数中手动将内容分配给 this,我们调用以 this 作为我们希望将属性分配给的对象的不同函数。

ProductFood 乍一看都像 classic 构造函数。让我们看看实例化从哪里开始......new Food利用Food作为构造函数,因此,在construction/instantiation时,Foodthis上下文已经引用到 Food 个实例。后者通过非实例化 Product.call 作为 thisArgs 进入 Product 的 call/invocation 时间。因此,现在有一个 Food 实例被 Product 处理为 this 上下文,它 add/augment 属性 nameprice 非常 Food实例。

看起来像构造函数(具有 this 上下文)但却是 never instantiated but always explicitly applied either via call or apply to an object is one of the possible ways of writing a mixin. Actually the example shows one of the most classic purely function-based mixin 模式的东西。

更多 ... (自我推销) ...

  • How to use mixins properly in Javascript

注意 ...需要注意的是,将类似构造函数的函数用作混合函数确实只适用于纯函数。 True/real class(基于语法)构造函数 函数不能是applyed/called, but only instantiated via the new operator.

下面示例的日志记录确实证明了上述解释...

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price); 
}

const item = new Food('cheese', 50);

console.log({ item });

console.log(
  '(Object.getPrototypeOf(item) === Product.prototype) ?',
  (Object.getPrototypeOf(item) === Product.prototype)
);
console.log(
  '(Object.getPrototypeOf(item) === Food.prototype) ?',
  (Object.getPrototypeOf(item) === Food.prototype)
);

console.log(
  '(item instanceof Product) ?',
  (item instanceof Product)
);
console.log(
  '(item instanceof Food) ?',
  (item instanceof Food)
);
console.log(
  '(item instanceof Object) ?',
  (item instanceof Object)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

编辑:

OP 的问题

I get that this inside Food() refers to the new instance item, but I'm not getting how is it that when you pass this to Product(), now the this.name inside Product() gets assigned to the passed this? First of all, I thought call() changes the object owner, but Product() is not instantiated yet, so how can it have a new object owner?

A.

不要被Product的命名和大写字母搞糊涂了。人们需要将其视为具有 this 上下文 的普通 函数,因此 方法 ,仍然 “免费floating 因为它本身没有分配给对象(永远不会)。

甚至可以将其重命名为 assignProductFeatures。使用 call / apply (显然 OP 甚至采用了前者的示例)确实在必须作为方法的第一个参数传递的 this 上下文中执行函数。因此,对于给定的示例,确实将 nameprice 分配给作为 this 上下文提供的任何 object/instance。

function assignProductFeatures(name, price) {
  // formerly known as "wan't to be" `Product` constructor.
  this.name = name;
  this.price = price;
}


const myUnknownType = {
  type: "unknown"
};
console.log({ myUnknownType });

assignProductFeatures.call(myUnknownType, 'cheese', 50);

console.log({ myUnknownType });


// ... deconstructing/refactoring the OP's original example code ...

// empty constructor
function Food() {}

// food factory
function createFood(name, price) {

  // create `Food` instance.
  const foodType = new Food;

  // augment the newly created type.
  assignProductFeatures.call(foodType, name, price);

  // return  the newly created augmented type.
  return foodType;
}

const item = createFood('cheese', 50);

console.log({ item });
console.log(
  '(item instanceof assignProductFeatures) ?',
  (item instanceof assignProductFeatures)
);
console.log(
  '(item instanceof Food) ?',
  (item instanceof Food)
);
console.log(
  '(item instanceof Object) ?',
  (item instanceof Object)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

编辑:

OP 的问题

For this.name = name inside Product() to be assigned to the passed object referenced by this, then the Product() function must be acting as a constructor to the item instance, otherwise, how are the properties defined inside Product() referenced by this inside Food()? call(this) invokes Product() with item as its new object owner, but item does not have any properties, and Food() is not instantiating any object... I can't wrap my head around this!!!! I get what you're saying to me, but I can't understand the mechanics, and how it's all working!

来自你所有的问题...

"the Product() function must be acting as a constructor to the item instance"

...不,一点也不,因为...

"Call() is supposed to change the object owner"

...完全正确,但只是在委托函数's/method的调用时间临时恰好一次...

"... if Product() is not even instantiated?"

...不需要实例化。查看上面的示例,我试图让您将 Product 视为未绑定方法,它通过显式调用 call/apply 及时应用于任何对象,从而调用前者作为应用上下文中的方法。

您可能需要考虑以下片段:

const item = {
    name: 'cheese',
    price: 50,
};

绝对等同于

const item = {};
item.name = 'cheese';
item.price = 50;

(除了 item 的原型)等同于

function Food() {}
const item = Object.create(Food.prototype);
item.name = 'cheese';
item.price = 50;

绝对等同于

function Food() {}
const item = new Food();
item.name = 'cheese';
item.price = 50;

绝对等同于

function Food(name, price) {
    this.name = name;
    this.price = price;
}
const item = new Food('cheese', 50);

绝对等同于

function setProductProperties(obj, name, price) {
    obj.name = name;
    obj.price = price;
}
function Food(name, price) {
    setProductProperties(this, name, price); 
}
const item = new Food('cheese', 50);

绝对等同于你问题中的代码

function Product(name, price) {
    this.name = name;
    this.price = price;
}  
function Food(name, price) {
    Product.call(this, name, price); 
}
const item = new Food('cheese', 50);

它们都有相同的结果 - 具有属性 .name.priceitem 对象。不同之处仅在于它们如何通过函数调用实现不同级别的抽象。拥有构造函数只会让实例化具有相同形状的多个对象变得更容易,只在一个地方定义该形状。