面向对象程序设计(高级篇)
之前章节,我们经过对象的入门篇,对象的基础篇,对象的番外篇这几个章节的学习以后,我们对于对象的一些特征已经有了一些明确的认识,现在我们就可以在原来基础上面进行提高了
本个章节讲到对象与对象的之关系。对象与对象之间其实是存在一定的关系,例如学生与班级,例如人和学生等,针对于对象之关系,我们大体上面可以划分成两个大
- 对象包含对象,如班级里面有学生,学校里面有系别和班级
- 对象继承对象,男孩 与 女孩应该都同属于人这个类别,它们应该都具备人的一些共同特点
针对这种关系,我们本章就来具体学习
对象的继承关系
对象的继承是面向对象程序设计里面最大的一个特点,它可以将一部分对象的公共特征提取出来,然后再去封装成另一个对象,如人,男人,女人等,那么人的特征就是男人与女人的共同特征
通过call/apply去实现继承
在我们的JavaScript里面,我们又怎么样去设计对象呢。请看如下代码
//现在有一个学生,它有姓名,性别,年龄,学号四个属性,有一个sayHello方法,还有一个study方法
function Student(userName, sex, age, sid) {
this.userName = userName;
this.sex = sex;
this.age = age;
this.sid = sid;
this.sayHello = function () {
console.log("大家好,我叫" + this.userName + ",我今年" + this.age + "岁了");
}
this.study = function () {
console.log(this.userName + "在学习");
}
}
//现在又有一个老师,它有姓名,性别,年龄,教师编号四个属性,有一个sayHello的方法,和一个teaching的方法
function Teacher(userName, sex, age, tid) {
this.userName = userName;
this.sex = sex;
this.age = age;
this.tid = tid;
this.sayHello = function () {
console.log("大家好,我叫" + this.userName + ",我今年" + this.age + "岁了");
}
this.teaching = function () {
console.log(this.userName + "在教书");
}
}
//可以创建两个学生
var stu1 = new Student("杨兰芳", "女", 18, "s001");
var stu2 = new Student("赵聪", "男", 19, "s002");
//可以创建一个老师
var t1 = new Teacher("杨标", "男", 18, "t001");
我们现在是分别创建了2个构造函数Student
,Teacher
我们发现它们都有一些共同的特征,学生和老师都具备姓名和性别还有年龄这三个属性,同时还有一个共同的方法叫sayHello
。针对这种现象,我们能不能进一下的简化我们的代码呢
我们可以很明显的感觉到这里面有很多相同的代码。我们能不能把相同的代码提取出来组成一个新的对象呢
TypeError: Cannot read properties of undefined (reading 'v')
我们能不能将公共的对象提取出来呢,这个时候就使用到了我们面向对象里面的继承
继承有一个特点,子级对象继承了父级对象以后就可以把父级对象的某些属性与方法拿过来使用了
//将原来的两个对象公共的部分抽取出来
function Person(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
this.sayHello = function () {
console.log("大家好,我叫" + this.userName + ",我今年" + this.age + "岁了");
}
}
//现在有一个学生,它有姓名,性别,年龄,学号四个属性,有一个sayHello方法,还有一个study方法
function Student(userName, sex, age, sid) {
this.sid = sid;
this.study = function () {
console.log(this.userName + "在学习");
}
//Student它是一个构造方法,被用new去调用,this是指向Student这个当前对象的
//call在这里改变了Person方法里在的this,让Person里面的this指向了Student
Person.call(this,userName,sex,age);
}
//现在又有一个老师,它有姓名,性别,年龄,教师编号四个属性,有一个sayHello的方法,和一个teaching的方法
function Teacher(userName, sex, age, tid) {
this.tid = tid;
this.teaching = function () {
console.log(this.userName + "在教书");
}
//Teacher也是构造函数,被new去调用以后this指向了当前的Teacher这个对象
//call在这里改变了Person方法里在的this,让Person里面的this指向了Teacher
// Person.call(this,userName,sex,age);
//这里也可以使用apply来实现
Person.apply(this,[userName,sex,age]);
}
//可以创建两个学生
var s1 = new Student("杨兰芳", "女", 18, "s001");
var s2 = new Student("赵聪", "男", 19, "s002");
//可以创建一个老师
var t1 = new Teacher("杨标", "男", 18, "t001");
上面的方法中我们已经通过call和apply去实现了继承关系,但是通过这种方式实现的继承关系有一个缺点就是在结构上面不能表现出继承关系以,如下图所示
我们可以看到t1与s1都是我们创建的对象,他们也确实是有我们所需要的一些属性,但是我们就是看出来哪些属性或方法是自己的,哪些是继承下来的?
通过原型来实现继承
要弄清楚这一点就必须要知道什么是“原型”
function Student(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
}
var s1 = new Student("杨兰芳", "女", 18);
s1 instanceof Student; //true
//s1对象是由Student构造出来的
上面的对象在控制台打印以后,我们看到了三个属性userName,sex,age
但是却还多了一个属性__proro__
这是为什么呢?
__proto__
是对象特殊的一个属性,指的就是对象的原型,也可以理解为这一个对象的父级,我们现在可以通过这么一个属实现对象的继承有关系
还有另外一点要说明:在JavaScript的面向对象里在,有这么一句话,一个对象的__proto__
应该是等于它的构造函数的prototype
根据上面的这句话,我们来分析。对象s1的__proto__
应该要等于它的构造函数Student
的prototype
s1.__proto__ === Student.prototype; //true
__proto__
可以设置对象的父级,那么prototype
应该也可以设置对象的父级。所以现在根据这样一个特点我们来偿试着去完成个对象的继承关系
function Person() {
this.height = height;
this.sayHello = function () {
console.log("大家好,我叫" + this.userName);
}
}
function Student(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
//思考一下,怎么样让height传给Person
}
//我如果要让Student继承自Person
var s1 = new Student("杨兰芳", "女", 18);
var s2 = new Student("赵聪","男",19);
第一种办法
s1.__proto__ = new Person();
s1.sayHello(); //调用了父级了sayHello方法
s2.__proto__ = new Person();
s2.sayHello();
在这种办法里面虽然可以实现继承,但是我次创建对象都要去手动的设置__proto__
这样很麻烦
第二种办法
根据之前的结论,一个对象的__proto__
应该等于它的构造函数的prototype
所以我们的代码可以做如下更改
Student.prototype = new Person();
通过第一种办法与第二种办法也有缺点,缺点如下
function Person(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
this.sayHello = function () {
console.log("大家好,我叫" + this.userName);
}
}
function Student(userName, sex, age, sid) {
this.sid = sid;
this.study = function () {
console.log(this.userName + "在学习");
}
}
//怎么样让Student继承Person以后还可以传参呢
怎么样让Student继承Person以后还可以传参呢这个时候我们就发现我们不好传参
Student.prototype = new Person(姓名参数,性别参数,年龄参数);
//现在的Person需要三个参数,但是我们的Student还没有创建,所以我根本就不知道到底应该传什么过去
还是牢牢记住我们之前的一句话,一个对象的__proto__
应该是等于它的构造函数的prototype
最终办法
function Student(userName,sex,age,sid){
//这是一个构造方法,这个访求是被new调用的,所以this一定指向当前对象
this.sid = sid;
this.study = function(){
console.log(this.userName + "在学习");
}
//__proto__ === prototype
//this.__proto__ == Student.prototype;
this.__proto__ = new Person(userName,sex,age);
}
有了这个最终办法以后,我们就可以轻松的去实现对象的继承关系,并且能够清淅的知道对象的结构
对象的包含关系
对象的包含主要指的是一个对象包含另一个对象,例如学生对象是属于班级的(这是一个非常简单的东西,我们一带而过就行了)
//人的构造函数
function Person(userName, sex, age) {
this.userName = userName;
this.sex = sex;
this.age = age;
}
//学生的构造函数
function Student(userName, sex, age, sid) {
this.sid = sid;
this.study = function () {
console.log(this.userName + "在学习");
}
this.__proto__ = new Person(userName, sex, age);
}
//老师的构造函数
function Teacher(userName, sex, age, tid) {
this.tid = tid;
this.teaching = function () {
console.log(this.userName + "在教书");
}
this.__proto__ = new Person(userName, sex, age);
}
//上面还是最基本的继承关系
var biaogege = new Teacher("标哥哥", "男", 18, "T001");
var s1 = new Student("杨兰芳", "女", 18, "S001");
var s2 = new Student("梁心悦", "女", 18, "S002");
//假设现在有一个班级对象了
var classInfo = {
className: "H2001",
classAddress: "湖北省武汉市江夏区光谷大道金融港",
//班级里面老师,这个老师,应该也是一个对象
teacher: biaogege,
//班级里面应该还有学生,但是不可能只有一个学生,所以它是包含多学生的
students: [
s1, s2
]
};
现在我们可以看到,一个班级里包含了一个教师对象,也包含了学生对象的数组
作业与练习
-
现在现个动物分别小猫Cat和小狗Dog,Cat构造函数里面有性别,年龄,昵称和体重weight四个属性,而Dog构造函数里面有性别,年龄,昵称 ,和身高height四个属性,猫与狗有一个共同的方法叫sleep睡觉,但是猫有一个方法
miao
,狗有一个方法叫wang
现在请列举出Cat与Dog的构造函数,并且提取公共部分使用继承
突然之间又有一个小动画,农家田园犬Pastoral,它也是属于狗的类别,也具备Dog所有的属性,但是它还有一个方法是看门
Janitor
现在请创建
Pastoral
的构造函数,并实现继承关系猫与狗它们都是动物,所以肯定具备动物的特点(性别,年龄,昵称)
同时它们还有自己各自的特点
最后又有一个农家田园犬,它也是属性狗的类别,所以应该也有狗的特点
我们可以根据上面的分别来实现对象的继承关系
TypeError: Cannot read properties of undefined (reading 'v')
//动画的构造函数 function Animal(sex, age, nickName) { this.sex = sex; this.age = age; this.nickName = nickName; this.sleep = function () { console.log(this.nickName + "在睡觉"); } } //猫的构造函数 function Cat(sex, age, nickName, weight) { this.weight = weight; this.miao = function () { console.log("喵喵喵"); } //相当于继承了Animal Animal.call(this, sex, age, nickName); } //狗的构造函数 function Dog(sex, age, nickName, height) { this.height = height; this.wang = function () { console.log("㕵㕵㕵"); } //继承了Animal Animal.call(this, sex, age, nickName); } //农家田园犬 function Pastoral(sex, age, nickName, height) { this.janitor = function () { console.log("看门"); } // Dog.call(this, sex, age, nickName, height); Dog.apply(this,[sex, age, nickName, height]); } var xiaohua = new Cat("母", 3, "小花", 4); var dahuang = new Dog("公", 5, "大黄", 100); var heizi = new Pastoral("公", 7, "黑子", 100);
上面的call的方法也可以使用apply的方法去完成
评论区