js原生继承实现

本文详细探讨了JavaScript中的四种继承实现:原型链继承、借用构造函数继承、组合继承以及最佳实践。针对每种方式,分析了其特点、优势和存在的问题,如属性共享、constructor指向、方法继承等,最后提出了改进的继承策略以避免不必要的资源浪费。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.原型链继承

子类prototype指向父类实例的prototype

function Person() {
    this.isPerson = true;
}

Person.prototype.sayHello = function() {
    console.log('Hello!');
}

function Student(grade){
    this.grade = grade;
}
Student.prototype = new Person();
Student.prototype.sayGrade = function(){
    console.log("I am Grade "+this.grade);
}

但该方法有几个问题:

1. constructor指向问题。Student的prototype其实是Person的,constructor指向的也是Person。

2.属性共享问题。如果Person实例上有引用值属性,那么不同的Student实例都是用的这个引用值,会有数据污染问题

3.参数。只能用父类的参数

二:借用构造函数继承

借用父类构造函数,可以传递参数

function Person(name) {
    this.name = name;
}
Person.prototype.sayHello = function() {
    console.log('Hi! I am ' + this.name);
}

function Student(name,grade){
    Person.call(this,name);
    this.grade = grade;
}
Student.prototype.selfIntroduce = function(){
    console.log( 'My name is '+this.name+'. I am Grade '+this.grade);
}

缺陷:无法继承父类原型链上的方法

三:组合继承

结合一和二的组合实现

function Person(name) {
    this.name = name;
}
Person.prototype.sayHello = function() {
    console.log('Hi! My name is ' + this.name + '.');
}

function Student(name,grade) {
    Person.call(this,name); //调用父类构造函数,在内部获取父类的name属性
    this.grade = grade;     //定义自己的属性
}
Student.prototype = new Person();  //原型指向父类实例,打通原型链
Student.prototype.constructor = Student;  //将父类原型链的constructor指向子类构造函数
Student.prototype.sayGrade = function() {
    console.log('I am Grade ' + this.grade + '.');
}

// 可以正确运行下面代码:
var student = new Student('Cover', 4);
console.log(student.name); // 'Cover'
student.sayHello(); // 'Hi! My name is Cover.'
student.sayGrade(); // 'I am Grade 4.'
student.hasOwnProperty('name');  // true

该方法好处:

        1. 属性和方法都是从父类继承的(代码复用)

        2. 继承的属性是私有的,互不影响

        3. 继承的方法都在原型里

该方法依然有不足,重复调用,属性冗余。注意到上面Persion方法被调了两次,这个会比较浪费。而且,Student.prototype = new Person()这一句生成一个name=undefined的person实例,但是这个实例是用于继承的,没必要存在,也会造成浪费

四:最佳实践

鉴于三的缺陷,进行改进:不必重复调用父类构造函数,只需继承原型

function Person(name){
    this.name = name
}

Persion.prototype.sayName = function(){
    console.log(this.name)
}

function Student(grade, name){
    Person.call(this, name) //执行父类构造,在子类添加父类的属性
    this.grade = grade
}

inheritPrototype(Student, Person)

Student.prototype.sayGrade = function(){
    console.log(this.grade)
}


//该方法主要将父构造函数的原型复制出来,将复制的原型的constructor指向子类构造函数,最后将其设
//为子类原型即可。
function inheritPrototype(subType, superType){
    let newProto = Object.create(superType.prototype);
    newProto.constructor = subType;
    subType.prototype = newProto
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值