为什么更改函数的 .prototype 会影响已使用该构造函数创建的对象的“实例”

Why does changing the .prototype of a Function affect "instanceof" for objects already created with that constructor function

本文关键字:创建 构造函数 对象 实例 函数 prototype 为什么 影响      更新时间:2023-09-26

我试图做的是在Javascript中创建一个简单的对象层次结构,其中层次结构中的每个对象都有自己的一组属性,同时能够在这些新创建的对象上使用instanceof。 换句话说,我希望我的基本对象(请参阅下面的 People)实际包含 firstName 和 lastName 属性。 我最终不希望我的所有属性都捣碎到同一个"这个"上。 我想要一个漂亮/整洁的真实对象层次结构。

为此,我认为这是正确的方法,但看起来我错了:

var Person = function (firstName, lastName)
{
    this.firstName = firstName;
    this.lastName = lastName;
}
var Ninja = function (beltColor)
{
    this.beltColor = beltColor;
}
Ninja.prototype = new Person ("George", "Clooney");
var ninja1 = new Ninja ("Black");
Ninja.prototype = new Person ("Scarlett", "Johansson");
var ninja2 = new Ninja ("Blue");
Ninja.prototype = new Person ("Brad", "Pitt");
var ninja3 = new Ninja ("Green");
console.log (ninja1 instanceof Ninja); // false
console.log (ninja1 instanceof Person); // true
console.log (ninja2 instanceof Ninja); // false
console.log (ninja2 instanceof Person); // true
console.log (ninja3 instanceof Ninja); // true
console.log (ninja3 instanceof Person); // true

上面的代码(在我看来)将使用 Ninja 构造函数有效地创建三个主要对象,生成的ninja1.__proto__, ninja2.__proto__ and ninja3.__proto__对象将包含 firstName/lastName 属性(这是我想要的)。 这些__proto__对象也将是"类型"Person,因为它们是使用Person构造函数创建的,而ninja1,ninja2和ninja3对象将是"Ninja"类型(或者我认为),因为它们是使用Ninja构造函数创建的。

我意识到通过 Person 构造函数将 Ninja.prototype 重新分配给新创建的对象是抛出instanceof运算符的原因,但对于我的生活,我不知道为什么。

所以我有两个问题:

1) 为什么通过 Person 构造函数将 Ninja.prototype 重新分配给新创建的对象会抛弃instanceof运算符?
2)做我正在尝试做的事情的正确方法是什么? 换句话说,创建一个简单的对象层次结构,其中属性真正位于层次结构的不同级别,同时还能够以有意义的方式使用instanceof

Q1:为什么通过 Person 构造函数将 Ninja.prototype 重新分配给新创建的对象会抛弃instanceof运算符?

这是因为instanceof运算符通过向上移动原型链并检查对象的内部实际原型([[Prototype]])是否与构造函数的当前原型属性进行比较来工作。

Q2:执行我正在尝试执行的操作的正确方法是什么?换句话说,创建一个简单的对象层次结构,其中属性真正位于层次结构的不同级别,同时还能够以有意义的方式使用instanceof

原型适用于构造函数的所有实例共有的项(例如,所有人都有大脑,但并非每个人都有相同的名称)。不要更改原型,而是直接添加名字和姓氏参数作为 Ninja 函数实例的属性,并使 Ninja 的原型属性成为空白Person(以覆盖没有名字的人的情况,以便instanceof返回 true):

var Person = function (firstName, lastName)
{   this.firstName = firstName;
    this.lastName = lastName;
}
var Ninja = function (beltColor, firstName, lastName)
{   this.beltColor = beltColor;
    Person.call(this, firstName, lastName); // call parent function
}
Ninja.prototype = new Person("", ""); // do this so that instanceof returns true
var ninja1 = new Ninja ("Black", "George", "Clooney");
var ninja2 = new Ninja ("Blue", "Scarlett", "Johansson");
var ninja3 = new Ninja ("Green", "Brad", "Pitt");
console.log (ninja1 instanceof Ninja); // true
console.log (ninja1 instanceof Person); // true
console.log (ninja2 instanceof Ninja); // true
console.log (ninja2 instanceof Person); // true
console.log (ninja3 instanceof Ninja); // true
console.log (ninja3 instanceof Person); // true

这里的instanceOf方法没有给出 Ninja 对象的构造函数,因为您已经手动将 NInja 对象的原型指定为 "Person",因此在遍历原型链时,它将具有 Person 对象。如果你期待你想要的,你可以尝试下面的代码

var Person = function (firstName, lastName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }

var Ninja = function (firstName, lastName, beltcolor)
{
    this.beltColor = beltColor;
    Person.apply(this,arguments);
}
var ninja1 = new Ninja ( "George", "Clooney", "Black");
var ninja2 = new Ninja ("Scarlett", "Johansson","Blue");
var ninja3 = new Ninja ("Brad", "Pitt", "Green");
console.log (ninja1 instanceof Ninja); // true
console.log (ninja1 instanceof Person); // false
console.log (ninja2 instanceof Ninja); // true
console.log (ninja2 instanceof Person); // false
console.log (ninja3 instanceof Ninja); // true
console.log (ninja3 instanceof Person); // false
console.log(ninja1.firstName); //George

希望这对你有帮助。

您也可以查看下面的链接 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain

instanceof运算符查看实例上缓存的原型链,并将其与您提供给它的构造函数进行比较。如果更改了构造函数上的原型,则它们不再匹配。

如果不完全更改原型对象,而只是更新属性,则不会发生这种情况。

您的对象模型看起来很奇怪;为什么名称不是实例属性,就像"皮带颜色"一样?