寄生式组合继承能否优化?
问题:寄生式组合继承能否优化?
1、修改子类原型对象的引用属性,其父类的原型对象的引用属性跟着改变
2、欢迎大家来指正!
JavaScript中的多种继承方式
一、混入式继承:指的是字面量1继承字面量2;
a) 实现方式:遍历字面量2赋值给字面量1
b) 注意点:必须用[]语法,不能用点语法
c) 缺点:修改字面量1会对字面量2造成影响
# 继承的实现(混入式继承)
<script>
var dog = {
name: "啦啦小新",
age: 20,
friends: ["哗啦哗啦", "哔哩哔哩"]
}
//dog1
var dog1 = {};
//dog1 能够拥有dog对象中所有的属性和方法
//遍历dog对象,拷贝它所有的属性和方法设置到自己的身上
for (var k in dog) {
//使用[]语法
dog1[k] = dog[k];
}
console.log(dog1);
//问题:修改其中的某个对象dog1,会对原对象产生影响
//为什么:引用类型的赋值(地址)
dog1.friends.push("乌拉乌拉");
哗啦哗啦", "哔哩哔哩","乌拉乌拉"]}
console.log(dog); //{name: "啦啦小新",age: 20,friends: ["哗啦哗啦", "哔哩哔哩","乌拉乌拉"]}
</script>
二、原型式继承:通过设置其原型的方式继承
a)实现方式:
i.设置构造函数的原型对象的属性和方法(点语法),构造函数创建出来的对象自动共享原型对象中的属性和方法
ii.设置构造函数的原型对象的属性和方法(字面量法),构造函数创建出来的对象自动共享原型对象中的属性和方法
iii.设置子对象的原型对象为父对象的原型对象,再修正构造器,子对象构造函数创建出来的对象自动共享父对象的原型对象中的属性和方法
b)注意点:使用原型替换的方式实现继承的时候,原有原型对象中的属性和方法会丢失
c)缺点:
i.修改一个对象的属性和方法,会影响其他的对象
ii.只能继承原型属性和方法,无法继承实例属性和方法
# 原型式继承A
<script>
function Animal() {}
//设置原型对象
Animal.prototype.run = function() {
console.log("run");
}
Animal.prototype.type = "猫科动物";
//创建对象
//性质:构造函数创建出来的所有对象都自动拥有其对于原型对象上面的所有属性和方法
var a = new Animal();
console.log(a.type);
a.run();
</script>
# 原型式继承B
<script>
var obj = {
name: "默认的名字",
age: 20
};
function Animal() {}
//设置原型对象
Animal.prototype = obj;
//修正构造器属性
Animal.prototype.constructor = Animal;
//创建对象
//性质:构造函数创建出来的所有对象都自动拥有其对于原型对象上面的所有属性和方法
var a = new Animal();
console.log(a.name);
console.log(a.age);
</script>
# 原型式继承C
<script>
function Animal() {}
//设置原型对象
Animal.prototype.run = function() {
console.log("run");
}
function Dog() {}
//设置原型
Dog.prototype = Animal.prototype;
//创建对象
var dog = new Dog();
dog.run();
//问题:
//001 Dog 的原型对象和Animal的原型对象是同一个,不论是谁修改了原型对象会影响到另外一个
//002 只能继承Animal的原型属性和方法,无法得到其实例属性和方法
</script>
三、原型链继承:是通过原型链的方式继承
a)实现方式:
i.设置子对象的原型对象为 new 父对象构造函数
ii.子对象构造函数创建出来的对象自动共享父对象中的属性和方法(实例和原型属性和方法);
b)注意点:
i.设置原型链继承必须在设置子对象的属性和方法之前
ii.如果设置同名的实例属性或方法,会覆盖父对象中的属性和方法
iii.设置原型链继承之后,需要修正构造器属性指向
iv.完成继承后再以字面量的方式设置子对象原型,会断开继承
c)缺点:
i.修改一个对象的属性和方法,会影响其他的对象
ii.无法对父对象进行传参
# 构造函数A - B
<script>
//01 提供两个构造函数
//02 设置A构造函数的原型对象的属性和方法
//03 设置原型链继承
//04 创建B类型的对象 使用B构造函数来创建对象
function A() {
this.description = "描述信息";
this.logDes = function() {
console.log(this.description);
}
}
A.prototype.name = "A的默认名称";
A.prototype.showName = function() {
console.log(this.name);
}
function B() {};
B.prototype = new A(); //完成继承
B.prototype.constructor = B; //修正构造器
var b1 = new B();
console.log(b1.name); //A的默认名称
b1.showName(); //A的默认名称
b1.logDes(); //描述信息
</script>
四、借用构造函数继承:是指通过借用别人的方法或属性来实现继承
a)实现方式:
i.子对象构造函数中设置:父对象构造函数.call/apply(this,参数);
ii.子对象构造函数创建出来的对象自动共享父对象中的实例属性和方法(实例属性和方法);
b)注意点:call和apply方法会改变this的指向,谁调用就指向谁
c)缺点:只能继承实例属性和方法,无法继承原型属性和方法
<script>
//01 提供父类型的构造函数
function SuperType(name) {
//02 在构造函数中中设置实例属性,该属性为引用类型
this.family = ['哥哥', '姐姐', '爸爸', '妈妈'];
//实例属性
this.name = name;
};
//03 提供子类型的构造函数
function SubType() {
//经典继承|借用构造函数|伪造对象继承
//SuperType.call(this);
//构造参数传递参数
SuperType.call(this, '张老汉');
};
//04 创建父类型的实例对象,并对内部的实例化属性进行修改
var subDemo1 = new SubType();
var subDemo2 = new SubType();
console.log(subDemo1);
alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈
subDemo1.family.push('爷爷', '奶奶');
alert(subDemo1.family); //哥哥,姐姐,爸爸,妈妈,爷爷,奶奶
alert(subDemo2.family); //哥哥,姐姐,爸爸,妈妈
//测试构造函数传递参数
alert(subDemo1.name);
</script>
五、组合继承:是指借用构造函数继承和原型式继承的组合
a)实现方式:
i.子对象构造函数中设置:父对象构造函数.call/apply(this,参数);
ii.设置子对象的原型对象为父对象的原型对象;
iii.修正子对象的构造器;
iv.子对象构造函数创建出来的对象自动共享父对象中的属性和方法(实例和原型属性和方法);
b)注意点:call和apply方法是伪继承 /没有继承关系 只是把Person构造函数的属性和方法深复制一份(包括引用类型:指针和堆空间的数据)
c)缺点:修改子类原型方法和原型引用属性,父类原型方法和原型引用属性也跟着变
<script>
/*该继承缺点:修改子类原型的方法,父类原型方法也跟着变,其*/
function Person(name) {
this.name = name;
}
Person.prototype.age = 12;
Person.prototype.arr = [1,2,3];
Person.prototype.play = function() {
console.log(this.name + "playfooterball");
}
function Student(name) {
Person.call(this, name);
/*伪继承 没有继承关系 只是把Person构造函数的属性和方法深复制一份(包括引用类型:指针和堆空间的数据)*/
}
Student.prototype = Person.prototype;
Student.constructor = Student;
Student.prototype.play = function() {
console.log(this.name + "playbasketball");
}
var s1 = new Student("漳卅");
s1.arr.push(4);
console.log(s1); //Student对象
console.log(s1.age); //12
console.log(s1.arr);
s1.play(); // 漳卅playbasketball
var p = new Person("郭嘉");
p.play(); // 郭嘉playbasketball
console.log(p.arr);
</script>
六、寄生组合式继承:优化了组合继承中的修改子类原型方法,父类原型方法也跟着变的缺点
a)实现方法:
i.只需把组合继承中的设置子对象的原型对象为父对象的原型对象这一句;
ii.修改为子对象的原型对象为object.create(父对象的原型对象);就OK了
<script>
function Person(name) {
this.name = name;
}
Person.prototype.age = 12;
Person.prototype.play = function () {
console.log(this.name + "playfooterball");
}
Person.prototype.arr = [1,2,3];
function Student(name) {
Person.call(this,name);
/*伪继承 没有继承关系 只是把Person构造函数的属性和方法深复制一份(包括引用 类型:指针和堆空间的数据)*/
}
Student.prototype = Object.create(Person.prototype);
Student.constructor = Student;
Student.prototype.play = function(){
console.log(this.name + "playbasketball");
}
var s1 = new Student("漳卅");
s1.arr.push(4);
console.log(s1);//漳卅
console.log(s1.age);//12
Console.log(s1.arr);//[1,2,3,4]
s1.play();//漳卅 playbasketball
var p = new Person("郭嘉");
p.play();//郭嘉 playfooterball
console.log(p.arr);//[1,2,3,4]
</script>
上述代码:修改子类的原型引用类属性,其父类的原型引用类属性跟着改变!