JavaScript--浅谈继承

ECMAScript实现继承的方式:程序员

1.原型链ide

利用原型让一个引用类型继承另外一个引用类型的属性和方法。函数

function Father(){
   this.name = 'father'  
}

function Son(){
    
}

//继承Father,将Father的实例赋给Son的原型
Son.prototype = new Father()

var son1 = new Son()
son1.name  // father     继承了Father的name属性
View Code

以前在《浅谈建立对象》中,咱们提出 原型模式建立对象 是能够修改原型的,以下this

 1 function Father(){
 2   this.name = 'father'  
 3 }
 4 
 5 function Son(){
 6 }
 7 
 8 //给Son添加原型属性
 9 Son.prototype.age = '1'
10 
11 //重写Son原型对象,继承Father
12 Son.prototype = new Father()
13 
14 //建立Son实例,能够放在修改原型以前,修改原型可以从实例上当即反映出来
15 var son1 = new Son()
16 
17 Son.prototype.job = '程序员'
18 
19 son1.age // error  
20 //在重写Son原型对象以前 给Son原型添加属性是没用的,重写会切断与以前原型对象的联系
21 
22 son1.job //  程序员
23 
24 Son.prototype = {
25      height: 1.70  
26 }
27 
28 son1.height  // error  上述方法也是属于重写原型对象
View Code

相似于原型模式,单独使用原型链实现继承的问题:spa

a.原型链中的引用类型的值会被全部实例共享,并能够修改,而修改后会反映到全部的实例上,这样实例就不能拥有私有的引用类型的属性。prototype

b.在建立子类型实例时,咱们没法在不影响其余实例的同时向父类型的构造函数中传递参数。3d

 

2.借用构造函数code

function Father(name){
  this.name = name; 
  this.colors = ['red','green','blue']  
}

function Son(name){
  //在子类型构造函数中调用父类型的构造函数,经过call函数等强制绑定做用域
  Father.call(this,name)  
}

var son1 = new Son('张三')
son1.name  //张三
son1.colors.push('yellow')

var son2 = new Son('李四')
son2.name  //李四

son1.colors  // ['red','green','blue','yellow']
son2.colors // ['red','green','blue']
View Code

相对原型链而言,借用构造函数能够向父类型的构造函数传参对象

相似于构造函数模式,单独使用借用构造函数模式实现继承的问题:blog

a.方法都在构造函数中定义,每次建立对象就会从新实例化一次方法属性

b.父类型的原型属性,子类型是访问不到的。

 

3.组合继承

使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承。

 1 function Father(name){
 2  this.name = name;
 3  this.colors = ['red','green','blue']
 4 }
 5 Father.prototype.sayName = function (){
 6    alert(this.name)
 7 }
 8 
 9 function Son(name,age){
10  Father.call(this,name);
11  this.age = age;
12 }
13 
14 //重写Son的原型实现继承,这种重写会致使Son的实例的constructor属性都变成父类型的构造函数Father,Son.constructor  //Father 而不是Son
15 Son.prototype = new Father()
16 //对于某些对constructor属性比较在乎的场景,能够手动的将Son的constructor属性设置成Son,Son.constructor  //Son
17 Son.prototype.constructor = Son;
18 Son.prototype.sayAge = function(){
19    alert(this.age)
20 }
21 
22 var son1 = new Son('张三',20)
23 son1.colors.push('yellow')
24 son1.sayName(); //张三
25 son1.sayAge(); //20
26 
27 var son2 = new Son('李四',21)
28 son2.sayName(); //李四
29 son2.sayAge(); //21
30 
31 son1.colors  //  ['red','green','blue','yellow']
32 son2.colors //  ['red','green','blue']
View Code

不管什么状况下,都会调用2次父类型的构造函数,

第一次在建立子类型原型的时候,Son.prototype = new Father(),

第二次在子类型构造函数内部,Fahter.call(this,name)

4.原型式继承

 1 var Father = {
 2     name:'father',
 3     friends:['张三','李四']  
 4 }
 5 //只传入一个参数的状况下,这2种写法的行为同样的
 6 var son1 = Object(Father)
 7 son1.name = '张三'
 8 var son2 = Object.create(Father)
 9 son2.friends.push('王五')
10 
11 son1.name // 张三
12 son2.name // 张三  ,使用object.create 相似于原型链的继承方式,超类原型的全部属性都是共享的
13 son1.friends // ['张三','李四']  
14 son2.friends // ['张三','李四','王五']
15 
16 var son3 = Object.create(Father,{
17    name:{
18         value:'王五'
19    }    
20 })  
21 son3.name // 王五
22 son3.friends // ['张三','李四','王五']
View Code

 

在不想建立构造函数,只是想一个对象与另外一个对象保持相似的状况下,原型式继承是能够胜任的,不过他们的引用类型的属性仍是全部实例共享的。

5.寄生式继承

function createSon(Father){
 var Son = Object(Father);
  //在这里能够添加属于子类型的属性  
 Son.sayHi = function(){
    alert('hi')
 }
 return Son
}
View Code

和原型式继承同样,在不考虑自定义类型和构造函数的状况下,只想返回一个相似的新对象时,寄生式继承能够作到,不过和构造函数模式相似,方法都在函数中定义,每次建立对象就会从新实例化一次方法属性,全部的实例的sayHi方法都是一个新的实例

6.寄生组合式继承

为了解决组合继承调用2次父类型构造函数的问题

首先咱们要搞清楚为何组合继承会调用2次父类型的构造函数,组合继承的2次调用分别是 子构造函数中的 借用构造函数 和 原型链继承的实现。

其实多余的一次调用就是 原型链继承的实现中咱们必需要拿到一个父类型的实例用来做为子类型的原型,这样修改子类型的原型只会修改父类型的实例原型,而不会影响父类型的其余实例

这就是为何 在原型链继承中,咱们用 Son.prototype = new Father() //获取父类型的实例重写子类型的原型  而不是 Son.prototype = Father.prototype // 这样写的话,咱们在Son.prototype上新添加一个属性,会影响到Father的其余实例

怎么才能只得到一个与父类型prototype类似的对象而不调用父类型的构造函数呢?

在不考虑自定义类型和构造函数的状况下,只想返回一个相似的新对象时,寄生式继承能够作到

fucntion expendPrototype(Son,Father){
  //建立父类型的原型副本
  var prototype = Obejct(Father.prototype);
  //弥补重写原型而失去的默认的constructor属性
  prototype.constructor = Son;
  //重写子类型的原型
  Son.prototype = prototype;
}
View Code

这样 咱们用这个方法去代替以前的 Son.prototype = new Father(),从而解决了多一次无用的构造函数调用.

相关文章
相关标签/搜索