3、组合继承(原型链继承 + 构造函数继承)
好处
1、初始属性被继承到了自身,所以单个实例对象之间初始属性是独立的,互相没有联系。
2、结合原型链继承,多个实例之间也有共同的父对象(父的实例对象),如果未来需要
同时为多个实例附加新的属性或方法时,可以找到父对象来附加。
function 钱多多() {
this.money = 'max';
}
function 穷光蛋() {
钱多多.call(this);
this.happiness = 'max';
}
穷光蛋.prototype = new 钱多多();
穷光蛋.prototype.constructor = 穷光蛋;
const 张三 = new 穷光蛋();
const 李四 = new 穷光蛋();
李四.happiness = 'min';
李四.money = 'null';//有继承关系,但是存在多态关系,所以改变李四对象的值,张三不影响。
4、原型继承
Object.create 这个方法可以实现普通对象的继承,不仅仅能继承属性,统一也可以继承abc方法。
缺点,多个实例继承的是同一个对象,任何一个实例改变了父对象,其他实例的父对象也会跟着变。
const 马 = { money: 10000000000000 };
const 腾 = {
腾腾: { money: 10000 }
};
马.__proto__.abc = function () {
return 123
}
const 张三1 = Object.create(马);
const 李四1 = Object.create(马);
console.log(
张三1 == 李四1//false
)
console.log(
张三1.腾腾.money
)
console.log(
李四1.腾腾.money
)
//Object.create 这个方法可以实现普通对象的继承,不仅仅能继承属性,统一也可以继承abc方法。
//缺点,多个实例继承的是同一个对象,任何一个实例改变了父对象,其他实例的父对象也会跟着变。
5、寄生式继承(工厂模式实现原型继承)
//工厂模式
//生成一个新对象,让这个新对象继承参数对象,做处理,最后返回。
优缺点与4一致,只是写法上有变化,由函数返回新继承来的对象。
为什么要有寄生式继承,因为工厂模式能够很好的将继承后的子对象做二次处理,且封装,利于阅读。
const 马腾 = { money: 10000000000000 };
马腾.__proto__.abc = function () {
return 123
}
//工厂模式
//生成一个新对象,让这个新对象继承参数对象,做处理,最后返回。
function clone(obj) {
const child = Object.create(obj);
child.a = 123;
child.abc = 1;//此条会覆盖父属性abc的值。
return child;
}
const 张三2 = clone(马腾);
const 李四2 = clone(马腾);
console.log(
张三2.__proto__
)
console.log(
张三2.a
)
console.log(
张三2.abc
)
const 父 = {
长相: { 曾经: '帅' }
}
父.__proto__.abc = function () {
return 123
}
//工厂模式
//生成一个新对象,让这个新对象继承参数对象,做处理,最后返回。
function clone(父) {
const clone = Object.create(父);
clone.abc = 12;
return clone;
}
const 张三 = clone(父);
const 李四 = clone(父);
李四.长相.曾经 = '丑'
console.log(张三.abc)
6、寄生组合式继承
结合第四种中提及的继承方式,解决普通对象的继承问题的 Object.create 方法,我们在前面这几种
继承方式的优缺点基础上进行改造,得出了寄生组合式的继承方式,在es5中这也是所有继承方式里面相
对最优的继承方式。
在js中任何值都是对象(万事万物皆对象)。
所有的对象都有一个共同的祖先(对象原型),对象原型没有祖先(null)
对象的属性或方法,有私有,如果没有,会顺着原型链一直向上找,直到原型对象。
一般创建的都是第三代
function 钱多多1() {
this.money = 1000;//this执行器的执行上下文
}
function 穷光蛋1() {
钱多多1.call(this);
this.happiness = true;
}
clone(钱多多1, 穷光蛋1)
function clone(father, child) {
child.prototype = Object.create(钱多多1.prototype);
child.prototype.constructor = child;
child.prototype.abc = 123;
child.a = 123;//undefined 没有创建他的属性,所以只能在外层用子对象.属性名的形式添加属性,工厂模式使用const创建了对象
return child;
}
const 张三3 = new 穷光蛋1();//new后生成的函数对象,在执行函数后的this指向未来生成的对象,不管写不写都会有this指向
const 李四3 = new 穷光蛋1();
穷光蛋1.prototype.abc = 456;//如果外边改变创建对象的值,则子实例对象的值都会发生改变。
李四3.__proto__.abc = 789//可以要 也可以不要原型对象属性。
console.log(
张三3.__proto__.constructor == 李四3.constructor
)
console.log(
张三3.__proto__//加f才是函数,实际打出来的是对象。
)
console.log(
张三3
)
console.log(
李四3
)
console.log(
李四3.__proto__ == 穷光蛋1.prototype
function 父() {
this.长相 = { 曾经: '帅' }
}
父.prototype.abc = function () {
return 123
}
function 子() {
父.call(this);
}
clone(父, 子);
function clone(父, 子) {
//因为没有父类的实例对象,所以创造了一个全新对象(没有必要创建父类的实例对象)
子.prototype = Object.create(父.prototype);
子.prototype.constructor = 子;
子.a = 1;//undefined 没有创建他的属性,所以只能在外层用子对象.属性名的形式添加属性,工厂模式使用const创建了对象。
子.prototype.abc = 456
return 子;
}
const 张三4 = new 子();
const 李四4 = new 子();
李四4.长相.曾经 = '丑'//不能通过这种方式修改父对象属性,只能修改子对象的属性
张三4.__proto__.abc = '丑'//但是可以通过访问父对象,给对象添加属性。
console.log(张三4.长相.曾经)
console.log(李四4.长相.曾经)
console.log(张三4.abc)
console.log(李四4.abc)
console.log(李四4.a)