function Person(name?) { //Person 构造函数
this.name = name ? name: 'wq';
this.sayHi = function() {
console.log('您好');
}
}
Person.prototype.age = 30; //构造函数原型上加 age 属性
var person = new Person();//实例化对象
person.sex = '男'; // 实例对象上添加 sex 属性
1、原型链继承
function P1() {}
P1.prototype = new Person(); //P1 的原型对象直接为 Person 的实例对象
var p1 = new P1();
//输出结果
p1.name; //wq
p1.sayHi(); // 您好
p1.age; //29
p1.sex; //undefined
重点:子类的原型直接赋值了父类的原型,根据原型链查找对应属性值。
特点:子类可继承父类属性:构造函数内属性、原型对象属性、实例对象属性无法继承。
缺点:1、无法向构造函数传参 2、继承单一 3、共享父类属性,继承属性不能单独存在
2、共享原型模式继承
function P2() {}
P2.prototype = Person.prototype; // P2 共享了 Person 的原型对象
var p2 = new P2();
p1.name; // undefined
p1.sex; // undefined
p1.age; // 29
p1.say();// 报错
重点:子类共享使用了父类的原型,只能继承父类原型属性。
缺点:构造函数属性和实例属性无法继承;单一;无法传参;继承属性不独立。
3、借用构造函数继承
function P3(name) {
Person.call(this,name); // call 改变 this 指向到 Person ,盗用其属性
}
var p3 = new P3('wq');
p3.name; // wq
p3.sex; // undefined
p3.age; // undefined
p3.say();// 您好
重点:call 或 apply 将父类构造函数引进子类构造函数,继承父类构造函数属性;
子类存在和父类构造函数相同属性,就近原则取值。
call 和 apply 写在最前面
特点:只能继承父类构造函数属性,不能继承原型及其实例属性
可以传值(一个或多个),解决了原型链及其共享原型单一、传参、数据共享问题。
缺点:无法实现构造函数复用,每次使用都要调用
每个实例函数都有父类构造函数副本,臃肿
4、组合式继承(原型链 + 构造函数)
function P4(name) {
Person.call(this,name); // call 改变 this 指向到 Person ,盗用其属性
}
P4.prototype = new Person(); // 包含构造函数属性及其原型属性
var p4 = new P4('wq');
p4.name; // wq
p4.sex; // undefined
p4.age; // 29
p4.say();// 您好
重点:传参、复用
特点:继承父类的构造函数方法,可传参可复用;新实例构造函数的属性都是私有属性。
缺点:调用两次父类构造函数(耗内存)
var person = {
name: 'wq',
age: 29,
sex: '男',
sayHi:function() {
console.log('您好');
}
}
5、原型式继承
function createObj(person) {
// 方法一:
function P5(){};
P5.prototype = person; // 基于以存在对象实现,构造函数原型赋值为以存在对象
return new P5();
方法二:
return Object.create(person);
}
var p5 = createObj(person);
p4.name; // wq
p4.sex; // 男
p4.age; // 29
p4.say();// 您好
重点:基于一个以存在对象实现,将以存在对象直接赋值构造函数原型。Object.create(null)
特点:对象复制
缺点:所有构造函数原型共享一个对象,导致相互影响,无法复用。
6、寄生式继承
function createObj(person) {
// 方法一:
function P5(){};
P5.prototype = person; // 基于以存在对象实现,构造函数原型赋值为以存在对象
return new P5();
方法二:
return Object.create(person);
}
function create(person) {
return createObj(person);
}
var p5 = create(person);
p4.name; // wq
p4.sex; // 男
p4.age; // 29
p4.say();// 您好
重点:寄生式继承给原型式继承再多套一层壳子(函数)
7、寄生组合式继承
// 寄生式继承
function createObj(person) {
function P7(){};
P7.prototype = person; // 基于以存在对象实现,构造函数原型赋值为以存在对象
return new P7();
}
function create(person) {
return createObj(person);
}
var p7 = create(Person.prototype); // p7.__.proto__ === Person.prototype
//构造函数继承
function Constructorextend(){
Person.call(this);
}
Constructorextend.prototype = p7; // Constructorextend 实例既包含 Person 原型属性又包含其构造函数属性
p7.constructor = Constructorextend; //改变其数据类型
var p8 = new Constructorextend();
p4.name; // wq
p4.sex; // 男
p4.age; // 29
p4.say();// 您好
重点:寄生继承使其继承父类的原型属性;构造函数继承使其继承父类的构造函数属性;原型链继承使其构造函数的原型为寄生式继承的对象;因从父类继承需要改变其 constructor 为其子类本身。
8、ES6 继承
class Person {
constructor (name?){
this.name = 'wq' || name;
}
sayHi() {
console.log('您好');
}
}
// 使用 extends 继承其父类
class P8 extends Person {
constructor() {
super();
}
}
var p8 = new P8();
p8.name; // 'wq'
p8.say();//'您好'