一、什么是继承?为什么要实现继承?
子类啃老,自己想从父类获取属性和方法
二、了解一下构造函数、实例、原型对象的区别
- 构造函数就是这样的function Fun() {} 大写的函数名,普通函数是小写
- 实例对象就是new Fun
- 每一个构造函数都有原型对象
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
三、实现继承的六种方式的优缺点
方式 | 实现原理 | 优点 | 缺点 |
---|---|---|---|
原型链继承 | 子类原型=父类实例 | 简单 | 子类实例没办法给父类传值,子类共享父类实例,一改都改 |
构造继承 | 父类的构造函数放到子类里 | 子类实例可以给父类传值,子类共享父类实例,一改不全改 | 执行两次父类函数,子类的实例无法访问父类原型上的方法 |
原型式继承 | 借用原型基于已有的对象创建新对象 | 必须有一个对象来作为另一个对象的基础,数据共享 | |
寄生式继承 | Object.create() | 和原型式继承一样,只是封装了一下而已 | |
寄生组合式继承 | 解决数据共享的问题 | 最接近ES6 | 代码冗杂 |
ES6中Class类 |
最好的方式就是寄生组合式和ES6中的class extend
四、实现继承的六种方式
1.原型链继承(将父类的实例赋值给子类的原型)
//定义父类
function Parent() {
this.name = '甜仔';
}
// 父类的原型添加方法
Parent.prototype.getName = function() {
console.log(this.name)
}
// 定义子类
function Child() {
}
Child.prototype = new Parent(); //父类的实例等于子类的原型
let child1 = new Child();
console.log(child1.__proto__.name) //甜仔
console.log(child1.name) //甜仔
child1.getName() //甜仔
优点
1.简单易于实现,父类的实例赋值给子类的原型
缺点
- 子类的实例没办法给父类传值
//
// 定义父类
function Parent(name) {
//属性
this.name = name;
// 实例方法
this.sleep = function() {
console.log(this.name + '正在睡美容觉')
}
}
//原型方法
Parent.prototype.eat = function(food) {
console.log(this.name +'正在吃'+ food)
}
//定义子类
function Children(name) {
}
Children.prototype = new Parent
var wen = new Children()
console.log(wen.sleep()) //un正在睡美容觉
console.log(wen.eat("火锅")) //un正在吃火锅
2.多个子类实例共享父类的属性,其中的子类实例改了属性,影响其他的子类
//
//定义父类
function Parent() {
this.name = ['甜崽', '甜仔'];
}
// 父类的原型添加方法
Parent.prototype.getName = function() {
console.log(this.name)
}
// 定义子类
function Child() {
}
Child.prototype = new Parent();
let child1 = new Child();
let child2 = new Child();
child1.name.push('温小鹿');
console.log(child1.name) //['甜崽', '甜仔', '温小鹿']
console.log(child2.name) //['甜崽', '甜仔', '温小鹿']
//其中一个子类的实例修改父类构造函数中的属性,其他的子类实例共享属性,也会访问改后的属性
2.借用构造继承(父类构造函数放到子类的构造函数中,call,apply改变指向)
//定义父类
function Parent() {
this.name = ['甜崽', '甜仔'];
}
// 父类的原型添加方法
Parent.prototype.getName = function() {
console.log(this.name)
}
// 定义子类
function Child(ChildName) {
Parent.apply(this)
this.name = ChildName
}
let child1 = new Child('温小鹿是他的饲养员aaa');
console.log(child1.name) // 温小鹿是他的饲养员aaa
3.原型式继承(借用原型基于已有的对象创建新对象)
借用原型基于已有的对象创建新对象
//定义父类
function object(o) {
function F() {};
F.prototype = o;
return new F()
}
let test = {
name: ['甜仔', '甜崽'],
age: '三个月',
getName : function() {
return this.name + '已经' + age;
}
}
let test1 = object((test)
let test2 = object((test)
test1.name.push('温小鹿')
console.log(test1.name) // ['甜仔', '甜崽', '温小鹿']
console.log(test2.name) // ['甜仔', '甜崽', '温小鹿']
// 父类的函数内部,创建一个临时的构造函数,传入的对象作为临时构造函数的原型,返回临时的实例
// 该继承需要有一个对象可以作为另一个对象的基础
// 缺点,数据共享
ES5中的Object.create(新创建对象的原型对象,为新创建的对象添加指定的属性值和对应的属性描述符。)规范了原型式继承,第二个参数如果相同会覆盖
let test = {
name: ["甜仔", "甜崽"],
age: "三个月",
getName: function () {
return this.name + "已经" + age;
},
};
let test1 = Object.create(test, {
name: {
value: "温小鹿",
},
age: {
value: 20,
},
});
console.log(test1);
4.寄生式继承(封装原型式继承)
function Parent(o) {
var clone = Object.create(o);
clone.getName = function () {
console.log(this.name, "1111");
};
return clone;
}
let test = {
name: ["甜仔", "甜崽"],
age: "三个月",
};
let a = Parent(test);
a.getName(); //['甜仔', '甜崽'] '1111'
5.寄生组合式继承
function inheritPrototype(parent, child) {
var clone = Object.create(parent.prototype); //创建对象
clone.constructor = child; // 增强对象
child.prototype = clone; // 指定对象
}
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function () {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Parent,Child);
var a = new Child("甜仔", 3);
a.getName();
6. ES6中的继承
class Parent {
constructor(name) {
this.name = name;
}
getName() {
console.log(`我的猫叫${this.name}`)
}
};
class Child extends Parent {
constructor(name, age) {
super(name, age);
this.name = name;
}
}
let a = new Child('甜仔', 3)
a.getName() //我的猫叫甜仔
console.log(a.name) //甜仔