这是一篇有关JS中对象属性与方法是如何继承的文章。下面的所有内容皆是建立在我现在的所学知识基础上来写的,各位注意甄别。接下来我们将直奔主题。
最后面有补充内容——JS原型与原型链关系详图
继承的作用就是实现代码的复用,减轻编程工作量以及尽可能的节约内存。
JS中子类继承父类的属性的写法就一种:里call()方法。
JS中子类继承父类的方法的写法有两种:第一种是直接改变子类的原型对象的父类,第二种是改变子类的原型对象。
现在先来创建一个Person类和几个Person类的方法,在最后,Person类将会作为Student类的父类。
// name, age, sex 这三个形参是作为Person这个父类的子类所共有的属性,因此将其抽离出来放在这里
function Person(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
}
// 这是Person类中的方法
Person.prototype.sayHello = function(){
console.log(`你好,我是${this.name}`);
}
Person.prototype.cry = function(){
console.log("你弄疼我了,哇呜呜呜呜呜呜呜呜呜~~~~");
}
接下来我们编写创建一个Student类并且让Student类来继承Person类的属性的代码快
//Student类
function Student(name, age, sex){
// 这里的call方法中的参数在call()被执行的时候会将其中的参数传递到调用他的类(这里是Person)中,先对Person类中的this的指向进行修改,改为Student这个类中的this,之后将name,age,sex也分别替换掉对应的形参。最终结果就是使得Person类中的属性键值对成为Student的属性键值对,并且这些属性还是带了实际值的。
Person.call(this, name, age, sex);
}
再接着就是编写让Student类来继承Person类的方法的代码块
(1)先使用第一种方法连接Student类与Person,这种方法是直接改变子类(Student)的原型对象的父类(Object)。
Student.prototype.__proto__ = Person.prototype; // 可以翻译为:将Person这个构造函数的原型对象的引用赋值给Student这个构造函数的原型对象的__proto__属性。
var stu = new Student("张酒瓶", 21, "男"); //生成一个Student类的实例stu
console.log(`你好,我是 ${stu.name} ,今年 ${stu.age} 岁,我是个 ${stu.sex} 生`);
在这第一种方法中他们的关系结构图如下:
图中的绿色部分是改变指向后的子类最新也是现在发挥作用的指向,而原来指向Object的原型对象的指向已经被废弃,没用啦。
运行结果为:
(2)现在是第二中方法连接Student类与Person,这种方法是改变子类的原型对象来达到目的。
Student.prototype = new Person();//这句话可以翻译为:先创建一个Person类的匿名实例对象,然后将这个匿名实例对象的引用赋值给Student类的prototype属性。
Student.constructor = Student;//翻译为:将Student的引用赋值给Student的原型对象;
var stu = new Student("张九平", 21, "男");
console.log(stu.name,stu.age,stu.sex);
stu.sayHello();
stu.cry();
在这第二种方法中他们的关系结构图如下:
在这张图中的红色部分是原先为改变指向的时候的属性指向,那时候并没有这些绿色部分的存在,但是在修改后,现在的绿色部分才是正确存在的指向关系,而红色部分将会被丢弃回收。
执行结果为:
这下面是我的草稿,上面的内容均是出自其中,但是原稿写的有些乱,要是有兴趣的话可以看。
// Person类将会成为主类
// name, age, sex 这三个形参是作为Person这个父类的子类所共有的属性,因此将其抽离出来放在这里
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// 这是父类中的方法,也是所有子类都可以使用的方方法!我们需要将这些方法放到原型对象中供子类访问
Person.prototype.sayHello = function () {
console.log(`你好,我是${this.name}`);
}
Person.prototype.cry = function () {
console.log("你弄疼我了,哇呜呜呜呜呜呜呜呜呜~~~~");
}
//Student类
function Student(name, age, sex) {
// 这里的call()中的this在call()被执行的时候会将其中的参数传递到调用他的类(这里是Person)中,先对Person类中的this的指向进行修改,改为Student这个类中的this,之后将name,age,sex也分别替换掉对应的形参。最终结果就是使得Person类中的属性键值对成为Student的属性键值对,并且这些属性还是带了实际值的。
Person.call(this, name, age, sex);
}
//这里开始对父类Person的原型对象和子类Student的原型对象进行“连接”,从而使得Person这个父类的原型对象中的方法可以被子类所使用。 方法有两种:一种是直接改变Student的原型对象的父类 一种是改变Student的原型对象
// 第一种方法是改变子类Student的原型对象的__proto__属性中存储的关于父类原型对象的引用,让它不再指向原本的父类Object的原型对象,而是指向将来会成为父类的Person类的原型对象,就是把Person这个类的原型对象的地址引用赋值给Student的原型对象的__proto__属性,将原来Object的原型对象的引用进行覆盖。
/*
Student.prototype.__proto__ = Person.prototype;// 这句话的意思可以翻译为:将Person这个构造函数的原型对象的引用赋值给,Student这个构造函数的原型对象的__proto__属性
var stu = new Student("张酒瓶", 21, "男");
console.log(`你好,我是 ${stu.name} ,今年 ${stu.age} 岁,我是个 ${stu.sex} 生`);
*/
//第二种方法是改变子类的prototype属性中存储的原型对象的引用指向,让他不再存储原本的构造函数的伴生的原型对象的地址引用,而是改为指向Person类的实例对象
Student.prototype = new Person();//这句话可以翻译为:先创建一个Person类的匿名实例对象,然后将这个匿名实例对象的引用赋值给Student类的prototype属性。作用是让prototype属性从此指向该实例对象,并将该实例对象作为Person这个构造函数的原型对象来使用。 不足时这个实例对象虽然是被当作了Student类的原型对象,但是它自身并没有一个名为constructor的属性用来指向Student,所以需要一步来将Student这个构造函数的引用赋值给这个Person类匿名实例对象。
Student.constructor = Student;//翻译为:将Student的引用赋值给Student的原型对象;
var stu = new Student("张九平", 21, "男");
console.log(stu.name, stu.age, stu.sex);
stu.sayHello();
stu.cry();
JS原型与原型链关系详图
这是我的第一篇博客,如有不足之处,请多指教