Spirit 带你彻底搞懂 JS 的6种继承方案

01-原型链的继承方案

function Person(){
    this.name="czx";
}
function Student(){}
var p1=new Person();
Student.prototype=p1;
var student1=new Student();
console.log(student1); // Person{}
console.log(student1.name); // czx

这是最简单的一种方案,同时也是弊端最多的方案,我们来分析下他的弊端

1、如果直接打印Student的实例对象,打印出来是这样

图片说明

2、为啥不是打印出来的Student呢,我们先得了解一个东西,打印出来的这个名称是由constructor决定的
我们直接将Person的实例对象赋值给Student的prototype,Student原来的prototype就被覆盖了
更多技术文章、面试资料、工具教程,还请移步:http://www.javatiku.cn/
3、通过这种方式继承过来,打印学生的实例对象,继承过来的属性是看不见的

4、如果在父类Person上添加引用类型的数据,如果我们创建多个学生对象实例,修改了引用类型的数据,那么父类中的数据也是会被改变的,那么实例的对象,相互之间就不是独立的,可以看下下面这段代码

function Person(){
    this.friends=[];
}
function Student(){}
var p1=new Person();
Student.prototype=p1;
var student1=new Student();
var student2=new Student();
student1.friends.push("czx");
console.log(student2.friends); // ["czx"]

无法传递参数

细心的同学可能就会发现,我这样写是无法传递参数的.那以后我们要传递参数的话,就会相当麻烦

所以,接下来我们看第二种方式

02-借用构造函数实现继承

function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 第一个弊端: Person执行了两次
// 第一次是new出来的,第二次是call执行的
// 第二个弊端是 在new的时候,会往student上加上一些不必要的属性,会把继承的属性加到了Student上面,这是没必要的

Student.prototype = new Person();
Student.prototype.studying = function () {
    console.log(this.name + "再学习");
}
function Student(name, age, sno) {
    Person.call(this, name, age);
    this.sno = sno;
}

let stu1 = new Student("czx", 18, 180047101);
stu1.studying();
console.log(stu1);

这个方案解决了第一种方案的(2),(3),(4)弊端

这两个放的顺序一定不能放错了

Student.prototype = new Person();
Student.prototype.studying = function () {
    console.log(this.name + "再学习");
}

因为在我们内直接使用call调用了Person并执行了,那么Person的属性也都可以在Student里面使用,这也能使的我们可以传递属性,可以复用父类中定义的属性.并且在我们打印的时候,也能把属性全部打印出来,大家可以自己试试

这个方案说实话已经很不错了,解决了大部分问题,如果简单使用也是可以的,但是这种方案就没有弊端吗?

答案是有的,我们现在来看下它的弊端
更多技术文章、面试资料、工具教程,还请移步:http://www.javatiku.cn/
第一个弊端: Person执行了两次,第一次是new出来的,第二次是call执行的

第二个弊端:在new的时候,会往student上加上一些不必要的属性,会把继承的属性加到了Student上面,这是没必要的,我们只需要在call的时候,调用Person,并且把属性放到Student上面去就行了

第一种方案的第一个弊端也没有解决

我们现在来看下一种方案

03-直接继承父类的原型

function Person(name) {
    this.name = name;
}
Person.prototype.running = function () {
    console.log(this.name + " is running");
}

function Teacher(name, career) {
    Person.call(this, name);
    this.career = career;
}
function Student(name, age) {
    Person.call(this, name);;
    this.age = age;
}

// 这种方法可不可行呢?
// 答案是不行
// 我们看下面的例子
// 我明明是给学生单独加的属性,但是teacher也可以共用它,这是不合理的
// 我们需要的是学生和老师自己的原型上是属于自己独一无二的属性
// 这样子可复用性才会高
Student.prototype = Person.prototype;
Teacher.prototype = Person.prototype;
Student.prototype.studying = function () {
    console.log(this.name + " is learning");
}

var s1 = new Student("czx", 18);
var t1 = new Teacher("teacher", "math");
console.log(s1);
s1.running()
s1.studying();
t1.studying();

这种方案是基于第二种方案的一个改版,但是我们看下这种方案可不可行

看完代码,大家也知道不行了,我在代码上也有详细的注释

1、我明明是给学生单独加的属性,但是teacher也可以共用它,这是不合理的,这是因为我们加的方法都是直接加在了父类的原型上

2、我们需要的是学生和老师自己的原型上是属于自己独一无二的属性

3、这样子可复用性才会高
更多技术文章、面试资料、工具教程,还请移步:http://www.javatiku.cn/

#学习路径#
全部评论
谢谢楼主分享!!!太棒了
点赞 回复 分享
发布于 2022-02-13 00:03

相关推荐

点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务