tri*_*nth 284 javascript oop inheritance
在MDN文章面向对象的Javascript简介中关于继承的部分中,我注意到他们设置了prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Run Code Online (Sandbox Code Playgroud)
这有什么重要意义吗?省略它可以吗?
Way*_*ett 257
它并不总是必要的,但确实有它的用途.假设我们想在基Person类上创建一个copy方法.像这样:
// define the Person Class
function Person(name) {
this.name = name;
}
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
// define the Student class
function Student(name) {
Person.call(this, name);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
Run Code Online (Sandbox Code Playgroud)
现在当我们创建一个新的Student并复制它时会发生什么?
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false
Run Code Online (Sandbox Code Playgroud)
副本不是.的实例Student.这是因为(没有明确的检查),我们无法Student从"基础"类返回副本.我们只能退货了Person.但是,如果我们重置了构造函数:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
Run Code Online (Sandbox Code Playgroud)
...然后一切都按预期工作:
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true
Run Code Online (Sandbox Code Playgroud)
T.J*_*der 72
这有什么重要意义吗?
是的,不是.
在ES5和更早版本中,JavaScript本身并没有constructor用于任何东西.它定义了函数prototype属性上的默认对象将拥有它,并且它将引用回函数,就是这样.规范中没有任何其他内容可以引用它.
这在ES2015(ES6)中发生了变化,它开始在继承层次结构中使用它.例如,在构建要返回的新承诺时,Promise#then使用constructor您调用它的promise(通过SpeciesConstructor)的属性.它还涉及子类型数组(通过ArraySpeciesCreate).
在语言本身之外,有时人们会在尝试构建通用"克隆"函数时使用它,或者只是在他们想要引用他们认为是对象的构造函数时.我的经验是使用它很少见,但有时候人们会使用它.
省略它可以吗?
默认情况下,您只需要在替换函数prototype属性上的对象时将其放回:
Student.prototype = Object.create(Person.prototype);
Run Code Online (Sandbox Code Playgroud)
如果你不这样做:
Student.prototype.constructor = Student;
Run Code Online (Sandbox Code Playgroud)
......然后Student.prototype.constructor继承Person.prototype(大概)constructor = Person.所以这是误导.当然,如果你是使用它的子类(如Promise或Array)并且不使用class¹(它为你处理这个)的子类,你需要确保正确设置它.所以基本上:这是一个好主意.
如果您的代码(或您使用的库代码)中没有任何内容使用它,那也没关系.我总是确保它正确接线.
当然,使用ES2015(又名ES6)的class关键字,我们大部分时间都会使用它,我们不再需要了,因为当我们这样做时它会被我们处理
class Student extends Person {
}
Run Code Online (Sandbox Code Playgroud)
¹ "......如果你是使用它的子类(如Promise或者Array)而不是使用class......" - 这可能会这样做,但这真的很痛苦(而且有点傻).你必须使用Reflect.construct.
bth*_*man 12
TLDR; 不是非常必要,但从长远来看可能会有所帮助,而且这样做更准确.
注意:很多编辑因为我之前的回答是令人困惑的写,并有一些错误,我急于回答错过了.感谢那些指出一些令人震惊的错误的人.
基本上,它是在Javascript中正确地进行子类化.当我们进行子类化时,我们必须做一些时髦的事情以确保原型委派能够正常工作,包括覆盖prototype对象.覆盖prototype对象包括constructor,所以我们需要修复引用.
让我们快速了解ES5中"类"的工作原理.
假设你有一个构造函数及其原型:
//Constructor Function
var Person = function(name, age) {
this.name = name;
this.age = age;
}
//Prototype Object - shared between all instances of Person
Person.prototype = {
species: 'human',
}
Run Code Online (Sandbox Code Playgroud)
当你调用构造函数来实例化时,说Adam:
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
Run Code Online (Sandbox Code Playgroud)
new使用'Person'调用的关键字基本上将运行Person构造函数,并带有一些额外的代码行:
function Person (name, age) {
// This additional line is automatically added by the keyword 'new'
// it sets up the relationship between the instance and the prototype object
// So that the instance will delegate to the Prototype object
this = Object.create(Person.prototype);
this.name = name;
this.age = age;
return this;
}
/* So 'adam' will be an object that looks like this:
* {
* name: 'Adam',
* age: 19
* }
*/
Run Code Online (Sandbox Code Playgroud)
如果我们console.log(adam.species),查找失败的adam实例,并期待在原型链的.prototype,这是Person.prototype-而且Person.prototype 有一个.species属性,因此查找将成功Person.prototype.然后它会记录'human'.
这里,Person.prototype.constructor将正确指向Person.
所以现在有趣的部分,即所谓的"子类化".如果我们想要创建一个Student类,这是该类的子Person类并进行一些额外的更改,我们需要确保Student.prototype.constructor指向Student 的点以确保准确性.
它本身并不是这样做的.子类化时,代码如下所示:
var Student = function(name, age, school) {
// Calls the 'super' class, as every student is an instance of a Person
Person.call(this, name, age);
// This is what makes the Student instances different
this.school = school
}
var eve = new Student('Eve', 20, 'UCSF');
console.log(Student.prototype); // this will be an empty object: {}
Run Code Online (Sandbox Code Playgroud)
在new Student()这里调用将返回一个包含我们想要的所有属性的对象.在这里,如果我们检查eve instanceof Person,它将返回false.如果我们试图访问eve.species,它将返回undefined.
换句话说,我们需要连接委托,以便eve instanceof Person返回true,以便Student委托的实例正确Student.prototype,然后Person.prototype.
但是因为我们用new关键字调用它,还记得调用添加了什么吗?它会调用Object.create(Student.prototype),这就是我们如何建立Student和之间的委托关系Student.prototype.请注意,现在Student.prototype是空的.所以找了.species一个实例Student作为它的代表会失败只 Student.prototype,和.species属性不上不存在Student.prototype.
当我们分配Student.prototype到Object.create(Person.prototype),Student.prototype本身然后委托给Person.prototype,并查找eve.species将返回human如我们预期.据推测,我们希望它继承自Student.prototype和Person.prototype.所以我们需要解决所有问题.
/* This sets up the prototypal delegation correctly
*so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
*This also allows us to add more things to Student.prototype
*that Person.prototype may not have
*So now a failed lookup on an instance of Student
*will first look at Student.prototype,
*and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);
Run Code Online (Sandbox Code Playgroud)
现在代表团工作,但我们覆盖Student.prototype了一个Person.prototype.因此,如果我们打电话Student.prototype.constructor,它将指向Person而不是Student.这就是我们需要修复它的原因.
// Now we fix what the .constructor property is pointing to
Student.prototype.constructor = Student
// If we check instanceof here
console.log(eve instanceof Person) // true
Run Code Online (Sandbox Code Playgroud)
在ES5中,我们的constructor属性是一个引用,它引用了我们编写的函数,意图是"构造函数".除了new关键字给我们的内容外,构造函数还是一个"普通"函数.
在ES6中,constructor它现在被构建为我们编写类的方式 - 就像在我们声明一个类时它作为一个方法提供的.这只是语法糖,但它确实给了我们一些便利,例如super当我们扩展现有类时访问.所以我们会像这样编写上面的代码:
class Person {
// constructor function here
constructor(name, age) {
this.name = name;
this.age = age;
}
// static getter instead of a static property
static get species() {
return 'human';
}
}
class Student extends Person {
constructor(name, age, school) {
// calling the superclass constructor
super(name, age);
this.school = school;
}
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*hen 10
我不同意.没有必要设置原型.采用完全相同的代码,但删除prototype.constructor行.有什么变化吗?不.现在,进行以下更改:
Person = function () {
this.favoriteColor = 'black';
}
Student = function () {
Person.call(this);
this.favoriteColor = 'blue';
}
Run Code Online (Sandbox Code Playgroud)
并在测试代码结束时......
alert(student1.favoriteColor);
Run Code Online (Sandbox Code Playgroud)
颜色为蓝色.
根据我的经验,对prototype.constructor的更改没有太大作用,除非你做的非常具体,非常复杂的事情,可能不是很好的做法:)
编辑:在网上挖了一下并做了一些实验之后,看起来人们设置了构造函数,使它看起来像是用'new'构造的东西.我想我会争辩说这个问题是javascript是一种原型语言 - 没有继承权这样的东西.但是大多数程序员都来自编程的背景,这种编程将继承作为"方式".因此,我们想出各种各样的东西来尝试将这种原型语言变成"经典"语言......例如扩展"类".实际上,在他们给出的例子中,一个新学生就是一个人 - 它不是从另一个学生那里"延伸"的......学生就是关于这个人的,而无论学生是谁,也是如此.扩展学生,无论您扩展什么,都是学生的核心,但可以根据您的需求进行定制.
克罗克福德有点疯狂和过分热心,但对他写的一些东西做了一些认真的阅读......它会让你以非常不同的方式看待这些东西.
小智 9
如果你写的话,这有很大的陷阱
Student.prototype.constructor = Student;
Run Code Online (Sandbox Code Playgroud)
但是如果有一位老师的原型也是人,你就写了
Teacher.prototype.constructor = Teacher;
Run Code Online (Sandbox Code Playgroud)
然后学生构造函数现在是老师!
编辑:您可以通过确保使用Object.create创建的Person类的新实例来设置Student和Teacher原型来避免这种情况,如Mozilla示例中所示.
Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
Run Code Online (Sandbox Code Playgroud)
小智 5
到目前为止,仍存在困惑.
按照原始示例,因为您有一个现有对象student1:
var student1 = new Student("Janet", "Applied Physics");
Run Code Online (Sandbox Code Playgroud)
假设你不想知道如何student1创建,你只需要像它这样的另一个对象,你可以使用student1like 的构造函数属性:
var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");
Run Code Online (Sandbox Code Playgroud)
Student如果未设置构造函数属性,则无法获取属性.相反,它会创建一个Person对象.
| 归档时间: |
|
| 查看次数: |
48942 次 |
| 最近记录: |