借用构造函数继承
function Person (name) {
this.love = ['apple','orange']
this.name = name
this.age = 18
}
function People (name) {
Person.call(this, name) // 改变this指向,这样people的实例就可以继承person上的属性
}
let p = new People('xl')
p.love.push('banana')
p.age = 19
let p1 = new Person('xw')
console.log(p.love, p1.love) // ['apple','orange','banana], ['apple','orange']
console.log(p.age, p1.age) // 19, 18
console.log(p.name, p1.name) // 'xl', 'xw'
优点:
- 引用类型的属性不会全局污染,每份实例独享
- 可以向父类中传参
缺点:
- 方法在构造函数中定义,每次新建实例都会调用一次改变this指向的方法
原型链继承
function Parent () {
this.names = ['xw','xl','xs']
}
function Child () {}
Child.prototype = new Parent() // 上一篇new操作符了解到它的内部操作实际是把构造函数的prototype赋值给了一个新对象的隐式原型__proto__,所以相当于把Parent的原型对象赋值给了Child
Parent.prototype.sayName = function () {console.log(this.names)}
let c = new Child()
c.names.push('xz')
let c1 = new Child()
console.log(c.sayName(), c1.sayName()) // ["xw", "xl", "xs", "xz"], ["xw", "xl", "xs", "xz"]
问题:
- 新建child实例时不能传参给父类
- 引用类型的属性会让所有实例共享
组合继承
function Parent (name) {
this.name = name
this.car = ['奔驰', '宝马']
}
function Child (name) {
Parent.call(this, name)
this.age = 18
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
let c = new Child('xl')
c.car.push('奥迪')
let c1 = new Child('xw')
c.age = 19
console.log(c.name, c.age, c.car) // 'xl', 19, ['奔驰', '宝马', '奥迪']
console.log(c1.name, c1.age, c1.car) // 'xw', 18, ['奔驰', '宝马']
这种继承方法结合了原型继承和借用构造函数继承的优点,会经常使用到
原型式继承
实际上是模拟了Object.create()方法
function createObj (o) {
function F () {}
F.prototype = o
return new F()
}
var person = {
name: 'kevin',
friends: ['daisy', 'kelly']
}
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'person1';
console.log(person2.name); // kevin
person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]
缺点:
- 和原型链继承一样,引用类型的属性会让所有实例共享
注意:修改
person1.name的值,person2.name的值并未发生改变,并不是因为person1和person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log('hi');
}
return clone;
}
缺点:
- 和构造函数继承一样,每次创建实例都会调用一次方法
寄生组合式继承
组合继承的缺点在于调用了两次构造函数,第一次在 Child.prototype = new Parent()。第二次在const c = new Child(),构造函数中Person.call(this)处调用。如何避免重复调用,可以不执行Child.prototype = new Parent(),而是间接让Child.prototype指向Parent.prototype
function Parent (name) {
this.name = name
this.car = ['奔驰', '宝马']
}
function Child (name) {
Parent.call(this, name)
this.age = 18
}
let F = function () {}
F.prototype = Parent.prototype
Child.prototype = new F()
let c = new Child('xl')
c.car.push('奥迪')
let c1 = new Child('xw')
console.log(c.car, c1.car) // ["奔驰", "宝马", "奥迪"] (2) ["奔驰", "宝马"]
封装一下
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
// 当我们使用的时候:
prototype(Child, Parent);
引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是: 这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
es6类继承
extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中constructor表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError错误,如果没有显式指定构造方法,则会添加默认的 constructor方法。在子类的constructor中必须声明super用于继承父类this,因为子类没有自己的this
class Rectangle {
constructor (height,width) {
this.width = width
this.height = height
}
get area () {
return this.calcArea()
}
calcArea () {
return this.height * this.width
}
}
class Squar extends Rectangle {
constructor (length) {
super(length,length)
this.name = 'Squar'
}
}
let s = new Squar(10)
s.area // 100
console.log(s.width, s.height) // 10, 10
结语
写的不好,有错误的地方还请各位大佬指正,感恩家人感恩🙏