37 JS继承
原型对象
new一个对象的过程
理解补充链接
https://juejin.cn/post/7075354546096046087#heading-1
原型链继承
这种继承是有问题的:
-
创建child实例时不能传参,也就是Child构造函数本身不接受参数。
-
当原型上的属性是引用数据类型时,所有实例都会共享这个属性,即某个实例对这个属性重写会影响其他实例
原因:子类实例的原型对象都指向new Parent()这个父类实例(的原型对象上),因此我们找不到子类实例自身没有name这个属性时,会自动去自己的原型对象(Child.prototype)上找,然后大家指向的都是父类的name这种引用类型,找的都是同一份数据
//原型链继承
function Parent() {
this.name = ['sikara']
this.s = 1
this.getS = function () {
console.log(this.s)
}
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child() {
}
//子类的原型链指向父类实例
// 等同于Child.prototype.__proto__ = Parent.prototype
Child.prototype = new Parent()
let child = new Child()
console.log(child.name) //sikara
console.log(child.s) //1
child.getName()
//弊端 子类某一实例对父类引用类型变量的修改会影响所有子类实例
let child1 = new Child()
child1.name[0] = 'asfa'
child1.s = 444
console.log(child1.name) //asfa
console.log(child1.s) //444
console.log(child.name) //asfa
console.log(child.s) //1
构造函数继承
//解决原型链继承弊端------使用构造函数继承
//定义:在子类的构造函数中执行父类的构造函数并且为其绑定子类的this
function Animal(name) {
this.name = [name]
}
Animal.prototype.getName = function () {
console.log(this.name)
}
function Dog() {
//创建一个对象,将父类构造函数内的属性赋值后转到该对象实例自身
Animal.call(this,'sikara')
//对于引用类型,对象是复制一份属性再添加到自身(深拷贝)
}
let dog = new Dog()
let dog2 = new Dog()
dog2.name[0] = 'aaa'
console.log(dog.name) //sikara
console.log(dog2.name) //aaa
//弊端 继承不到父类原型上的方法和属性
dog2.getName()
组合式继承
//解决原型链继承弊端------使用构造函数继承
//定义:在子类的构造函数中执行父类的构造函数并且为其绑定子类的this
function Animal(name) {
this.name = [name]
}
Animal.prototype.getName = function () {
console.log(this.name)
}
function Dog() {
Animal.call(this,'sikara')
}
// 等同于Dog.prototype.__proto__ = Animal.prototype
//这样Dog就能通过__proto__访问到父类原型对象上的方法了
Dog.prototype = new Animal()
//使用原型链继承解决构造函数继承弊端:两者一同使用就是组合式继承 弊端就是每次实例化对象都需要new Animal()即多执行一次父类构造方法
//原型链的一个规则
Dog.prototype.constructor = Dog
let dog = new Dog()
let dog2 = new Dog()
dog2.name[0] = 'aaa'
console.log(dog.name) //sikara
console.log(dog2.name) //aaa
dog2.getName()
console.log(Animal.prototype.constructor)
console.log(Dog.prototype.constructor)
寄生式继承
const object = function (o) {
function F() { this.s = 3}
F.prototype = o
return new F()
}
function FF() {
this.n = 2
}
FF.prototype.add=9999 //无效
FF.prototype= object(Dog.prototype)
// FF.prototype= object(dog)//会多加一层dog的实例
FF.prototype= Object.create(Dog.prototype)//弊端就是一旦对FF原型对象做出修改,自身原型对象属性将丢失(构造函数内的不受影响),建议是先继承,再为子类添加方法
let f1 = new FF()
console.log(f1)
寄生组合式继承------避免了组合式继承两次调用父类构造方法
function Animal(name) {
this.name = [name]
}
Animal.prototype.getName = function () {
console.log(this.name)
}
function Dog() {
Animal.call(this,'sikara')
}
// 简化了原型链继承调用构造函数写法(F是临时构造函数)
//等同于Dog.prototype .__proto__ = F.prototype = Animal.prototype
Dog.prototype = Object.create(Animal.prototype)//{...Animal.prototype}不行 create不是简单的浅拷贝
//原型链的一个规则 原型对象.constructor = 原型对象对应的对象的构造方法
Dog.prototype.constructor = Dog//原型链连接父类后(也即是对Dog.prototype做出了修改),子类自身的原型对象会缺少constructor属性,但不影响继承
let dog = new Dog()
let dog2 = new Dog()
dog2.name[0] = 'aaa'
console.log(dog.name) //sikara
console.log(dog2.name) //aaa
dog2.getName()
//测试
console.dir(dog2)
console.dir(dog2.__proto__)
console.dir(dog2.__proto__.__proto__)
console.dir(dog2.__proto__.__proto__.__proto__)
console.dir(dog2.__proto__.__proto__.__proto__.__proto__)