JS继承的几种方式

255 阅读3分钟

继承的概念
  继承,封装,多态是面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。

继承的方式

  1. 原型链继承:
//父类
function Parent(name) {
    this.name = name;			//实例属性
    this.list = [1, 2, 3];
    this.sex = 'male';
}
Parent.prototype.color = 'yellow';	//原型属性

//子类
function Son(age) {
	this.age = age;
}

//将子类原型指向父类实例(将父类的实例属性赋值给子类原型)
 Son.prototype=new Parent();
 
let son1 = new Son(10);
let son2 = new Son(11);
console.log(son1.list);	//[1, 2, 3]
console.log(son2.list);	//[1, 2, 3]
console.log(son1.sex);	//male
console.log(son2.sex);	//male

//该继承方法会共享父类属性的值(其他文章说是共享引用类型的值,我个人不这么认为),且无法往父类构造函数传值
son1.list.push(4);
console.log(son1.list);	//[1, 2, 3, 4]
console.log(son2.list); //[1, 2, 3, 4]
//有小伙伴可能会说,这时候没有共享
son1.sex='Female'
console.log(son1.sex);	//Female
console.log(son2.sex);	//male
//son1.sex是给son1这个实例新增了sex属性,按照查找顺序,如果在实例上找到了属性,就不会继续往上查找,所以这里的sex的值不是原型上的sex属性的值
//son1.list.push其实是操作原型上的数组
console.log(son1) // {age: 10, sex: "Female"}	//sex属性是son1.sex新增的
console.log(son1.__proto__) //Parent {name: undefined, list: Array(4), sex: "male"}
console.log(son2) // {age: 11}	//sex属性是子类实例所没有的,而是来自原型对象
console.log(son2.__proto__) //Parent {name: undefined, list: Array(4), sex: "male"}

缺点:1.子类不能往父类的构造函数传值;
   2.所有子类共享父类属性的值;

  1. 构造函数继承
   	function Parent(name) {
        this.name = name;
        this.list = [1, 2, 3];
        this.sex = 'male'
    }

    Parent.prototype.color = 'yellow';

    function Son(age, name) {
        Parent.call(this, name) //这一句实现继承,创建子类实例时调用父类构造函数
        this.age = age;
    }

    let son1 = new Son(10,'一个名字')
    let son2 = new Son(10,'又是一个名字')
    console.log(son1.list);	//[1, 2, 3]
	console.log(son2.list);	//[1, 2, 3]
    //该继承方法解决了原型链继承共享父类属性值的缺点
    son1.list.push(4);
	console.log(son1.list);	//[1, 2, 3, 4]
	console.log(son2.list); //[1, 2, 3]
    //但是无法继承父类原型上的属性和方法
    console.log(son1.color) //undefined
    

缺点:1.无法继承父类原型上的属性和方法;
   2.父类不在子类的原型链上;
   3.父类的实例方法不能复用;

  1. 组合继承
	function Parent(name) {
        this.name = name;
        this.list = [1, 2, 3];
        this.sex = 'male'
    }

    Parent.prototype.color = 'yellow';

    function Son(age, name) {
        Parent.call(this, name) 	
        this.age = age;
    }
    
    Son.prototype=new Parent();
    Son.prototype.construct = Son;
    
    let son1 = new Son(10,'一个名字')
    //该继承方法兼备原型链继承和构造函数继承的优点,但是会造成属性重复多余
    // Son.prototype=new Parent(); 将父类属性加到子类原型上
    // Parent.call(this, name); 将父类实例属性传递给子类
    //子类的实例和原型上会有重复的属性,实例的属性将原型上的属性覆盖。
    console.log(son1)	//{name: "一个名字", list: Array(3), sex: "male", age: 10}
    console.log(son1.__proto__)	//{name: undefined, list: Array(3), sex: "male"}

缺点:1.造成属性重复多余;

  1. 原型式继承
	//将已有的对象作为原型对象
    function CreateObj(obj) {
        function T() { };
        T.prototype = obj;
        return new T();
    }

    const obj = {
        name: 'name',
        list: [1, 2, 3]
    }
    
    let obj1 = CreateObj(obj)
    let obj2 = CreateObj(obj)
    //该方法和原型链继承类似,父类的属性和方法都在原型上,不同的实例共享。
    console.log(obj1.list);	//[1, 2, 3]
	console.log(obj2.list);	//[1, 2, 3]
    obj1.list.push(4);
	console.log(obj1.list);	//[1, 2, 3, 4]
	console.log(obj2.list); //[1, 2, 3, 4]

缺点:1.不同的实例共享属性和方法;

  1. 寄生式继承
//与原型继承的区别在于增强对象
    function CreateObj(obj) {
        function T() { };
        T.prototype = obj;
        T.Say = function(){		
        	console.log('Say')
        }
        return new T();
    }

    const obj = {
        name: 'name',
        list: [1, 2, 3]
    }

缺点:1.实例方法不能复用;

  1. 寄生组合继承
    function Parent(name) {
        this.name = name;
        this.list = [1, 2, 3];
        this.sex = 'male'
    }

    Parent.prototype.color = 'yellow';

    function Son(age, name) {
        Parent.call(this, name) //继承父类实例属性和方法
        this.age = age;
    }

	//只继承父类原型属性和方法
    function ChangePrototype(Son, Parent) {
        function T() { }; 
        T.prototype = Parent.prototype;
        T.prototype.construct = Son;
        Son.prototype = new T(); 
    }

    ChangePrototype(Son, Parent)

    let son1 = new Son();
    console.log(son1) // {name: undefined, list: Array(3), sex: "male", age: undefined}
	console.log(son1.__proto__) // {}
    console.log(son1.color) //yellow
  1. extends继承
class Parent {
    constructor(name) {
        this.name = name;
        this.list = [1, 2, 3];
        this.sex = 'male'
    }

    sayName() {
        console.log(this.name)
    }
}

class Son extends Parent {
    constructor(age, name) {
        super(name);
        this.age = age;
    }
}

let son1=new Son(11,'name')
son1.SayName();	//name