JS中的继承
继承:类型和类型之间的关系
继承的目的,把子类型里面的共同成员提取到父类型中,代码重用
原型链继承
原型链继承:
- 无法设置构造函数的参数,(没有办法做到给每个子类的实例单独设置各自的属性。)
- 子类改变父类的引用类型属性会导致其他子类跟着改变
- 如下面的
s1.name.push('科比')这样设置后导致子类s2中的name同样有了科比
- 如下面的
function Person(name, age) {
this.name = [name]
this.age = age
}
Person.prototype.fun = function () {
console.log(this.name);
}
function Son(sex) {
this.sex = sex
}
Son.prototype = new Person('zs', 14);//这样设置导致s1和s2都是一样的name和age,肯定不合理
//不写也不影响,但是原型链有个规则,构造函数的原型.constructor指向构造函数,如果不写会发现Son.prototype.constructor;//Person
Son.prototype.constructor = Son;
let s1 = new Son('男')
let s2 = new Son('女')
s1.name.push('科比')//子类改变父类的引用类型
//导致了s2一起改变
console.log(s1.name);// ['zs', '科比']
console.log(s2.name);// ['zs', '科比']
s1.fun()// ['zs', '科比']
s2.fun()// ['zs', '科比']
// 所以这样的继承,有缺点,不实用
构造函数继承
构造函数继承:
-
通过在子类Son中调用父类构造函数来实现
-
利用call函数改变this执行
还是使用上面的例子:
function Person(name, age) {
this.name = [name]
this.age = age
//可以解决钩子函数继承导致子类继承不到原型链上方法的缺陷
this.bar = function () {
console.log('子类可以继承,但是不推荐这么写');
}
}
Person.prototype.fun = function () {
console.log(this.name);
console.log('子类继承不到');
}
function Son(name, age, sex) {
// 通过在子类Son中调用父类构造函数,实现给每个子类单独设置属性
Person.call(this, name, age);//改变this执行
this.sex = sex
}
let s1 = new Son('哈登', 30, '男')
let s2 = new Son('小哈', 21, '男')
let p = new Person('乔丹',40)
console.log(s1);//name:哈登
console.log(s2);//name:小哈
console.log(p);//name:小哈
s1.name.push('科比')//子类改变父类的引用类型
console.log(s1.name);//['哈登', '科比']
console.log(s2.name);//['小哈']
console.log(p.name);//['乔丹']
通过上面的打印可以发现
构造函数继承:
- 解决了原型链继承导致的不能给每个子类实例单独设置属性的缺陷。
- 解决了子类改变父类引用属性造成父类和其他子类跟着变化的缺陷。
构造函数继承的缺点:
- 那就是无法继承父类原型链上的方法(fun)和属性
- 解决方法:
- 在父类的构造函数中写方法,但是不推荐
- 通过组合继承解决
组合继承
组合继承 = 原型链继承 + 构造函数继承
完美解决上面的缺点
function Person(name, age) {
this.name = [name]
this.age = age
}
Person.prototype.fun = function () {
console.log(this.name);
}
function Son(name, age, sex) {
// 通过在子类Son中调用父类构造函数,实现给每个子类单独设置属性
Person.call(this, name, age)
this.sex = sex
}
//通过原型让子类型继承父类型中的方法
Son.prototype = new Person();
Son.prototype.constructor = Son
let s1 = new Son('哈登', 30, '男')
let s2 = new Son('小哈', 21, '男')
s1.fun()//['哈登']
s2.fun()//['小哈']
通过上面的打印,可以发现已经完美的解决了原型链继承和构造函数继承所导致的缺陷。
但是到这里,可以发现,其实组合继承并非是最好的,他还有可以优化的地方。
不好的地方:
- 在
new Son的时候 会执行 构造函数Son那么就会执行Person.call和Son.prototype = new Person();这样会在原型中存在两份相同的属性和方法。 - 可用通过
寄生组合继承来解决
寄生组合继承
-
就是把组合继承中的
Son.prototype = new Person();改为Student.prototype = Object.create(Person.prototype)//浅拷贝 -
这样就不会存在两份相同的属性和方法了。
-
为什么不直接
Student.prototype = Person.prototype这里需要了解JavaScript的引用类型赋值,如果真像前面那样写,会导致子类在 **原型 **上添加方法或属性,其他子类和父类也同样会有该子类添加的方法和属性,这是不合理的,所以需要使用Object.create创建一个对象。 -
这里的浅拷贝不需要担心子类改变父类的引用属性导致父类和其他子类引用属性发生变化,这里在构造函数继承中已经解决了。
// 组合继承 = 原型链继承 + 构造函数继承
// 完美解决上面的缺点
function Person(name, age) {
this.name = [name]
this.age = age
}
Person.prototype.fun = function () {
console.log(this.name);
}
function Son(name, age, sex) {
// 通过在子类Son中调用父类构造函数,实现给每个子类单独设置属性
Person.call(this, name, age)
this.sex = sex
}
//Son.prototype = Person.prototype;//不能这样,要使用下面一条
Son.prototype = Object.create(Person.prototype)//浅拷贝
Son.prototype.constructor = Son
let s1 = new Son('哈登', 30, '男')
let s2 = new Son('小哈', 21, '男')
let p = new Person()
Son.prototype.fff = function(){}
console.log(p);
console.log(s1);
console.log(s2);
如果是直接将原型链赋值:
-
Son.prototype = Person.prototype -
查看图片可以发现p和s1,s1上都出现了fff方法

ES6继承
class Person {
constructor(name, age) {
this.name = [name]
this.age = age
}
// 自动在原型上
fun() {
console.log('父类的方法', this.name);
}
}
class Son extends Person {
constructor(name, age, sex) {
super(name, age)
this.sex = sex
}
static arr = [1, 2, 3]
static fun() {
console.log('静态方法');
}
fun() {
console.log('子类重写的方法', this.name);
}
}
let p = new Person('乔丹', 40)
let s1 = new Son('哈登', 30, '男')
let s2 = new Son('小哈', 21, '男')
p.name.push('篮球')
s1.name.push('碰瓷')
console.log(p);
console.log(s1);
console.log(s2);
p.fun()// 父类的方法 , ['乔丹', '篮球']
s1.fun()//子类重写的方法 ['哈登', '碰瓷']
s2.fun()//子类重写的方法 ['小哈']
Son.fun()//静态方法
console.log(Son.arr);//[1, 2, 3]
1280

被折叠的 条评论
为什么被折叠?



