不同的继承方式

时间:2018-03-13 13:14:34

标签: javascript prototype

我已经看到,在很多情况下,js中的继承可以像这样实现

function Organism(age) {
    this.age = age;
}

Organism.prototype.growOlder = function(){ this.age = this.age + 1}
var org = new Organism("1000");

function Human(name,age){
     Organism.call(this,age); //this sets up the properties on the human object
     this.name = name;
}

Human.prototype = Object.create(Organism)
Human.prototype.constructor = Human; //sets the constructor
Human.prototype.run = function(){ console.log(this.name + "run")}

我的问题是,是否有人可以详细解释为什么Object.create(Organism)是必要的并且只是这样做

Human.prototype = Organism.prototype

是不够的。原型链看起来像

没有object.create Human.__proto__ -> Organism __proto__-> Object

with object.create: Human.__proto__-> IntermediateObject.__proto__ -> Organism.__proto__-> Object

感谢您的时间。

编辑:我知道es6类并继承语法糖。我只是好奇如何在较低层面上工作。

3 个答案:

答案 0 :(得分:4)

  

我的问题是,如果有人可以详细解释为什么Object.create(Organism)是必要的,只是简单地做这个Human.prototype = Organism.prototype是不够的。

你可以完全做到这一点,它甚至可以工作。

然而存在微妙的问题。如果您直接指定Organism.prototype,当Organism将继承您在Human.prototype上创建的所有方法时,会产生令人讨厌的副作用。不是你想要的。

例如,如果您执行Human.prototype = Organism.prototype,那么orgrun,但它应该没有人类的方法。

答案 1 :(得分:1)

Organism不是有机体。它是一种创造有机体的构造函数。原型链应指向正确类型的对象。

您不应该执行以下操作的原因更为微妙:

Human.prototype = new Organism()

这是因为调用new Organism以您可能不想要的方式设置对象的属性。例如,它将this.age设置为undefined。它也可能具有全球性的影响,例如更新计数器,发射核武器,偷走你的男朋友,或其他类似的东西。使用Object.create时,可以避免此问题;它只是为你的新对象设置正确的原型链。

关于您的技术的一个注意事项:如果未在age构造函数中传递Human,然后将其传递给Organism,那么您最终会得到undefined。这可能会有问题。

更新

编辑已将Human.prototype = Organism更改为Human.prototype = Organism.prototype。对此的反对意见是,Human.prototype的任何更改都会更新所有生物。这不太可能是你想要的。

答案 2 :(得分:1)

  

我知道es6类并且继承了语法糖。我只是好奇如何在较低层面上工作。

所以,让我们从现代的方法开始,然后向后退一步:

class Organism {
  constructor (age) {
    this.age = age
  }

  growOlder () {
    this.age++ 
  }
}

class Human extends Organism {
  constructor (name, age) {
    super(age)

    this.name = name
  }

  run () {
    console.log(`${this.name} run`) 
  }
}

将是原型继承的ES6规范方法。使用Babel并简化一些不适用于正确声明的类的类型检查,我们可以看到它如何转换为ES5:

function Organism(age) {
  this.age = age
}

Organism.prototype.growOlder = function growOlder() {
  this.age++
}

function Human(name, age) {
  Organism.call(this, age)

  this.name = name
}

Human.prototype = Object.create(Organism.prototype)
Human.prototype.constructor = Organism

if (Object.setPrototypeOf)
  Object.setPrototypeOf(Human, Organism)
else
  Human.__proto__ = Organism

Human.prototype.run = function run() {
  console.log(this.name + " run")
}

为了简单起见使用es2015-loose进行透明化,否则为了使prototype方法不可枚举和其他细节

,事情会变得更加复杂。

Human.prototype = Object.create(Organism.prototype)创建一个新的空对象,__proto__设置为Organism.prototype而不调用副作用的构造函数,并将其设置为新的{ {1}} prototype

从ES5开始支持原型继承的更正确方法,因为ES3没有Human,因此需要Object.create()。不幸的是,这会创建一个具有属性Human.prototype = new Organism()的对象,这可能会导致边缘情况的不良行为,因为这会导致age = undefined。其他构建体可能具有进一步不希望的副作用。

因此,为了正确地转换ES6 'age' in Human.prototype === true语法,必须使用Object.create()

另一个注意事项是,classObject.setPrototypeOf(Human, Organism)会导致静态原型继承,以防Human.__proto__ = Organism包含静态方法。这不是这种情况,但如果您继承自Organism,那么该语句将导致子类也有方法ArrayisArray()。前者比后者更受欢迎,因为from()是一个特定于实现的黑客,针对V8进行原型继承,而不是ECMAScript官方规范的一部分。