原型、构造函数、实例对象的关系
构造函数能够生成实例对象,实例对象拥有构造函数里的属性和方法,并继承构造函数原型上的属性及方法。
继承方式
原型链继承
子类原型等于父类构造函数的实例
特点:
子类实例能够继成父类及父类原型中属性和方法
问题:
- 如果父类构造函数中有引用类型的属性,会导致通过子类创建的所有实例都共享该属性,其中一个实例修改了这个引用值,其他实例的这个属性都会同步变化,造成数据污染。
- 子类构造函数无法向父类构造函数传值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function Super(){ this.name = 'super' this.arr = [1,2,3] }
Super.prototype.getName = function(){ console.log(this.name) }
function Sub(){ this.age = 18 }
Sub.prototype = new Super()
const sub1 = new Sub() const sub2 = new Sub()
sub.arr.push(4) console.log(sub1.name, sub1.arr) console.log(sub2.name, sub2.arr)
|
借用构造函数继承
使用call或者apply在子类构造函数中调用父类构造函数
特点:子类构造函数能够向父类构造函数中传值,继承了父类构造函数的属性和方法,但无法继承父类原型上的属性和方法
问题:
- 只能在子类构造函数中定义方法,因此不能重用函数
- 子类不能访问父类原型上定义的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function Super(name){ this.name = name }
Super.prototype.getName = function(){ console.log(this.name) }
function Sub(name){ Super.call(this, name) this.age = 18 }
const sub1 = new Sub('lili') console.log(sub1.name, sub1.age)
|
组合继承(伪经典继承)
特点:解决了上述两种的缺陷;
问题:父类构造函数会执行两次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function Super(name){ this.name = name this.arr = [1,2,3] }
Super.prototype.getName = function(){ console.log(this.name) }
function Sub(name){ Super.call(this, name) this.age = 18 }
Sub.prototype = new Super()
const sub1 = new Sub('sub1') const sub2 = new Sub('sub2')
sub1.arr.push(4)
console.log(sub1.name, sub1.arr) console.log(sub2.name, sub2.arr)
|
原型式继承
本质是进行了浅拷贝,与原型链相似,引用值会被共享
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function objectCreate(fatherPrototype){ function Fun(){} Fun.prototype = prototype return new Fun() }
const person = { name: 'person', arr: [1,2,3] }
const son1 = objectCreate(person) son1.name = 'son1' son1.arr.push(4)
const son2 = objectCreate(person) son2.name = 'son2' son2.arr.push(5)
console.log(son1.name, son1.arr) console.log(son2.name, son2.arr)
|
寄生式继承
背后思路类似于寄生构造函数和工厂模式:先创造一个实现继承的构造函数,然后再增强对象(给继承后的对象添加方法),再将这个对象返回。
特点:类似借用构造函数,函数不能重用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function objectCreate(fatherPrototype){ function Fun(){} Fun.prototype = fatherPrototype return new Fun() }
function createAnother(fatherPrototype){ let cloneObj = objectCreate(fatherPrototype) cloneObj.sayHi = function(){ console.log('hi') } return cloneObj }
const person = { name: 'person', arr: [1,2,3] }
const son1 = createAnother(person) son1.sayHi()
|
寄生式组合继承
取原型的副本(寄生式继承)赋值给子类原型。算引用类型继承的最佳模式,但有点复杂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function objectCreate(fatherPrototype){ function Fun(){} Fun.prototype = fatherPrototype return new Fun() }
function inheritPrototype(subType, superType){ let prototype = objectCreate(superType) prototype.consructor = subType subType.prototype = prototype } function Super(name){ this.name = name this.arr = [1,2,3] }
Super.prototype.getName = function(){ console.log(this.name) }
function Sub(name){ Super.call(this, name) this.age = 18 }
inheritPrototype(Sub, Super)
Sub.prototype.sayHi = function(){ console.log('hi') }
const son1 = new Sub() son1.name = 'son1' son1.arr.push(4)
const son2 = new Sub() son2.name = 'son2' son2.arr.push(5)
console.log(son1.name, son1.arr) console.log(son2.name, son2.arr)
|
得多看几遍,多理解几遍~