面向對象、類與繼承

一、前言

二、類與實例

  • 類的申明
// 一、構造函數
function Person(name) {
  this.name = name;
};
// 二、Es6 class
class Person2 {
  constructor(name) {
    this.name = name;
  };
};
複製代碼
  • 實例
// 實例化
console.log(new Person('張三'), new Person2('李四'));
複製代碼

三、繼承

  • 一、藉助構造函數實現繼承
function Parent1() {
  this.name = 'parent1';
};
Parent1.prototype.say = function() { // 沒法被Child1的實例對象繼承
  console.log('say hello!');
};
function Child1() {
  Parent1.call(this); // 改變this指向
  this.type = 'child1';
};
console.log(new Child1());
複製代碼

PS:缺點是隻能繼承構造函數中的屬性和方法,沒法繼續原型對象上的屬性和方法,如圖: bash

  • 二、藉助原型鏈實現繼承
function Parent2() {
  this.name = 'parent2';
};
Parent2.prototype.say = function() {
  console.log('say hello parent2!');
};
function Child2() {
  this.type = 'child2';
};
Child2.prototype = new Parent2();
console.log(new Child2())  // new Child2()_proto_===Child2.prototype
複製代碼

繼承的原理:new Child2()實例對象的_proto屬性指向的是構造函數Child2的prototype原型對象,
            即 new Child2()_proto_===Child2.prototype;
            因爲Child2.prototype = new Parent2(),
            則可經過 new Parent2()的_proto_屬性找到Parent2的prototype原型對象。
複製代碼

PS:缺點是經過一個構造函數實例多個對象時候,修改構造函數的屬性,全部的繼承自構造函數的實例對象的改屬性都將改變。以下所示:函數

function Parent2() {
  this.name = 'parent2';
  this.arr1 = [1,2,3,4];
  this.arr2 = [1,2,3,4];
};
function Child2() {
  this.type = 'child2';
};
Child2.prototype = new Parent2();
var obj1 = new Child2();
var obj2 = new Child2();
obj1.arr1.push(5)  // 因爲obj自己並無arr1屬性,則經過_proto_原型鏈找到了Parent2的arr2屬性
obj1.arr2 = [1,2,3,4,5] // 這種方式並不會修改obj2.arr2屬性,至關於給obj1新增長了arr2屬性。
console.log(obj1, obj2)
複製代碼

PS:從圖中能夠看到 obj1.arr1.push(5)修改使得 obj2.arr1的值也被修改。這顯然不是咱們想要的,咱們但願各個實例之間是相互獨立的。

  • 三、構造函數和原型鏈相組合方式
function Parent3() {
  this.name = 'parent3';
  this.arr1 = [1,2,3,4];
};
Parent3.prototype.say = function() {
  console.log('say hello!')
}
function Child3() {
  Parent3.call(this); // 改變了this指向,使得Parent3中的this指向的是Child3的實例對象。
  this.type = 'child3';
};
Child3.prototype = new Parent3()
var o3 = new Child3();
var o4 = new Child3();
o3.arr1.push(5)
console.log(o3, o4)
複製代碼

PS: 能夠看到這裏已經解決了方法一、2中的問題。可是這裏有個問題就是 Parent3構造函數被執行了兩次。

Parent3.call(this); // 改變了this指向,使得Parent3中的this指向的是Child3的實例對象。
Child3.prototype = new Parent3() // 既然上面已經繼承了Parent3構造函數中的屬性,這裏只是爲了繼承Parent3原型屬性
思考:根據以前原型鏈的相關知識,有如下關係
new Parent3().__proto__ === Parent3.prototype
故這裏能夠改爲:
Child3.prototype = Parent3.prototype  // 缺點Child3和Parent3的constructor是同一個
複製代碼
  • 優化
  1. 到這裏已經實現了繼承,可是這裏還有個問題。
var o3 = new Child3(); // 這裏o3實例應該是由`Child3`直接生成的實例。
更具根據以前原型鏈的相關知識
o3 instanceof Child3 // true
o3 instanceof Parent3 // true
instanceof判斷o3是Child3原型鏈上的一個實例若是想要直接判斷o3是Child3直接生成的實例,能夠經過constructor:
咱們指望
o3.constructor === Child3
三實際上的結果:
o3.constructor === Parent3
複製代碼

// 代碼優化
function Parent4() {
  this.name = 'parent4';
  this.arr1 = [1,2,3,4];
};
function Child4() {
  Parent3.call(this);
  this.type = 'child4';
};
Child4.prototype = Object.create(Parent4.prototype); // Object.create建立的對象只是_proto_的引用
Child4.prototype.constructor = Child4  // 給Child4原型對象添加constructor,覆蓋_proto_的constructor
var o5 = new Child4();
var o6 = new Child4();
o5.arr1.push(5)
console.log(o5, o6)
複製代碼

四、總結

  • 本文主要講述瞭如何申明一個類。
  • 如何實現繼承和繼承的實現原理。
  • instanceoconstructor的使用。
相關文章
相關標籤/搜索