js面向对象三大特性之系列二 继承

继承,js面向对象的三大特点之一

1、何谓继承

  • 定义: 继承就是子类可以使用父类的所有功能,并且能够对这些功能进行扩展
  • 继承的优点: 继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码,同时,还可以重新定义某些属性、方法,即覆盖父类原有的属性和方法,使其获得与父类不同的功能

2、实现继承的方式

  • 构造函数实现继承 ---------- call/apply
  • 原型链实现继承 --------- prototype
  • 组合继承 -------- prototype + call/apply
  • 寄生继承-----通过 Object.create() 实现继承,并对其进行扩展
  • es6的extends实现继承------extends方法
2.1、借助构造函数实现继承(call、apply)---- 数据私有化

借助 call调用Parent函数

优点: 实例化后,被继承的构造函数内的属性与方法,相互独立,互不影响 (私有对私有)
缺点: 被继承的构造函数的原型对象(Parent.prototype)上的方法和属性,不会被子类不会被继承。

总结:
child私对parent:只能继承私有的,无法继承公有的;

function Parent() {
	this.age = 100
	this.arr = []
}
Parent.prototype.say = function() {
	alert(this.age)
};

function Child() {
	this.love = 'learn English'
	Parent.call(this)
}
let obj0 = new Child()
let obj1 = new Child()
obj0.age = 51;
obj1.age = 10000;
obj0.arr.push(100);
obj1.arr.push(100000);
console.log(obj0.age,obj1.age) // 51 10000     相互独立
console.log(obj0.arr,obj1.arr) //[100] [100000]  相互独立
obj0.say()                      //报错:obj0.say is not a function

上面代码相当于函数Child继承了函数Parent的私有属性,通过改变父类的this实现继承

2.2、借助原型链实现继承(prototype)---- 数据公有化

优点: 被继承的构造函数的原型对象(Parent.prototype)上的方法和属性,可以被子类继承

缺点: 由于是通过prototype实现继承,所以被继承的构造函数上的引用类型的属性是共有的,改变一个,另一个也跟着改变,eg:obj2.info,obj3.info

总结:
child公对parent公: 公;
child公对parent私:基本类型的数据是私,引用类型的数据是公;

function Parent1() {
	this.age = 30;
	this.arr = [];
}
Parent1.prototype.getName = function() {
	return '你好'
}
Parent1.prototype.info = {};

function Child1() {
	this.name = 'Tony'
}
Child1.prototype = new Parent1() //把父类的实例挂载到子类的原型链上
Child1.prototype.constructor = Child1 //把该构造函数的构造方法指向自身

let obj2 = new Child1()
let obj3 = new Child1()
obj2.age = 70;
obj3.age = 100;
obj3.arr.push(100);
obj2.arr.push(500);
console.log(obj2.age,obj3.age) // 70  100   基本类型相互独立
console.log(obj2.arr,obj3.arr) // [100, 500] [100, 500]  引用类型的属性则是共有的,一个改变,别一个也改变

obj2.info['name'] = 'test';
obj3.info['sex'] = 'man';
console.log(obj2.info,obj3.info)  // {name: "test", sex: "man"}  二者相等
console.log(obj2.getName()) // 你好    可以访问到被构造函数的原型对象上属性和方法

原型继承通过将父类的实例赋值给子类的原型,这种方法子类可以继承父类的私有属性也可以继承父类的私有方法

2.3、组合继承( prototype + call/apply ) — 私对私,公对公(推荐!!!)

实例化对象后,
公有数据通过prototype继承(改变一个,另一个也跟着改变,不过只适用于引用类型的数据),
私有数据通过call/apply继承,在其被继承的构造函数内创建后被继承

function Parent5() {
	this.name = 'name';
	this.list = []
}
Parent5.prototype.arr = [];

function Child5() {
	Parent5.call(this);   //私有对私有
	this.type = 'child5';
}

Child5.prototype = new Parent5(); // 使用原型链实现继承,公有对公有
Child5.prototype.constructor = Child5;   //修改其构造函数为其自身
       
