构造函数的特点:封装, 继承, 多态

继承: 子类获取父类的属性和方法的过程

1. 原型链的继承

让一个引用类型继承另一个引用类型的属性和方法

//父级
	//构造函数
function Person(name,sex){
    this.name=name;		//属性
    this.sex=sex;
    this.poop=function(){	//方法
        console.log('hi')
    }
}
	//原型 
Person.prototype.hobby=["11","22"]
Person.prototype.say=function(){
    console.log('hello')
}

//继承的子级
function Student(){}
Student.Pertotype=new Person('小白','男')

//实例化对象
var stu1=new Student();
var stu2=new Student();

优点:

可以把父级原型的属性和方法继承过来,简单,易于实现

缺点:

1. 在父级构造函数中继承过来的属性值是一样的,无法实现多继承

(通过原型来实现继承时,原型会变成另一个类型的实例,原先的实例属性变成了现在的原型属性

该原型的引用类型属性会被所有的实例共享。)

2. 无法给父级构造函数传参

(在创建子类型的实例时,没有办法在不影响所有对象实例的情况下给超类型的构造函数中传递参)

2. 构造函数继承

在子类型的构造函数中调用父类型构造函数

//父级
function Person(name,sex){
    this.name=name;		//属性
    this.sex=sex;
}
Person.prototype.say=function(){
    console.log('hi')
}

function Student(name,sex){
    Person.call(this,name,sex)
}
var stu = new Student('小白',18,'男')
console.log(stu)

优点:

可以继承父级构造函数中的属性,并可以赋值

(相当于复制父类的实例给子类,没有用到原型)

缺点:

父级原型的属性和方法访问不到

(只能使用父类的属性和方法,不能继承原型的属性和方法)

3. 组合继承(原型链+借用构造函数)

使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

//父级
function Person(name,sex){
    this.name=name;		//属性
    this.sex=sex;
}
Person.prototype.say=function(){
    console.log('hi')
}

//子类
function Student(name, sex){
    Person.call(this,name,sex)		//构造函数继承
}
Student.prototype=new Person();    //原型链继承

var stu=new Student('小白',"男") 		//实例化对象

缺点:

什么情况都会调用两次超类型的构造函数,一次创建子类原型,一次在子类型构造函数内部

优点:

1. 可以向超类传递参数

2. 每个实例都有自己的属性

3. 实现了代码复用

4. 原型式继承

ES5 通过新增 Object.create()方法规范了原型式继承

function object(obj) {
    //自定义一个构造函数
    function Per(){}
    //把传入的obj对象赋给 构造函数的原型
    //这样原型方法中有了共有的属性
    Per.prototype = obj;
    return new Per();
}
var Per={
    name:'小白',
    arr:["11","22","33"]
}
//将Per通过形参传到object函数中
var aa = object(Per)

console.log(aa)
//打印结果就是
Per {}
__proto__:
	arr: (3) ["11", "22", "33"]
	name: "小白"
	__proto__: Object

缺点:跟原型链实现继承一样,包含引用类型值的属性会被所有实例共享

5. 寄生式继承

是与原型式继承紧密相关的一种思路,创建一个仅用于封装继承过程的函数,

该函数在内部以某种方式来增强对象,最后再返回对象

function object(o){
    function F() {}
    F.prototype = o;
    return new F();
}
function creObj(val){
    var clone = object(val);	//通过调用函数创建一个新对象
    clone.say = function () {	//以某种方式来增强这个对象
        console.log("Hi");
    };
    return clone; //返回这个对象
}

var person = {
    name:"李白",
    friends:["杜甫","陆游"]
};

var aa = creObj(person)
aa.say();	//Hi

缺点:

使用寄生式继承来为对象添加函数,不能做到函数复用而效率低下

含引用类型值的属性会被所有实例共享

6. 寄生组合式继承

借用构造函数来继承属性,通过原型链的混成形式来继承方法

//父类
function Person(name){
    this.category = 'human';
    this.legNum = 2;
    this.name = name;
}

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

//定义要继承的方法
function inherit(subType,superType){
    //在new inheritFn 的时候将构造函数指向子类
    function inheritFn(){this.constructor = subType}
    inheritFn.prototype = superType.prototype;
    //将子类的原型指向父类原型的一个副本
    subType.prototype = new inheritFn();
}

//定义子类构造函数Pan
function Pan(name,age){
    Person.call(this,name);  //借用构造函数
    this.age = age;
}

//将子类Pan的原型指向父类Person原型的一个副本
//注意:要执行该动作后才能在Pan的prototype上定义方法,否则没用
inherit(Pan,Person); 

Pan.prototype.sayAge = function(){
    console.log(this.age);
}

//定义子类构造函数Duan
function Duan(name,hairColor){
    Person.call(this,name);
    this.hairColor = hairColor;
}

inherit(Duan,Person);

Duan.prototype.showHairColor = function(){
    console.log(this.hairColor);
}

优点:

只调用了一次超类构造函数,效率更高

7. ES6 类实现继承

//class 相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class P7 extends School1{
    constructor(name) {
        super(name)
        // this.type = "我们都是帅逼"
    }
}
var className = new P7('小白')
console.log(className)