我已经看到,在很多情况下,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类并继承语法糖。我只是好奇如何在较低层面上工作。答案 0 :(得分:4)
我的问题是,如果有人可以详细解释为什么Object.create(Organism)是必要的,只是简单地做这个Human.prototype = Organism.prototype是不够的。
你可以完全做到这一点,它甚至可以工作。
然而存在微妙的问题。如果您直接指定Organism.prototype
,当Organism
将继承您在Human.prototype
上创建的所有方法时,会产生令人讨厌的副作用。不是你想要的。
例如,如果您执行Human.prototype = Organism.prototype
,那么org
将run
,但它应该没有人类的方法。
答案 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()
。
另一个注意事项是,class
或Object.setPrototypeOf(Human, Organism)
会导致静态原型继承,以防Human.__proto__ = Organism
包含静态方法。这不是这种情况,但如果您继承自Organism
,那么该语句将导致子类也有方法Array
和isArray()
。前者比后者更受欢迎,因为from()
是一个特定于实现的黑客,针对V8进行原型继承,而不是ECMAScript官方规范的一部分。