关于经典继承与原型继承,我有一个问题。我想看看哪个更好?
假设我们有一个名为familyTree的函数。
function familyTree(){
this.lastname = "xyz";
}
如果我想为我们添加任何其他详细信息,我们可以通过两种方式继承父级:
familyTree.prototype.personDetails = function(){
this.firstName = "abc";
this.middleName = "middle1";
var newToString = function(name){ //overwriting toString method
console.log("The name is: "+name+"Middle Name is "+middleName);
}
}
var newPerson = new familyTree();
newPerson.firstName = "abc";
newPerson.middleName = "middle1";
newperson.newToString = function (name){
console.log("The name is: "+name+"Middle Name is "+middleName);
}
假设我要创建100个不同的中间名。
什么更有意义?使用经典继承还是原型?因为使用Classical可以复制所有对象,但使用原型可以使一切变得混乱。
请说明,当一个人应该使用古典与原型时。
答案 0 :(得分:2)
JavaScript中只有一种继承,即典型的继承。 JavaScript中不存在“经典”继承。
尽管JavaScript语言必须使基于类编程的OOP开发人员感到宾至如归(包括class
关键字)的“语法糖”,但JavaScript实际上并没有或使用类。这种语言词汇只是为了让你感到温暖和模糊。
您的问题实际上并不是询问继承,而是询问是否应将属性附加到构造函数或原型。
请参阅代码注释以获取解释:
// When you create a function that you will use to create object instances,
// you have a "constructor function". By convention, these functions should
// be named in PascalCase as a way to let others know that they are to be
// used in conjunction with the "new" keyword as a constructor.
function FamilyTree(first, middle, last){
// Because this function will be used to create instances of a
// FamilyTree object, each instance created will need to store
// data that is different from another. This is done with "instance
// properties" and they are created by prepending the property name
// with "this.". The "this" object will be referenced by the object
// instance variable that is used when the instance is created:
this.firstName = first;
this.middleName = middle;
this.lastName = last;
}
// JavaScript objects don't technically have "methods" - - they
// have properties that store functions and functions are how
// to add behavior to an object. Since the behaviors of an object
// don't typically change from instance to instance, you should not
// add them to the constructor function. If you did, the code would
// work, but each instance would need to store a copy of the exact
// same behavior, making the objects unnecessarialy large in memory.
// Instead, we attach behaviors that all instances of an object will
// need to the prototype of the constructor and that way all instances
// created from the constructor will inherit the behaviors, but the
// acutal behavior will only be stored once, thus saving on memory space
// and eliminating the possibility of one instance behaving differently
// than others, unintentionally.
// Implementing "methods" that all FamilyTree instances will inherit:
FamilyTree.prototype.newToString = function(name){
return "First name: " + this.firstName + ", Last Name: " + this.lastName;
}
// The constructor function's prototype (as with all objects) derives from "Object"
// which defines a "toString" property, by re-defining that property on the constructor's
// prorotype, we will be able to override the inherited one
FamilyTree.prototype.toString = function(name){
return this.lastName + ", " + this.firstName;
}
// To use this object, we have a few choices, but the simplest one is to just instantiate it:
var myFamilyTree = new FamilyTree("John","Fitzgerald","Kennedy");
// Now, we just work with the instance:
console.log(myFamilyTree.firstName);
console.log(myFamilyTree.middleName);
console.log(myFamilyTree.lastName);
console.log(myFamilyTree.newToString());
console.log(myFamilyTree.toString());
现在,上面的代码可以工作,但在技术上没有很好地组织,因为对象是一个“家谱”,它真正存储的只是一个人的名字和一些输出该名称的方法。家谱不多。实际上,这个“家谱”对象应该由其他对象组成(即,许多个人应该能够与其他相关的家谱数据一起被包括在内)。如果我们应用OOP "Single Responsibility Principle" (“类/模块应该只有一个改变的原因”),我们需要使Family Tree对象具有较小的对象部分。所有这些都涉及使原始对象具有存储一组人物对象的属性:
// First, make objects that represent the parts of the whole
function Person(first, middle, last, dob, maiden){
// Instance properties are added to the constructor, which makes individual instances:
this.firstName = first;
this.middleName = middle;
this.lastName = last;
this.dob = dob;
this.maidenName = maiden;
}
// Behavior properties are added to the constructor's prototype to avoid duplication
// of code across instances:
Person.prototype.newToString = function(name){
return "First name: " + this.firstName + ", Last Name: " + this.lastName;
}
Person.prototype.toString = function(name){
return this.lastName + ", " + this.firstName;
}
// Then create an object that becomes the sum of the parts:
function FamilyTree(){
// We just need a way to store family members.
// Each FamilyTree instance can have different members, so an instance property
// is needed:
this.people = [];
}
// And, again, behaviors are added to the prototype:
FamilyTree.prototype.getMemberCount = function(){
return this.people.length;
}
FamilyTree.prototype.addMember = function(personObject){
this.people.push(personObject);
}
FamilyTree.prototype.removeMember = function(personObject){
var index = this.people.findIndex(function(element){
return personObject === element;
});
this.people.splice(index, 1);
}
// And, because the tree stores an array, we can looop through it:
FamilyTree.prototype.enumerate = function(){
var result = "";
this.people.forEach(function(person){
result += person.firstName + " " + person.middleName + " " + person.lastName +
" (" + person.newToString() + " [" + person.toString() + "])";
});
return result;
};
// Now, to use the Family Tree, we first need some people
var jack = new Person("John","Fitzgerald","Kennedy", new Date(1917, 4, 29));
var bobby = new Person("Robert", "Francis", "Kennedy", new Date(1925, 11, 20));
var teddy = new Person("Edward","Moore","Kennedy", new Date(1932, 1, 22));
// Now, we add those objects to a new Family Tree instance:
var kennedyTree = new FamilyTree();
kennedyTree.addMember(jack);
kennedyTree.addMember(bobby);
kennedyTree.addMember(teddy);
console.log("The tree contains: " + kennedyTree.getMemberCount() + " members.");
console.log(kennedyTree.enumerate());
// Let's remove a member:
kennedyTree.removeMember(bobby);
console.log("The tree contains: " + kennedyTree.getMemberCount() + " members.");
console.log(kennedyTree.enumerate());