js实现继承的几种方式?extends用的是哪种继承
1. 原型链式继承
缺点:由于俩个实例使用的是同一个原型对象,他们的内存空间是共享的,当其中一个变化时,另一个也跟着变化
function Parent() { this.name = parent this.number = [1,2,3] } Parent.prototype.getName = function() { return this.name // parent } function Child() { this.newName = child } Child.prototype = new Parent() console.log(new Child())
2.构造函数继承(Parent.call(this))
缺点:父类原型对象中存在父类之前自己定义的方法,子类将无法继承这些方法
function Parent() { this.name = parent } Parent.prototype.getName = function() { return this.name } let parent = new Parent() console.log(getName-Parent:,parent.getName()) function Child() { Parent.call(this) // 改变this指向,this是 Child函数 this.newName = child } let child = new Child() console.log(child:,child) console.log(getName:,child.getName)
3.组合式继承
缺点: Parent执行了俩次, 第一次是改变Child的prototype的时候, 第二次是通过call方法调用Parent,Parent多构造一次就多进行了一次性能开销
function Parent() { this.name = parent this.number = [1,2,3] } Parent.prototype.getName = function() { return this.name } function Child() { // 第二次调用Parent() Parent.call(this) // 改变this指向 this.newName = child } // 第一次调用 Parent() Child.prototype = new Parent() // 手动挂上构造器,指向自己的构造函数 Child.prototype.constructor = Child var child1 = new Child() var child2 = new Child() child1.number.push(4) console.log(child1.number, child2.number) // 不互相影响 [1,2,3,4] [1,2,3] console.log(child1.getName()) // parent console.log(child1.getName()) // parent
4.原型继承:拷贝继承(Object.create()),这个方法接收俩个参数:一个是用作新对象原型的对象,而是新对象定义额外的对象,实现的是浅拷贝
缺点:多个实例的引用类型属性指向相同的内存地址,存在篡改的可能
let parent = { name: parent, number: [1,2,3], getName: function() { return this.name } } let child1 = Object.create(parent) child1.name = child1 child1.number.push(4) let child2 = Object.create(parent) child2.number.push(5) console.log(child1.name) // child1 console.log(child1.name === child1.getName()) // true console.log(child2.name) // parent console.log(child1.number) // [1,2,3,4,5] console.log(child2.number) // [1,2,3,4,5]
5.寄生式继承
在原型继承的基础上,再添加一些方法,进行返回
缺点:同原型继承
let parent = { name: parent, number: [1,2,3], getName: function() { return this.name } } function clone(original) { let clone = Object.create(original) clone.getNumber = function() { return this.number } return clone } let child = clone(Parent) console.log(child.getName()) // parent console.log(child.getNumber()) // [1,2,3]
6.寄生组合式继承
extends基本和寄生组合式继承类似
function Parent() { this.name: parent this.number: [1,2,3] } Parent.prototype.getName = function() { return this.name } function Child() { Parent.call(this) this.newName = child } function clone(parent, child) { // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程 child.prototype = Object.create(parent.prototype) child.prototype.constructor = child } clone(Parent, Child) Child.prototype.getNewName = function() { return this.newName } let child = new Child() console.log(child) console.log(child.getName()) // parent console.log(child.getNewName()) // child
示例
const foo = function(a) { console.log(this:,this) this.name = foo this.a = aa this.fun = function() { console.log(baz:,this.a) // aa console.log(baz:,a) // 3 } function b() { console.log(b:, a) } } foo.prototype = { c: function() { console.log(this.name,this.name) // foo console.log(c:, a) // a is not defined } } const f = new foo(3) console.log(f:,f) f.fun() // 3 f.c() // this.name: foo a is not defined f.b() // f.b is not a function