var s7 = new Child5();
var s8 = new Child5();
s7.name = 's7-name';
s8.name = 's8-name';
console.log(s7.name,s8.name);  // s7-name s8-name
s7.list.push(100)
s8.list.push(1000);
console.log(s7.list,s8.list);  // [100]  [1000]
s7.arr.push(100);
s8.arr.push(1000);
console.log(s7.arr,s8.arr);    //  [100, 1000]    [100, 1000]

寄生组合继承就是
1、使用call继承改变this,实现私有继承私有,
2、使用原型链实现公有继承公有

这种方式看起来就没什么问题,方式一和方式二的问题都解决了,但是从上面代码我们也可以看到Parent5执行了两次,造成了多构造一次的性能开销

2.4、寄生组合式继承

是对组合继承的优化,通过 Child.prototype 间接访问到 Parent.prototype,减少一次构造函数执行。

function Parent(name, actions) {
  this.name = name;
  this.actions = actions;
}

Parent.prototype.eat = function () {
  console.log(`${this.name} - eat`);
};

function Child(id) {
  Parent.apply(this, Array.from(arguments).slice(1));
  this.id = id;
}
//封装写法
function inheritPrototype(child, parent){
    // 通过Object.create() 实现:
    // 将现有的对象挂载到新创建对象的__proto__上,从而实现新创建的对象与原对象的隔离,
    let prototype = Object.create(parent.prototype)//object(parent.prototype)
    prototype.constructor = child
    child.prototype = prototype
}
inheritPrototype(Child, Parent)

const child1 = new Child(1, "c1", ["hahahahahhah"]);
const child2 = new Child(2, "c2", ["xixixixixixx"]);

为什么一定要通过桥梁的方式让 Child.prototype 访问到 Parent.prototype? 直接 Child.prototype = Parent.prototype 不行吗? 答:不行!!在给 Child.prototype 添加新的属性或者方法后,Parent.prototype 也会随之改变。

这里通过桥梁的方式就是为了让Child.prototype 与 Parent.prototype 彻底隔离开来。

如果直接Child.prototype = Parent.prototype,就会如下产生影响

function Parent(name, actions) {
  this.name = name;
  this.actions = actions;
}

Parent.prototype.eat = function () {
  console.log(`${this.name} - eat`);
};

function Child(id) {
  Parent.apply(this, Array.from(arguments).slice(1));
  this.id = id;
}

Child.prototype = Parent.prototype;

Child.prototype.constructor = Child;

console.log(Parent.prototype); // Child { eat: [Function], childEat: [Function] }

Child.prototype.childEat = function () {
  console.log(`childEat - ${this.name}`);
};

const child1 = new Child(1, "c1", ["hahahahahhah"]);

console.log(Parent.prototype); // Child { eat: [Function], childEat: [Function] }
2.5、es6 extends 继承

继承:继承可以使得子类具有父类的属性和方法并重新定义、追加属性和方法等。

class Parent {
    name_= null
    constructor() {
        this.name = 'aaa';
    }
    getName() {
        return this.name
    }
    static get(){    // class的静态方法
        return 'good'
    }
    //支持获取和设置访问器
    //使用get和set关键字对某个属性设置存值函数和取值函数,拦截该函数的存取行为
    set name(value){
        this.name_ = value
    }
    get name(){
        return this.name_
    }
}
Parent.get() //good
//通过类继承
class Child extends Parent {    
    constructor() {
        super();
    }
}
const p1 = new Child();
p1.getName();

这里通过关键字extends实现子类继承父类的私有和公有

这里需要注意如果子类里面写了constructor,就必须写super否则会报错
例子如下:

  class parent{
      constructor(){
          this.a=1
      }
      name(){
          return 2
      }
  }
  class child extends parent{
      constructor(){
          super();   // 这里不写super会报错,就会报错,报错信息如下 Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
      }
      
  }
  let A = new child()
  console.log(A.a)  // 1
  console.log(A.name())  // 2

在这里插入图片描述

参考链接:https://www.cnblogs.com/mengxiangji/p/10399066.html
参考链接:https://mp.weixin.qq.com/s/mnQde8T6frvYautX4Ajdxg

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值