为什么要继承
代码的复用可以用函数实现,对象的复用就要用继承实现。
想要继承的效果
通过向构造函数传递参数实现实例对象属性的赋值,同时多个实例属性互不干扰。但是对象的方法可以共享,简单来说就是实现“属性不共享,方法共享”
原型三角关系
继承的方式
1. 原型链继承
-- 基本思想:让子类的原型对象指向父类的实例,这样通过prototype属性就能继承父类的属性和方法
问题一: 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;
问题二: 在创建子类型(例如创建Coder的实例)时,不能向超类型(例如Person)的构造函数中传递参数.
<script>
/**
*
* 原型链继承关键是将子构造函数的prototype赋值给new 父对象的实例
* 即:Coder.prototype = new Person()
*
* 缺点:1.不能给父构造函数传递参数,
* 2.如果修改子类继承来的引用类型属性,会影响其他子类继承的引用类型属性
*
**/
//父构造函数
function Person(name, inherit) {
this.name = name;
this.inherit = inherit;
this.pinkColor = ["yellow", "white"];
this.sayName = function () {
console.log("my name is", this.name);
};
this.working = function () {
return "父类的woking";
};
}
//子构造函数
function Coder(name) {
this.name = name;
this.age = 1;
this.sayName = function () {
console.log("my age is ", this.age);
};
}
Coder.prototype = new Person();
let shaohui = new Coder("shaohui");
shaohui.pinkColor.push("black");
console.log("shaohui.pinkColor:", shaohui.pinkColor);
console.log("shaohui继承的属性inherit:", shaohui.inherit);
console.log("shaohui.working:", shaohui.working());
let duohui = new Coder("duohui");
console.log("duohui.pinkColor:", duohui.pinkColor);
console.log("duohui继承的属性inherit:", duohui.inherit);
console.log("duohui.working:", duohui.working());
</script>
运行结果及其说明:
2.借用构造函数继承
-- 基本思想:即在子类型构造函数的内部调用超类型构造函数.
问题一:方法都在构造函数中定义, 因此函数复用也就不可用了.
问题二:不能继承父类原型链上的属性和方法
<script>
<script>
/**
* 借用构造函数继承
* 其实就是在子构造里面加了:父构造函数.call(this,父亲构造函数参数)
*
* 缺点:不能继承父类原型链上的属性和方法,
* 无法实现函数复用,每个子构造函数都有父函数的副本,性能不好
* **/
//父构造函数
function Person(name, inherit) {
this.name = name;
this.inherit = inherit;
this.pinkColor = ["yellow", "white"];
this.sayName = function () {
console.log("my name is", this.name);
};
}
//不能继承父类原型链上的属性和方法,
// Person.prototype.working = () => {
// return "prototype.working";
// };
//子构造函数
function Coder(name, age, inherit) {
this.name = name;
this.inherit = inherit;
Person.call(this, this.name, this.inherit);
this.age = age;
this.sayName = function () {
console.log("my age is ", this.age);
};
}
let dalin = new Coder("dalin", 1, "dalin继承");
dalin.pinkColor.push("black");
console.log("dalin.pinkColor:", dalin.pinkColor);
console.log("dalin.inherit:", dalin.inherit);
// console.log("dalin.working:", dalin.working());
let duohui = new Coder("duohui", 2, "duohui继承");
console.log("duohui.pinkColor:", duohui.pinkColor);
console.log("duohui.inherit:", duohui.inherit);
// console.log("duohui.working:", duohui.working());
</script>
运行结果及其说明:
3.借用构造函数原型继承
-- 基本思路: 使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.
问题一:组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数: 一次是在创建子类型原型的时候(Son.prototype = new Super()),
另一次是在子类型构造函数内部(借用构造函数call). 寄生组合式继承就是为了降低调用父类构造函数的开销而出现的
<script>
/**
* 借用构造函数+原型继承
* 在子构造函数加上:父构造函数名.call(this,传参)
* 再加:Person.call(this, this.name, this.inherit); // 实现父类属性的继承
Coder.prototype = new Person(); // 实现父类方法的继承
*
*问题一:组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数:
一次是在创建子类型原型的时候(Coder.prototype = new Person()), 另一次是在子类型构造函数内部(借用构造函数call)
**/
//父构造函数
function Person(name, inherit) {
(this.name = name), (this.inherit = inherit);
this.pinkColor = ["yellow", "white"];
this.sayName = function () {
console.log("my name is", this.name);
};
this.working = function () {
return "父类的woking";
};
}
//子构造函数
function Coder(name, age, inherit) {
this.name = name;
this.inherit = inherit;
Person.call(this, this.name, this.inherit); // 实现父类属性的继承
this.age = this.age;
this.sayName = function () {
console.log("my age is ", this.age);
};
}
Coder.prototype = new Person(); // 实现父类方法的继承
let dalin = new Coder("dalin", 1, "dalin继承");
console.log(dalin);
console.log(dalin instanceof Coder);
dalin.pinkColor.push("black");
console.log("dalin.pinkColor:", dalin.pinkColor);
console.log("dalin.inherit:", dalin.inherit);
console.log("dalin.working:", dalin.working());
let duohui = new Coder("duohui", 2, "duohui继承");
console.log("duohui.pinkColor:", duohui.pinkColor);
console.log("duohui.inherit:", duohui.inherit);
console.log("duohui.working:", duohui.working());
</script>
运行结果及其说明:
4.原型继承
为什么要原型继承--- 对已存在的对象不必重新创建属性
-- 基本思想:在object()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例.
问题一: 当对象中包含引用类型值的原型时,该引用类型值会被所有子类实例共享;
问题二: 在创建子类型(例如创建Coder的实例)时,不能向超类型(例如Person)的构造函数中传递参数.
<script>
/** 4.原型继承
* 对已存在的对象不必重新创建属性
*
* 缺点:
* 1.无法传参,
* 2.如果修改子类继承来的引用类型属性,会影响其他子类继承的引用类型属性
* **/
const person = {
name: "personName",
age: "personAge",
pinkColor: ["white", "yellow"],
sayHi: () => {
return "person say:hello";
},
};
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
console.log("继承的pinkColor:", person.pinkColor);
const shaohui = object(person);
console.log(shaohui.sayHi());
shaohui.pinkColor.push("black");
console.log("shaohui.pinkColor:", shaohui.pinkColor);
const duohui = object(person);
console.log(duohui.sayHi());
console.log("duohui.pinkColor:", duohui.pinkColor);
</script>
运行结果及其说明:
5.寄生式继承
为什么要寄生式继承 --- 对原型式的增强版,子类可以自定义更多的属性和方法
-- 基本思想:在object()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例. 再来个包装壳函数增强object函数里面的对象,最后返回增强版对象。
问题一: 当对象中包含引用类型值的原型时,该引用类型值会被所有子类实例共享;
问题二: 函数无法复用
<script>
/**
* 5.寄生式继承是对原型式继承的增强版本
*
* 缺点:
* 1.如果修改子类继承来的引用类型属性,会影响其他子类继承的引用类型属性
* 2.无法复用函数
*
* **/
const person = {
name: "personName",
age: "personAge",
pinkColor: ["white", "yellow"],
sayHi: () => {
return "person say:hello";
},
};
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
function enhanceObject(obj, otherName) {
let clone = object(obj);
clone.otherName = otherName;
clone.sayOther = () => {
return "我还会说其他的";
};
return clone;
}
let shaohui = enhanceObject(person, "shaohui");
shaohui.pinkColor.push("black");
console.log("shaohui.pinkColor:", shaohui.pinkColor);
console.log("shaohui.sayOther:", shaohui.sayOther());
console.log("shaohui.otherName:", shaohui.otherName);
let duohui = enhanceObject(person, "duohui");
console.log("duohui.pinkColor:", duohui.pinkColor);
console.log("duohui.sayOther:", duohui.sayOther());
console.log("duohui.otherName:", duohui.otherName);
</script>
运行结果及其说明:
6.寄生组合继承
--基本思路:不必要指定子构造原型对象调用构造函数
<script>
/***
* 6.寄生组合继承
* 解决:借用构造函数组合继承调用两次构造函数的问题
* 思路:不必为了指定子类型的原型而调用超类型的构造函数
* function extend(son, parent) {
*let parentPrototype = object(parent.prototype);
*parentPrototype.constructor = son;
*son.prototype = parentPrototype;
*}
* **/
//父构造函数
function Person(name, inherit) {
this.name = name;
this.inherit = inherit;
this.pinkColor = ["yellow", "white"];
this.sayName = function () {
console.log("my name is", this.name);
};
}
Person.prototype.working = function () {
return "父类的woking";
};
//子构造函数
function Coder(name) {
this.name = name;
Person.call(this, this.name);
this.sayName = function () {
console.log("my age is ", this.age);
};
}
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
function extend(son, parent) {
let parentPrototype = object(parent.prototype);
parentPrototype.constructor = son;
son.prototype = parentPrototype;
}
extend(Coder, Person);
const shaohui = new Coder("shaohui");
console.log("shaohui.working:", shaohui.working());
shaohui.pinkColor.push("black");
console.log("shaohui.pinkColor:", shaohui.pinkColor);
const duohui = new Coder("duohui");
console.log("duohui.working:", duohui.working());
console.log("duohui.pinkColor:", duohui.pinkColor);
</script>
运行结果及其说明: