1、概念
让某一个不具备方法和属性的对象,具有另一个对象的方法和属性,这个过程叫做继承
tips: 下面每种继承方式的总结很重要,注意阅读
2、继承方式
2.1、改变this
指向
2.1.1 利用对象改变this
指向的继承
var obj = {
name: "我是obj",
show: function() {
console.log(this.name);
}
}
obj.show(); // 我是obj
var obj2 = {
name: "我是obj2"
}
// 想要利用show打印obj2的name
// obj2.show(); // 直接执行报错,show不属于obj2
obj.show.call(obj2); // 我是obj2,执行时让show方法的this指向强行改成obj2
2.1.2 构造函数改变this
的指向
function Parent() {
this.name = "假设我是父级";
}
Parent.prototype.init = function() {
console.log("hello");
}
function Child() {
// 执行Parent()的this指向window
// 需要将parent的this指向改成指向child将来的实例
// 而这里chilid的this的指向就是将来的实例
// 将parent的this改成child的this
Parent.call(this);
}
// 想让child获得parent的属性
var c = new Child();
console.log(c); // Child {name: "假设我是父级"}
console.log(c.name); // 假设我是父级
// c.init(); 报错,不能继承原型上的内容
- 如果想继承多个父级
function P1() {
// 每个父级的属性名不能重复,否则会覆盖
this.cont = "属性1"
}
function P2() {
// 每个父级的属性名不能重复,否则会覆盖
this.cont1 = "属性2"
}
function P3() {
this.cont2 = "属性3"
}
function C() {
// 在子级内部将所有父级的this指向都改成指向自己的实例
P1.call(this);
P2.call(this);
P3.call(this);
}
var cc = new C();
console.log(cc); // C {cont: "属性1", cont1: "属性2", cont2: "属性3"} 继承了多个父级的属性
- 改变this指向继承的特点
1.简单方便易操作
2.可以实现多继承(子继承多个父)
3.只能继承构造函数中的属性,不能继承原型上的方法
2.2、原型继承
2.2.1 原型继承
function Parent() {
this.name = "构造函数里的内容";
}
Parent.prototype.init = function() {
console.log("hello");
}
function Child() {}
// 原型继承的方式一:直接复制版原型继承
// 直接将父级的原型复制给子级的原型
// 不推荐,相当于浅拷贝,改变子级会改变父级
Child.prototype = Parent.prototype;
// 浅拷贝之后,再给子级更改方法,会同时改变父级的方法
Child.prototype.init = function() {
console.log("world");
}
// 原型继承的方式二:利用是深拷贝复制父级,改变子级不会影响父级
for (var i in Parent.prototype) {
Child.prototype[i] = Parent.prototype[i]
}
// 深拷贝之后,再给子级更改方法,不会改变父级的方法
Child.prototype.init = function() {
console.log("world"); // world
}
var p = new Parent();
p.init();
console.log(p); // Parent {name: "构造函数里的内容"}
var c = new Child();
c.init();
console.log(c); // Child {},没有继承父级构造函数内部的内容
- 原型继承–原型继承的特点
可以继承原型上的方法,但是继承不了构造函数内的属性
2.2.2 原型链继承
// 构造一个父级函数
function Parent() {
this.name = "父级里的内容";
}
// 给父级的prototype添加方法
Parent.prototype.init = function() {
console.log("hello");
}
// 构造一个子级函数
function Child() {}
// 原型链继承方式:把父级的实例赋值给子级的prototype
// 也就是说:子级的原型就是父级的实例,父级实例有的东西就能被子级的原型继承到
Child.prototype = new Parent();
// 给子级的prototype添加方法相当于给父级的实例添加方法
// 最终执行c.init()打印子级的内容,因为父级的实例在子级实例的上一层,读取,使用最近原则
// 不会影响到父级的方法,父级的方法在父级实例的原型里即子级实例的上上一层
// 如果想要改掉父级的方法:Child.prototype.__proto__.init = function(){...}
Child.prototype.init = function() {
console.log("子级的内容");
}
var p = new Parent();
p.init(); // hello
console.log(p); // Parent {name: "父级里的内容"}
var c = new Child();
c.init(); // hello
console.log(c); // Child {} 在浏览器的结构如下👇👇👇
Child {} // 子级的实例没有方法,也没有属性
__proto__: Parent // 上一层:子级的原型是,父级的实例
name: "父级里的内容" // 父级的实例里有属性
__proto__: // 上上一层:父级的原型有,方法
init: ƒ () // 方法在父级的原型里
constructor: ƒ Parent()
__proto__: Object
- 原型继承–原型链继承特点
1、可以继承原型上的所有内容(继承方法),也能继承构造函数内的内容(继承属性)
2、不利于传参
2.3、混合继承
// 混合继承是将'改变this指向继承'和'原型继承'相结合
function Parent(parent) {
// 父级接收参
this.name = parent;
}
Parent.prototype.init = function() {
console.log(this.name);
}
// 子级接收参数
function Child(son) {
// 改变this指向实现继承属性
// 接收参数后再把参数给this的第二个参数,覆盖原函数的参数
Parent.call(this, son);
}
// 原型继承:只能拿到方法,不能拿到属性
for (var i in Parent.prototype) {
Child.prototype[i] = Parent.prototype[i];
}
// 父级传参在new执行函数时传
var p = new Parent("父级的内容");
p.init(); // 父级的内容
console.log(p); // Parent {name: "父级的内容"}
// 子级传参在new执行函数时传
var c = new Child("子级的内容");
c.init(); // 子级的内容
console.log(c); // Child {name: "子级的内容"}
- 混合继承也能实现继承多个父级
function Another() {
// 注意属性名不能重名,否则会覆盖
this.xixi = "又一个父级";
}
// 注意方法名也不能重名,否则会覆盖
Another.prototype.show = function() {
console.log(this.xixi);
}
// 再执行深拷贝
for (var i in Another.prototype) {
Child.prototype[i] = Another.prototype[i];
}
// 注意其他父级都要在子级内执行并改变this指向
function Child() {
// 改变this指向实现继承属性
Another.call(this);
}
- 混合继承的特点
1、可以继承构造函数,可以继承原型
2、可以实现多继承
2.4、class
继承
- 其实就是面对对象编程的
class
语法 - 语法层面上的继承
ES6
对混合继承的封装
class Parent {
constructor(p) {
this.str = p;
}
init() {
console.log(this.str); // 父级的内容
}
}
// 创建子级构造函数时,使用extends,表示要继承父级了
class Child extends Parent {
// 子级的参数必须要在constructor接收,且
constructor(c) {
// super不写会报错
// super也要传参
super(c);
}
// 如果给子级添加方法,不会影响父级的方法
// 具体参考原型链继承
init() {
console.log("子级的方法"); // 子级的方法
}
}
var p = new Parent("父级的内容");
p.init();
console.log(p); // Parent {str: "父级的内容"}
var c = new Child("子级的内容");
c.init();
console.log(c); // Child {str: "子级的内容"}
class继承的特点:也就是混合继承的特点
2.5、对象的继承:Object.create()
- 根据指定对象创建新对象:
Object.create(指定对象)
var obj = {
name: "admin"
}
var obj2 = Object.create(obj);
console.log(obj == obj2); // false
console.log(obj); // {name: "admin"}
console.log(obj2); // {}
console.log(obj.name); // admin
console.log(obj2.name); // admin,继承到的属性在obj2的原型上
2.6、数组的继承:Array.form()
- 根据指定数组创建新数组:
Object.create(指定数组)
var arr = [1, 2, 3];
var arr2 = Array.from(arr);
console.log(arr == arr2); // false
console.log(arr2); // [1, 2, 3]