JS继承和变异原型

JS Inheritance and mutating prototype

本文关键字:原型 变异 继承 JS      更新时间:2023-09-26

AFAIK,JS通过将原型链分配给新创建的对象来提供固有性。因此,下面的代码对我来说似乎是正确的方法:

function Animal(name){
    this.name = name;
}
Animal.prototype.getName = function(){return this.name;};
function Cat(){
    Animal.apply(this, arguments);
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.sayHi = function(){return 'Hi, my name is '+this.name+', and I am a "'+this.constructor.name+'" instance';};

这真的正确吗?而且,我已经读到,变异对象的原型是一个缓慢的操作,会影响以后对它的所有调用。但在这里,我们刚刚变异了Animal.prototypeCat.prototype.那么,这不好吗?如果是,我们如何处理它?还是我对原型变异警告误解了什么?如果是这样,它实际上意味着什么?

这真的正确吗?

因为它不包含任何可能被认为是不良做法的东西,是的。就结果而言是否正确是您所期望的,无法确定。

而且,我已经读到,变异物体的原型是一个缓慢的不鼓励的操作,

我不知道这意味着什么。但是,修改不属于您的对象并不被认为是好的做法,因此不要弄乱内置或主机对象(有很多文章说明为什么不这样做,例如扩展 DOM扩展内置本机有什么问题。邪恶与否?

这会影响以后对它的所有调用。

修改原型可能会影响所有将其作为[[Prototype]]的对象,这就是原型继承的重点。

但在这里,我们刚刚改变了Animal.prototype和Cat.prototype。那么,这不好吗?

本身,没有。如果它达到了您需要的结果,那就好了。您正在定义构造函数、原型属性和继承方案,因此由您决定。可能有更有效或更容易维护的方案,但这是另一个话题。

评论

复杂的继承方案在javascript中很少有用,只是没有太多的要求。大多数内置对象只有一个或两个继承级别(例如,函数实例继承自 Function.prototype 和 Object.prototype)。宿主对象(例如 DOM 元素)可能有更长的链,但这是为了方便起见,并不是真正必要的(至少一个非常流行的浏览器直到最近才实现主机对象的原型继承)。

你走在正确的轨道上。你不想改变Animal的原型,因为这打破了继承的概念。如注释中所述,使用 Object.create() 是将属性和方法从一个对象继承到另一个对象的正确方法。使用您的示例,原型继承的简单示例是通过以下方式实现的:

function Animal(name) {
  this.name = name;
}
Animal.prototype = {
  getName: function() {
    return this.name;
  }
};
function Cat(name, color) {
  Animal.call(this, name);
  this.color = color;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.getColor = function() {
  return this.color;
};
var animal = new Animal('Bob');
var cat = new Cat('Tabby', 'black');
console.log(cat.getName());
console.log(cat.getColor());
console.log(animal.getName());
console.log(animal.getColor());  // throws an error
嗨,

JavaScript 中的继承相当复杂,您必须对原型对象有很好的理解。建议你使用例如TypeScript的继承模式。您可以在操场上尝试一下

看看这个:

var extends = this.extends || function (class, parent) {
    for (var property in parent) {
        if (parent.hasOwnProperty(property)) {
            class[property] = parent[property];
        }
    }
    function newClass() { 
        this.constructor = class;
    }
    newClass.prototype = parent.prototype;
    class.prototype = new newClass();
};
var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    return Animal;
})();
var Cat = (function (_super) {
    extends(Cat, _super);
    function Cat(name) {
        _super.call(this, name);
    }
    Cat.prototype.sayHi = function () {
        return "Hello my name is:" + this.name;
    };
    return Cat;
})(Animal);

您似乎将函数的 prototype 属性与所有对象(包括函数)的内部 [[proto]] 属性混合在一起。它们是两个不同的东西。

不鼓励改变对象的内部[[proto]]属性。这可以通过setPrototypeOf或通过继承自Object.prototype__proto__访问器来完成:

var a = {}; // `a` inherits directly from `Object.prototype`
var b = {}; // `b` inherits directly from `Object.prototype`
a.__proto__ = b;             // `a` now inherits directly from `b`
// or
Object.setPrototypeOf(a, b); // `a` now inherits directly from `b`

这是不鼓励的,在创建对象后改变对象的内部[[proto]]属性。但是,应该注意的是,在创建对象时,可以将任何对象分配为其内部[[proto]]属性。

例如,许多JavaScript程序员希望创建从Function.prototype以外的某个对象继承的函数。目前,这只能通过在创建函数后更改函数的内部[[proto]]属性来实现。但是,在 ES6 中,您将能够在创建对象时分配对象的内部[[proto]]属性(从而避免了更改对象的内部[[proto]]属性的问题)。

例如,考虑以下错误代码:

var callbackFunctionPrototype = {
    describe: function () {
        alert("This is a callback.");
    }
};
var callback = function () {
    alert("Hello World!");
};
callback.__proto__ = callbackFunctionPrototype; // mutating [[proto]]
callback.describe();

它可以在 ES6 中重写如下:

var callbackFunctionPrototype = {
    describe: function () {
        alert("This is a callback.");
    }
};
// assigning [[proto]] at the time of creation, no mutation
var callback = callbackFunctionPrototype <| function () {
    alert("Hello World!");
};
callback.describe();

因此,改变对象的内部[[proto]]属性(使用 setPrototypeOf__proto__ 访问器)是不好的。但是,修改函数的 prototype 属性是可以的。

函数的 prototype 属性(我们称函数为 F )仅影响 new F 创建的对象。例如:

var myPrototype = {
    describe: function () {
        alert("My own prototype.");
    }
};
function F() {}
var a = new F; // inherits from the default `F.prototype`
alert(a.constructor === F); // true -  inherited from `F.prototype`
F.prototype = myPrototype; // not mutating the `[[proto]]` of any object
var b = new F; // inherits from the new `F.prototype` (i.e. `myPrototype`)
b.describe();

要了解有关 JavaScript 中继承的更多信息,请阅读以下问题的答案:

JavaScript 继承和构造函数属性

希望有帮助。