JavaScript-继承的实现方式

继承的方式
1、传统形式 ----> 原型链

缺点一:过多的继承了没用的属性

原型链继承eg:

Grand.prototype.lastName = 'Hong'
function Grand(){}
var grand = new Grand()
Father.prototype = grand;
function Father(){}
var father = new Father()
Son.prototype = father;
function Son(){}
var son = new Son();
console.log(son.lastName);//沿着原型链继承了Grand.prototype的lastName属性,但是也会继承grand、father之中的一些多余属性

缺点二:对原型中引用类型值的误修改。

eg:

//父类:人
function Person () {
    this.head = '脑袋瓜子';
}
//子类:学生,继承了“人”这个类
function Student(studentID) {
    this.studentID = studentID;
}
Student.prototype = new Person();

var stu1 = new Student(1001);
console.log(stu1.head); //脑袋瓜子

stu1.head = '聪明的脑袋瓜子';
console.log(stu1.head); //聪明的脑袋瓜子

var stu2 = new Student(1002);
console.log(stu2.head); //脑袋瓜子

以上例子,我们通过重写 Student.prototype 的值为 Person 类的一个实例,实现了 Student 类对 Person 类的继承。所以 ,stu1 能访问到父类 Person 上定义的 head 属性,打印值为“脑袋瓜子”。我们知道,所有的 Student 实例都共享着原型对象上的属性。那么,如果我在 stu1 上改变了 head 属性值,是不是会影响原型对象上的 head 值呢?看我上面的代码就知道,肯定是不会。stu1 的 head 值确实是改变了,但是我重新实例化的对象 stu2 的 head 值仍旧不变。

这是因为,当实例中存在和原型对象上同名的属性时,会自动屏蔽原型对象上的同名属性。stu1.head = “聪明的脑袋瓜子” 实际上只是给 stu1 添加了一个本地属性 head 并设置了相关值。所以当我们打印 stu1.head 时,访问的是该实例的本地属性,而不是其原型对象上的 head 属性(它因和本地属性名同名已经被屏蔽了)。

刚才我们讨论的这个 head 属性是一个基本类型的值,可如果它是一个引用类型呢?

其实原型对象上任何类型的值,都不会被实例所重写/覆盖。在实例上设置与原型对象上同名属性的值,只会在实例上创建一个同名的本地属性。

但是,原型对象上引用类型的值可以通过实例进行修改,致使所有实例共享着的该引用类型的值也会随之改变。

eg:

//父类:人
    function Person () {
      this.head = '脑袋瓜子';
      this.emotion = ['喜', '怒', '哀', '乐']; //人都有喜怒哀乐
    }
    //子类:学生,继承了“人”这个类
    function Student(studentID) {
      this.studentID = studentID;
    }
    Student.prototype = new Person();

    var stu1 = new Student(1001);
    console.log(stu1.emotion); //['喜', '怒', '哀', '乐']

    stu1.emotion.push('愁');
    console.log(stu1.emotion); //["喜", "怒", "哀", "乐", "愁"]
    
    var stu2 = new Student(1002);
    console.log(stu2.emotion); //["喜", "怒", "哀", "乐", "愁"]

因此,我们得出结论,原型上任何类型的属性值都不会通过实例被重写,但是引用类型的属性值会受到实例的影响而修改

2、借用构造函数实现继承

原理:在子类的构造函数中,通过 apply ( ) 或 call ( )的形式,调用父类构造函数,以实现继承

缺点:不能继承借用构造函数的原型

​ 每次构造函数都要多走一个函数

​ 不是严格意义上的继承

Person.prototype.lastName = "yaxi"
function Person(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}

function Student(name, age, sex) {
    Person.call(this, name, age, sex);
}
var student = new Student("hong", 14, "male");
//this指向了student,==>person.calll(student),就相当于 student.Person( )。最后,student 去调用 Person 方法时,Person 内部的 this 指向就指向了 student。那么Person 内部this 上的所有属性和方法,都被拷贝到了 student 上
console.log(student) //{'hong',14,'male'}
console.log(student.lastName); //undefined,不能继承Person原型上面的属性
3、共享原型继承

缺点:不能随便改变自己的原型

//共享原型
Teacher.prototype.grade = 2017;
function Teacher() {};
var teacher = new Teacher();
function Student() {};
Student.prototype = Teacher.prototype;//Teacher的原型跟Student的原型一样
var student = new Student();
console.log(student.grade);//2017
Student.prototype.name = 'yaxi';//改变Student的原型将影响Teacher的原型
console.log(student.name, teacher.name) //'yaxi' 'yaxi'

teacher跟student共用一个原型Teacher.prototype

封装成一个传参类型的函数

//共享原型的传参函数
function inherit(Target, Orign) {
    Target.prototype = Orign.prototype;
}
Teacher.prototype.lastName = 'hong';
function Teacher() {}
function Student() {};
inherit(Student, Teacher)
var teacher = new Teacher();
var student = new Student();
console.log(student.lastName);//'hong'
Student.prototype.name = 'yaxi';
console.log(student.name, teacher.name);//'yaxi' 'yaxi'
4、圣杯模式继承

最好的一种继承方式

可以处理共享原型带来的不能随便更改原型的缺点

Teacher.prototype.grade = 2017;
function Teacher() {};
var teacher = new Teacher();
function F() {};//加一个空构造函数
F.prototype = Teacher.prototype;//F的原型跟Student的原型一样
function Student() {};
Student.prototype = new F();//将构造函数F的实例做为Student的原型
var student = new Student();
console.log(student.grade);//2017
Student.prototype.name = 'yaxi';//改变Student的原型将不影响Teacher的原型
console.log(student.name, teacher.name) //'yaxi' undefined

整合成一个传参类型的函数

//整合成一个传参类型的函数
function inherit(Target, Orign) {
    function F() {};
    F.prototype = Orign.prototype;
    Target.prototype = new F();
    Target.prototype.constuctor = Target;/*这是让sutdent的constuctor归位指向她自己,没有这句话时,console.log(student.constuctor)结果为Tather(),原因是student.__proto__-->new F().__prto__--->Father.prototype*/
    //真正继承自谁的说明
    Target.prototype.uber = Origin.prototype
}
Teacher.prototype.grade = 2017;

function Teacher() {};

function Student() {};
inherit(Student, Teacher); //调用函数的位置一定要放对
var teacher = new Teacher();
var student = new Student();
console.log(student.lastName);//'hong'
Student.prototype.name = 'yaxi';
console.log(student.name, teacher.name);//'yaxi' 'yaxi'

更高级写法–>F函数私有化

//更高大上的写法 形成一个闭包,让F函数私有化
Teacher.prototype.grade = 2017;
function Teacher() {};
function Student() {};
//更高大上的写法 形成一个闭包,让F函数私有化
var inherit = (function() {
    var F = function() {};
    return function(Target, Orign) {
        F.prototype = Orign.prototype;
        Target.prototype = new F();
        Target.prototype.constuctor = Target;
        Target.prototype.uber = Orign.prototype;
    }
}());//立即执行行数reutrn一个函数赋值给inherit,形成闭包,立即执行函数中的F作为返回函数的私有变量可以被返回函数执行
inherit(Student, Teacher);
//调用函数的位置一定要放对
var teacher = new Teacher();
var student = new Student();
console.log(student.grade);//2017
Student.prototype.name = 'yaxi';
console.log(student.name, teacher.name)//'yaxi' undefined
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值