js继承
继承就是子类可以使用父类的所有功能,并且对这些功能进行扩展。 有个构造函数A,然后又有个构造函数B,但是B想要使用A里的一些属性和方法就是利用继承 js中的继承相当麻烦和复杂
1. 原型链继承
2. 构造继承
3. 组合继承
4. 寄生组合继承
5. 原型式继承
6. 寄生继承
7. 混入式继承
8. extends继承
1... 原型链继承
先复习原型链的知识
function Parent () {
this.name = 'Parent'
this.sex = 'boy'
}
Parent.prototype.getName = function () {
console.log(this.name)
console.log(this.sex)
}
function Child () {
this.name = 'child'
}
Child.prototype = new Parent()
var child1 = new Child()
child1.getName()
console.log(child1)
console.log(Child.prototype.__proto__ == Parent.prototype)
// 这种方式就叫做原型链继承 将子类的原型对象指向父类的实例
上半部分
下半部分
// 缺陷1:
function A () {}
function B () {
this.name = 'anlan'
}
function C () {}
B.prototype = new C()
A.prototype = new B()
a = new A()
console.log(a.name)
先讲push方法:
深拷贝和浅拷贝
浅拷贝的话,如果要是改变原始的内容,浅拷贝的值也会变,指向了同一个内存地址
深拷贝的话,如果要是改变原始的内容,深拷贝的值不会变,重新复制了一份,并开辟了新的内存地址
function Parent (name) {
this.name = name
this.sex = 'boy'
this.colors = ['white', 'black']
}
function Child (name) {
this.name = name
this.feature = ['cute']
}
var parent = new Parent('parent')
Child.prototype = parent
var child1 = new Child('child1')
child1.sex = 'girl'
child1.colors.push('yellow')
child1.feature.push('sunshine')
var child2 = new Child('child2')
console.log(child1)
console.log(child2)
console.log(child1.name)
console.log(child2.colors)
console.log(parent)
优点 继承了父类的模板,又继承了父类的原型对象
缺点 1. 如果要给子类的原型上新增属性和方法,就必须放在Child.prototype = new Parent()这样的语句后面
2. 无法实现多继承, 多个原型指向同一个实例,当有多个实例化子对象时,修改一个会影响其他对象
3. 来自原型对象的所有属性都被共享了【浅拷贝】
4. 创建子类时,无法向父类构造函数传参数
Object.prototype.isPrototypeOf()方法这是对象才有的方法,因为在构造函数的原型上
也就是原型对象中的方法。
isPrototypeOf()
方法用于检查一个对象是否存在于另一个对象的原型链中。
备注: isPrototypeOf()
与 instanceof 运算符不同。在表达式 object instanceof AFunction
中,会检查 object
的原型链是否与 AFunction.prototype
匹配,而不是与 AFunction
本身匹配。
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
function Parent () {
this.name = 'parent'
}
function Child () {
this.sex = 'boy'
}
Child.prototype = new Parent()
var child1 = new Child()
console.log(Child.prototype.isPrototypeOf(child1))
console.log(Parent.prototype.isPrototypeOf(child1))
console.log(Object.prototype.isPrototypeOf(child1))
2. 构造继承
基本原理
复习.call的用法
function Parent (name) {
this.name = name
}
function Child () {
this.sex = 'boy'
Parent.call(this, 'child')
// this.Parent = Parent('child')
}
Child()
function Parent (name) {
this.name = name
}
function Child () {
this.sex = 'boy'
Parent.call(this, 'child')
// this.Parent = Parent('child')
}
var child1 = new Child()
console.log(child1)
变种
function Parent (name) {
this.name = name
}
function Child () {
this.sex = 'boy'
Parent.call(this, 'good boy')
this.name = 'bad boy'
}
var child1 = new Child()
console.log(child1.name)
涉及到引用类型
function Parent (name, sex) {
this.name = name
this.sex = sex
this.colors = ['white', 'black']
}
function Child (name, sex) {
Parent.call(this, name, sex)
}
var child1 = new Child('child1', 'boy')
child1.colors.push('yellow')
var child2 = new Child('child2', 'girl')
console.log(child1)
console.log(child2)
构造继承的缺点
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child () {
this.sex = 'boy'
Parent.call(this, 'good boy')
}
Child.prototype.getSex = function () {
console.log(this.sex)
}
var child1 = new Child()
console.log(child1)
child1.getSex()
child1.getName()
// 缺点1:构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法
console.log(child1 instanceof Parent)
// 缺点2:实例并不是父类的实例,只是子类的实例
组合继承 【原型链继承 + 构造继承】
// 使用原型链继承来保证子类能继承到父类原型中的属性和方法
// 使用构造继承来保证子类能继承到父类的实例属性和方法
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name) {
this.sex = 'boy'
Parent.call(this, name)
}
Child.prototype = new Parent()
Child.prototype.getSex = function () {
console.log(this.sex)
}
var child1 = new Child('child1')
var parent1 = new Parent('parent1')
console.log(child1)
console.log(parent1)
child1.getName()
child1.getSex()
parent1.getName()
parent1.getSex()
constructor
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name) {
this.sex = 'boy'
Parent.call(this, name)
}
Child.prototype = new Parent()
Child.prototype.getSex = function () {
console.log(this.sex)
}
var child1 = new Child('child1')
var parent1 = new Parent('parent1')
console.log(child1.constructor)
console.log(parent1.constructor)
// constructor 其实只是个标识作用,再实际的代码中并没有实际意义,所以是否在组合继承中修复这个地方取决于自己
组合继承的思维导图
思考题
var a;
(function () {
function A () {
this.a = 1
this.b = 2
}
A.prototype.logA = function () {
console.log(this.a)
}
a = new A()
})()
a.logA()
// 现在我想要在匿名函数外给A这个构造函数的原型对象中添加一个方法logB用以打印出this.b
a.constructor.prototype.logB = function () {
console.log(this.b)
}
a.logB()
// 这样也是可以的,但是在任何时候都不建议去人工修改隐式原型
a.__proto__.logB = function () {
console.log(this.b)
}
a.logB()
组合继承关于引用对象的处理
function Parent (name, colors) {
this.name = name
this.colors = colors
}
Parent.prototype.features = ['cute']
function Child (name, colors) {
this.sex = 'boy'
Parent.apply(this, [name, colors])
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var child1 = new Child('child1', ['white'])
child1.colors.push('yellow')
child1.features.push('sunshine')
var child2 = new Child('child2', ['black'])
console.log(child1)
console.log(child2)
console.log(Child.prototype)
// 组合继承的优点
// 可以继承父类实例属性和方法,也能够继承父类原型属性和方法
// 弥补了原型链继承中引用属性共享的问题
// 可传参,可复用
// 缺点:
function Parent (name) {
console.log(name)
this.name = name
}
function Child (name) {
Parent.call(this, name)
}
Child.prototype = new Parent()
var child1 = new Child('child1')
console.log(child1)
console.log(Child.prototype)
// 原型链继承和构造继承的时候都会调用一次 console.log(name)
// 1. 使用组合继承时,父类构造函数会被调用两次
// 2. 并且生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,所以增加了不必要的内存。
... 注意坑~
function Parent (name, colors) {
this.name = name
this.colors = colors
}
Parent.prototype.features = ['cute']
function Child (name, colors) {
Parent.apply(this, [name, colors])
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
var child1 = new Child('child1', ['white'])
child1.colors.push('yellow')
child1.features.push('sunshine')
var child2 = new Child('child2', ['black'])
console.log(child1.colors)
console.log(child2.colors)
console.log(child1.features)
console.log(child2.features) // 滴滴
// features是定义在父类构造函数原型对象中的,是比new Parent()还要更深一层的对象、
// 它只能解决原型(匿名实例)中引用属性共享的问题。features是Parent.prototype上的属性,相当于是爷爷那一级