1. 程式人生 > >js面向物件 繼承

js面向物件 繼承

1、類的宣告 2、生成例項 3、如何實現繼承 4、繼承的幾種方式



1、類的宣告有哪些方式
<script type="text/javascript">
  //類的宣告
  function Animal() {
    this.name = 'name'
  }
  // es6中的class的宣告
  class Animal2 {
    constructor() {
      this.name = name
    }
  }
</script>

 

 

2、怎麼例項化這個物件
console.log(new Animal(), new
Animal2());

 

 

3、js有幾種繼承方式   1)
/**
* 藉助建構函式實現繼承
*/
function Parent1() {
  this.name = 'parent1'
}
function Child1() {
  Parent1.call(this);
  this.type = 'child1';
}
console.log(new Child1());
這樣就實現了一個繼承了。重點看這個Parent1.call,他改變了js執行的上下文,通過這個呼叫,改變了Parent1的this指向。也就是父類的所有屬性都指向了子類的這個例項

 

缺點:這個建構函式是有自己的原型鏈的,也就是有自己的prototype屬性,雖然說Parent1的屬性指向了Child1這個例項,但是他的prototype並沒有被Child1所繼承。比如說
function Parent1() {
  this.name = 'parent1'
}
Parent1.prototype.say = function(){};


function Child1() {   Parent1.call(this);   this.type = 'child1'; } console.log(new Child1());
看下,是沒有say方法的,說明不會繼承父類的原型鏈,所以說,這種繼承只是部分繼承,如果父類還有一些方法,就拿不到



2)
/**
* 藉助原型鏈實現即成
*/
function Parent2() {
  this.name = "parent2";
}
function Child2() {
  this.type = "child2"
}
Child2.prototype = new Parent2();
console.log(new Child2());
這個方法彌補第一種方式的不足,我們說任何一個建構函式都有一個prototype這個屬性的,這個屬性的作用是訪問他的例項能訪問到原型物件上。 如圖,我們看到Child2的__proto__指向的是Parent2的原型物件,prototype是Child2的一個屬性,這個屬性是個物件,將new Parent2()這個物件賦值給了Child2,那麼new一個Child2後,訪問Child2.name,在Child2裡面沒有找到,就會去__proto__找,也就是prototype找,也就是Parent2這個例項找,這樣就實現了繼承

 

這個繼承方式的缺點:在Parent2裡面新增,this.play = [1,2,3];,然後new多個Child2的例項,發現他們都指向了同一個play。當s1改變了play,第二個物件跟著被改變了 比如
/**
* 藉助原型鏈實現即成
*/
function Parent2() {
    this.name = "parent2";
    this.play = [1,2,3]
}
function Child2() {
    this.type = "child2"
}
Child2.prototype = new Parent2();

var s1 = new Child2();
var s2 = new Child2();
s1.play.push(4);

console.log(s1.play,s2.play);

 

 

3)
/**
* 組合方式(結合建構函式 和 原型鏈的優點)
*/
function Parent3 () {
  this.name = 'parent3';
}
function Child3 () {
  Parent3.call(this);
  this.type = 'child3'
}
Child3.prototype = new Parent3();

我們再加上方法,看看是否能避免上面的問題呢

/**
* 組合方式(結合建構函式 和 原型鏈的優點)
*/
function Parent3 () {
this.name = 'parent3';
  this.play = [1,2,3];
}
function Child3 () {
  Parent3.call(this);
  this.type = 'child3'
}
Child3.prototype
= new Parent3(); var s3 = new Child3(); var s4 = new Child3(); s3.play.push(4); console.log(s3.play,s4.play);

這個時候發現就不一樣了,避免了eg2的缺點,這種組合方式結合了優點,彌補了缺點,這是通常實現繼承的方式

缺點:執行了兩次父類的原型鏈,第一次Parent2.call()。第二次 new Parent3()

 

4)

/**
* 組合繼承的優化
*/
function Parent4 () {
  this.name = 'parent4';
  this.play = [1,2,3];
}
function Child4 () {
  Parent4.call(this);
  this.type = 'child4'
}
Child4.prototype = Parent4.prototype;
var s5 = new Child4();
var s6 = new Child4();

這樣父類的原型鏈只執行了一次,但是還剩下一個問題,s5,s6都是父類的例項,沒有自己的例項,prototype裡面有個contron指明是哪個的例項,而子類的prototype拿的直接是父類的prototype,所以當然拿的是父類的建構函式

 

/**
* 組合繼承的優化2
*/
function Parent5 () {
  this.name = 'parent5';
  this.play = [1,2,3];
}
function Child5 () {
  Parent5.call(this);
  this.type = 'child5'
}
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;

Object.create方法建立原型物件,Parent5.prototype就是create方法的一個引數,一層一層往上找實現了繼承,同時完成了繼承,這個就是實現繼承的完美方式