对象继承
- 原型链继承
原型链作为实现继承的工具,存在的主要问题在于:原型引用值的问题(使用原型实现继承时,原型实际上变成了另一个类型的实例)。如下:
function SuperType(){
this.colors = ['red','blue','green']
}
function SubType(){ }
SubType.prototype = new SuperType()
// 以上创建了一个构造函数,并且把原型指向SuperType实例,这个原型(ProtoType)会获得自己的colors属性
let instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors)
console.log(new SuperType().colors,SubType.prototype)
/*
对比这一行console.log()当SubType通过原型继承SuperType之后,
SubType.prototype变成了SuperType的一个实例,获得了自己的color属性
所以SubType的实例共享了同一个__proto__
*/
let instance2 = new SubType()
console.log(instance2.colors)
// 所以这一个实例的color也会包含black,相当于修改instance1之后会影响instance2的color
同时还有另外一个问题:子类型实例化的时候不能给父类型构造传参,为了解决这个问题,引入了盗用构造函数的方法:
- 盗用构造函数继承(经典继承)
function SuperType(){
this.colors = ['red','blue','green'];
}
function SubType(){
SuperType.call(this);
//调用之后相当于在实例上自己定义了属于自己的colors属性,
//对比使用原型链继承color在实例的__proto__上
//但没有使用Prototype导致子类无法访问父类的方法
}
const instance1 = new SubType()
instance1.colors.push('balck')
console.log(instance1.colors)
const instance2 = new SubType()
console.log(instance2.colors)
- 结合上面两种继承方式,实现了 ==组合继承==
(()=>{
console.log('组合继承')
function SuperType(name){
this.name = name;
this.colors = ['red','blue','green']
}
SuperType.prototype.sayName = function(){
console.log(this.name)
}//加个函数印证下 2.结合原型链继承 的效果
function SubType(name){
SuperType.call(this,name)//1.盗用构造函数
}
SubType.prototype = new SuperType()//2.结合原型链继承
let instance1 = new SubType('张三');
instance1.colors.push('pink');
console.log(instance1,instance1.colors)
let instance2 = new SubType('李四');
console.log(instance2,instance2.colors)
instance2.sayName()
})()
- 原型式继承
适用于这种情况:你有一个对象,想在这个对象的基础上再创建一个对象。
(()=>{
function object(o){
function F(){};
F.prototype = o;
return new F();
}
let person = {
name:'zhangsan',
friends:['lisi','wangwu']
};
let anotherPerson = object(person);
anotherPerson.name = 'liuliu';
anotherPerson.friends.push('皮卡丘');
let yetAnotherPerson = object(person);
yetAnotherPerson.name = 'huangqi';
yetAnotherPerson.friends.push('妙蛙种子');
console.log(anotherPerson,yetAnotherPerson,)
console.log(person.friends)
})();
es5通过Object.create()将上面这个模式规范化了,上面这段代码相当于Object.create()只传入第一个参数的版本。另外,Object.create()的第二个参数和Object.defineProperties()的第二个参数一样。
- 寄生式继承
(()=>{
console.log('寄生式继承')
function object(o){
function F(){
};
F.prototype = o;
return new F()
}
let person = {
name:'zhangsan',
friend:['lisi','wangwu']
}
function createAnother(origin){
const clone = object(origin);
clone.sayHi = ()=>{
console.log('hi');
}
return clone;
}
const anotherPerson = createAnother(person)
anotherPerson.sayHi()
})()
- 寄生式组合继承 上面3提到中组合式继承中,父类的构造函数执行了两次(一次在SubType盗用中,另一次在改写SubType的ptototype时创建对象),并且创建的对象instance和instasnce.__proto__上有同名属性,效率低,寄生式组合继承解决了这个问题
(()=>{
console.log('寄生式组合继承');
function object(o){
function F(){};
F.prototype = o;
return new F();
}
function inheritPrototype(subType,superType){
let prototype = object(superType.prototype);//创建对象
prototype.constructor = subType;
subType.prototype = prototype;
// 上面两行将subType的对象指向新创建的对象
}
function SuperType(name){
this.name = name;
this.colors = ['red','yellow','black']
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age
}
inheritPrototype(SubType,SuperType)
console.log(new SubType('皮卡丘',111))
// 这里只调用了一次SuperType构造函数,避免了SubType.prototype上不必要的也用不到的属性
})()
- class继承 上面提到的东西用es5的特性来模拟继承,每种策略都有对应的策略和妥协,因此代码都冗长和混乱
- 如果不要传入参数,去掉后面的括号也是可以的例如
class F{} const f = new F - class定义的数据和原型链的关系(设构造函数为F,实例为f)
- class能包含什么?
- 构造函数(Constructor):构造函数是一个特殊的方法,用于在创建对象时初始化对象的状态。它的名称始终是constructor。 位置:F的Prototype上
- 属性(Properties)
- 静态属性:静态属性是定义在类本身上而不是实例上的属性。它们使用static关键字来声明。 位置:F的Prototype上的constructor上,符合只能在class调用的直觉,因为constructor就是class F
- 实例属性:实例属性是在类的构造函数中定义的,或者在其他方法中动态添加到对象上的属性。 位置:在实例f上
- 方法(Methods)
- 静态方法:静态方法是定义在类本身上而不是实例上的方法。它们使用static关键字来声明,并且只能通过类本身来调用,而不能通过实例来调用。 位置:F的Prototype上的constructor上
- 实例方法:实例方法是定义在类原型上的函数,可以通过类的实例来调用。 位置:在Prototype上
- 存取器(Getters and Setters) 存取器是特殊的方法,用于定义对对象属性的访问和修改行为。它们使用get和set关键字来声明。 位置:在Prototype上
- 私有字段和私有方法 私有字段在f上 私有方法在f的PrivateMethods(是一个数组)上
class F{ constructor(){ this.name = '实例属性name' //在f上 } name1 = '实例属性name1' //在f上 get name1(){ return this.name1 + '111' //在prototype上 } set name1(val){ //在prototype上 console.log(val) this.name1 = val + 'set' } static age = '静态属性age' //在constructor上 static sayAge(){ //在constructor上 console.log('静态方法sayAge',this.age) } saySomething(){ //在Prototype上 console.log('实例方法') } #name111 = 1; //在f上 #name222(){ //在f的privateMethods数组上 console.log('name222') }; } const f = new F() console.log(f)
- class能包含什么?
完整代码
(()=>{
console.log('原型模式')
function SuperType(){
this.colors = ['red','blue','green']
}
function SubType(){ }
SubType.prototype = new SuperType()
// 以上创建了一个构造函数,并且把原型指向SuperType实例
let instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1,instance1.colors)
// console.log(new SuperType().colors,SubType.prototype)
/*
对比这一行console.log()当SubType通过原型继承SuperType之后,
SubType.prototype变成了SuperType的一个实例,获得了自己的color属性
所以SubType的实例共享了同一个__proto__
*/
let instance2 = new SubType()
console.log(instance2,instance2.colors)
// 所以这一个实例的color也会包含black
})();
(()=>{
console.log('盗用构造函数继承')
function SuperType(){
this.colors = ['red','blue','green'];
}
function SubType(){
SuperType.call(this);//调用之后相当于在实例上自己定义了属于自己的colors属性
}
const instance1 = new SubType()
instance1.colors.push('balck')
console.log(instance1,instance1.colors)
const instance2 = new SubType()
console.log(instance2,instance2.colors)
})();
(()=>{
console.log('组合继承')
function SuperType(name){
this.name = name;
this.colors = ['red','blue','green']
}
SuperType.prototype.sayName = function(){
console.log(this.name)
}//加个函数印证下 2.结合原型链继承 的效果
function SubType(name){
SuperType.call(this,name)//1.盗用构造函数
}
SubType.prototype = new SuperType()//2.结合原型链继承
let instance1 = new SubType('张三');
instance1.colors.push('pink');
console.log(instance1,instance1.colors)
let instance2 = new SubType('李四');
console.log(instance2,instance2.colors)
instance2.sayName()
})();
(()=>{
console.log('原型式继承')
function object(o){
function F(){};
F.prototype = o;
return new F();
}
//
let person = {
name:'zhangsan',
friends:['lisi','wangwu']
};
let anotherPerson = object(person);
anotherPerson.name = 'liuliu';
anotherPerson.friends.push('皮卡丘');
let yetAnotherPerson = object(person);
yetAnotherPerson.name = 'huangqi';
yetAnotherPerson.friends.push('妙蛙种子');
console.log(anotherPerson,yetAnotherPerson)
console.log(person.friends)// 也存在引用值的问题
// 这里实际上克隆了两个person
})();
(()=>{
console.log('寄生式继承')
function object(o){
function F(){
};
F.prototype = o;
return new F()
}
let person = {
name:'zhangsan',
friend:['lisi','wangwu']
}
function createAnother(origin){
const clone = object(origin);
clone.sayHi = ()=>{
console.log('hi');
}
return clone;
}
const anotherPerson = createAnother(person)
anotherPerson.sayHi()
})()
(()=>{
console.log('寄生式组合继承');
function object(o){
function F(){};
F.prototype = o;
return new F();
}
function inheritPrototype(subType,superType){
let prototype = object(superType.prototype);//创建对象
prototype.constructor = subType;
subType.prototype = prototype;
// 上面两行将subType的对象指向新创建的对象
}
function SuperType(name){
this.name = name;
this.colors = ['red','yellow','black']
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age
}
inheritPrototype(SubType,SuperType)
console.log(new SubType('皮卡丘',111))
})()