JS继承和变异原型

时间:2015-06-17 14:07:31

标签: javascript inheritance prototype

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。那么,那很糟糕吗?如果是我们如何处理它?或者我误解了原型变异警告的一些事情?如果是这样,它究竟意味着什么?

4 个答案:

答案 0 :(得分:1)

你走在正确的轨道上。你不想改变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

答案 1 :(得分:1)

  

这实际上是否正确?

因为它不包含任何可能被认为是不良做法的东西,是的。在结果方面是否正确无法确定。

  

并且,我已经读过,变异对象的原型是一个缓慢的阻止操作,

我不知道这意味着什么。但是,修改你不拥有的对象并不是一种好的做法,所以不要搞乱内置对象或宿主对象(有很多文章说明为什么没有,例如What's wrong with extending the DOMExtending builtin natives. Evil or not? )。

  

影响所有后来的调用。

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

  

但是我们刚刚改变了Animal.prototype和Cat.prototype。那么,那是不是很糟糕?

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

注释

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

答案 2 :(得分:0)

您在JavaScript中的继承相当复杂,您必须对原型对象有一个很好的理解。我建议您使用TypeScript的继承模式。您可以试试Playground

看看这个:

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

答案 3 :(得分:0)

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

不鼓励改变对象的内部[[proto]]属性。这可以通过setPrototypeOf__proto__访问者完成,该访问者继承自Object.prototype

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]]属性来实现。但是,in ES6 you will be able to assign the internal [[proto]] property of an object when it is created(从而避免了改变对象的内部[[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 inheritance and the constructor property

希望有所帮助。