- 继承方式
- 原型链继承
利用cat.prototype = new Animal(), 连通了子类(cat)-子类原型(cat.prototype)-父类(animal)// 创建父类 function Animal(){ this.name = 'Animal' } // 添加共享方法eat Animal.prototype.eat = function(){ console.log(`${this.name} eat food!`) } // 创建子实例 function Cat(){ this.size = 'big' } // 实现继承 Cat.prototype = new Animal() // 添加子类的方法 Cat.prototype.sleep = function(){ console.log(`${this.size} ${this.name} sleep 3 hours!`) } // 创建实例 let myCat = new Cat() // 调用构造函数的方法 myCat.eat() // 调用父类方法 myCat.sleep()
缺点: 引用类型的属性被所有实例共享, 修改堆内存数据时候会将所有实例的数据都修改,但是直接创建新的存储对象就不会产生此错误// 创建父类 function Animal(){ this.name = ['Animal'] } // 创建子实例 function Cat(){ this.size = 'big' } // 实现继承 Cat.prototype = new Animal() // 创建实例 let myCat = new Cat() let youCat = new Cat() // 修改对象存储类型的对内存数值 youCat.name.push('cat') console.log(youCat.name ) // ["Animal", "cat"] console.log(myCat.name ) // ["Animal", "cat"] // 修改对象存储类型的引用地址 youCat.name = ['cat'] console.log(youCat.name ) // ["cat"] console.log(myCat.name ) // ["Animal", "cat"]
function Animal(){ this.sleep = [1,2,3,4] } function cat (){ } cat.prototype.run = '20km' // 原型链继承 cat.prototype = new Animal() let mao1 = new cat() let mao2 = new cat() console.log(mao1.sleep) // [1,2,3,4] mao1.sleep.push(...[7,8,9]) // 构造函数的引用类型被修改后其余对象实例都受到影响 console.log(mao1.sleep) // [1,2,3,4,7,8,9] console.log(mao2.sleep) // [1,2,3,4,7,8,9] // 子类的prototype的constructor指向了构造函数,所以子类的构造方法不会被子类实例对象继承 console.log(mao1.run) //undefined // 解决办法,将子类的prototype的constructor指向子类 cat.prototype.constructor = cat cat.prototype.jump = '10km' // 子类的原型上绑定的方法必须要在修改原型的constructor之后 console.log(mao1.run) // unfined console.log(mao1.jump) // 10km
如果子对象的方法和构造函数的方法相同,子对象的对象实例,会调用子对象的方法,构造函数中的属性将被隐蔽,可以使用__prorto__调用function Animal(){ this.sleep = [1,2,3,4] this.eat = function(){ console.log('Animal eat food') } } function cat (){ this.eat = function(){ console.log('cat eat finsh') } } // 原型链继承 cat.prototype = new Animal() let mao1 = new cat() console.log(mao1.eat()) // cat eat finsh
- 盗用构造函数继承
function Animal(){ this.sleep = [1,2,3,4] this.eat = function(){ console.log('Animal eat food') } } function cat (){ // 盗用构造函数 Animal.call(this) } let mao = new cat() console.log(mao)
1.创建子类实例时候,都会调用构造函数,复制一份构造函数.影响性能
2.只能继承构造函数的属性和方法。不能继承构造函数的原型function Animal(){ this.sleep = [1,2,3,4] this.eat = function(){ console.log('Animal eat food') } } Animal.prototype.jump = function (){ console.log('Animal jump 20km')} function cat (){ // 盗用构造函数 Animal.call(this) } cat.prototype.run = function (){ console.log('cat run 10km')} let mao = new cat() console.dir(mao.run()) console.dir(mao.jump())
- 组合继承
将原型链继承和借用构造函数继承组合在一起的继承function Animal(){ this.coler = 'red' this.age = 2 this.sleep = [1,2,3,4] } Animal.prototype.run = function (){ console.log('Animal run 10km')} function Cat(){ // 盗用构造函数 Animal.call(this) this.name = {first:'big',last:'mao'} } // 原型链继承 Cat.prototype = new Animal() Cat.prototype.constructor = cat Cat.prototype.eat = function (){console.log('cat eat fish')} // 创建实例 let mao = new Cat() let mao1 = new Cat() mao.run() // 构造函数的原型 ,被继承 mao.sleep.push(...[8,9])// 修改构造函数的对象类型属性,没有相互影响 console.log(mao.sleep) console.log(mao1.sleep) mao.eat() // 调用对象的原型方法,也被继承了
缺点继承了双份一样的构造函数的属性和方法 - 原型式继承
利用一个空对象作为中介,将空对象的原型指向传入的对象,返回空对象的对象实例
缺点是多个实例对象之间会将传入的对象的引用属性篡改,并且无法传参function getObj(obj){ function F(){} F.prototype = obj return new F() } let obj = {name:'zhangsan', age:['20',5],game:function(){}} let f1 = getObj(obj) let f2 = getObj(obj) f1.age.push(78) console.log(f1.age) //["20", 5, 78] console.log(f1.age) //["20", 5, 78]
- 寄生式继承
就是创建一个方法,调用原型式继承赋值给一个对象,然后给对象添加一些方法,再返回对象,目的是给构造函数新增属性和方法已增强函数
原型式继承有的缺点寄生式继承也存在function getObj(obj){ function F(){} F.prototype = obj return new F() } function getOther(obj){ let p = getObj(obj) p.say= function(){console.log('say hello ')} return p } let obj = {name:'zhangsan', age:['20',5],game:function(){}} let other = getOther(obj) let other1 = getOther(obj) other.age.push(92) console.log(other.age) console.log(other1.age) other1.say()
- 寄生组合式继承
使用盗用构造函数方式,将父类的属性方法放入子类
通过一个函数将父类的原型复制对象指向子类,将子类的原型指向复制的这个对象
最后可以获取父类的属性和方法的复制对象(不会相互影响),也不会存放两次父类的属性和方法
这是现在库实现的方法,也是目前最成熟的方法// 构造函数的构成 function inheritPrototype(subType, superType){ // 复制一个父类原型 copySuperPrototype = Object.create(superType.prototype) // 将父类原型的构造函数执行子类,弥补因重写原型而失去的默认的constructor 属性 copySuperPrototype.constructor = subType // 将子类的原型指向父类复制出来的原型上 subType.prototype = copySuperPrototype } // 父类 function superType(name){ this.name = name this.color = ['red'] } // 子类 function subType(name, age){ superType.call(this,name) this.age = age } inheritPrototype(subType, superType) let o1 = new subType('zhangsan',12) let o2 = new subType('lisi',78) o1.color.push(1) console.dir(o1) console.dir(o2)
- 混入方式继承多个对象
Object.assign
会把OtherSuperClass
原型上的函数拷贝到MyClass
原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法function MyClass() { SuperClass.call(this); OtherSuperClass.call(this); } // 继承一个类 MyClass.prototype = Object.create(SuperClass.prototype); // 混合其它 Object.assign(MyClass.prototype, OtherSuperClass.prototype); // 重新指定constructor MyClass.prototype.constructor = MyClass; MyClass.prototype.myMethod = function() { // do something };
- ES6类继承extends
extends
关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中constructor
表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError
错误,如果没有显式指定构造方法,则会添加默认的constructor
方法,使用例子如下。class Rectangle { // constructor constructor(height, width) { this.height = height; this.width = width; } // Getter get area() { return this.calcArea() } // Method calcArea() { return this.height * this.width; } } const rectangle = new Rectangle(10, 20); console.log(rectangle.area); // 输出 200 ----------------------------------------------------------------- // 继承 class Square extends Rectangle { constructor(length) { super(length, length); // 如果子类中存在构造函数,则需要在使用“this”之前首先调用 super()。 this.name = 'Square'; } get area() { return this.height * this.width; } } const square = new Square(10); console.log(square.area); // 输出 100 extends继承的核心代码如下,其实现和上述的寄生组合式继承方式一样 function _inherits(subType, superType) { // 创建对象,创建父类原型的一个副本 // 增强对象,弥补因重写原型而失去的默认的constructor 属性 // 指定对象,将新创建的对象赋值给子类的原型 subType.prototype = Object.create(superType && superType.prototype, { constructor: { value: subType, enumerable: false, writable: true, configurable: true } }); if (superType) { Object.setPrototypeOf ? Object.setPrototypeOf(subType, superType) : subType.__proto__ = superType; } }
- class继承(es6中的class继承,本质是原型链)
- 原型链继承
js继承的几种方式及优缺点
最新推荐文章于 2023-01-30 10:21:17 发布