javaScript的面向對象與繼承

js面向對象概述

"javaScript 沒有提供傳統面嚮對象語言中的類式繼承,而是經過原型委託的方式來實現對象與對象之間的繼承",這句話是摘自《javaScript設計模式與開發實踐》書中第一章的一句話,這句話開門見山的說明了js的編程模式「原型」,而這樣也直接說明了javaScript的面向對象是基於原型克隆的方式來建立對象,並以原型鏈的方式實現對象與對象之間的關係。java

那咱們先來了解一下這js這種基於原型編程語言的基本規則:es6

  • 一. 全部的數據都是對象,
  • 二. 要獲得一個對象,不是經過實例化類,而是找到一個對象做爲原型並克隆它,
  • 三. 對象會記住它的原型,
  • 四. 若是對象沒法響應某個請求,它會把這個請求委託給它本身的原型。

上面這幾句話,我一樣是摘自《javaScript設計模式與開發實踐》裏面的,原模原樣抄錄下來分享給你們的。編程

js繼承

1. 原型鏈繼承設計模式

實現方式:新對象的實例的原型等於父對象類的實例。 `閉包

let Animals = function (type, name) {   // 定義父類
	this.type = type;
	this.name = name;
    }
    Animals.prototype.animal = '動物';
    Animals.prototype.eat = function () {
		console.log('這是一隻小'+ this.type + ': 它會吃東西')
    }			   
		   
let Cat = function (sex) {  // 定義子類
	this.sex = sex
    }
		   
Cat.prototype = new Animals('貓', '小花');  // 原型鏈繼承
	let cat = new Cat('公') // 生成子類實例
	cat.eat()   // 執行繼承來的方法
複製代碼

` 原型鏈繼承的特色是:可繼承父類的私有屬性和原型上的屬性和方法,可是,父類私有上的屬性方法它們之間的繼承是屬於引用同一個內存地址,所以修改其中的一個,也會影響到另外一個對象app

2. 構造函數繼承編程語言

實現方式:在子類的函數體裏執行父類,經過call和apply來改變this的指向。函數

`性能

let Dog = function (sex) {
	Animals.call(this, '狗', '旺財');   // 構造繼承:執行父類,經過call改變this指向
	this.sex = sex;
	}
let dog = new Dog('母');
dog.eat();  // 報錯:Uncaught TypeError: dog.eat is not a function;
複製代碼

`this

構造函數繼承: 可繼承父類裏的私有屬性和方法,可是不能繼承父類原型上的方法和屬性,此類繼承修改一個對象的屬性不會影響到另外一個對象

3. 組合式繼承

實現方式:該方式的繼承其實就是上面的原型繼承和構造函數繼承的混合方式。

`

let Dog = function (sex) {
	Animals.call(this, '狗', '旺財');   // 構造繼承: 執行父類,經過call改變this指向
	this.sex = sex;
	}
Dog.prototype = new Animals('貓', '小花');  // 原型鏈繼承
let dog = new Dog('母');
dog.eat();  // 執行父類原型上的方法
複製代碼

` 組合式繼承:可繼承父類私有的屬性和方法,繼承的私有屬性和方法都是子類私有的,能夠繼承父類原型上的屬性,能夠傳參,可複用。可是組合式繼承調用了兩次父類方法,所以在性能上有必定的損耗

4. 包裝式繼承

實現方式: 經過一個包裝函數,把父類實例做爲參數傳遞進去,子類實例在包裝函數體裏生成。

`

function context (obj) {    // 定義包裝函數,並傳遞父類實例
		function Pig () {
		   	this.sex = '公'		
		}
		Pig.prototype = obj;    // 繼承父類實例
		return new Pig()    // 返回子類實例
	}
		   
    let animals = new Animals('豬', '小胖')
	let pig = context(animals);
    pig.type = '羊';  //這樣寫等於給子類添加一個新的type屬性
	pig.eat()    // 輸出:這是一隻小羊: 它會吃東西
	animals.eat()    // 輸出: 這是一隻小豬: 它會吃東西
複製代碼

` 包裝式繼承:相似於函數閉包的用法,語義上不夠明顯。

5. 組合寄生式繼承

實現方式: 經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點。

`

function context (obj) {
		function Fn () {
		   this.sex = '公'
		}
		Fn.prototype = obj; // 函數的原型等於另外一個實例
		   return new Fn()
	}
		   
    let objA = context(Animals.prototype);
		   
    function Cattle () {
		Animals.call(this, '牛', '小蠻'); //在函數中用apply或者call引入另外一個構造函數
	}
		   
    Cattle.prototype = objA;
		   
	objA.constructor = Cattle;   // 修復實例
		   
    let cattle = new Cattle();
		   
	console.log(cattle)
複製代碼

`

組合寄生式繼承:一、函數的原型等於另外一個實例。二、在函數中用apply或者call引入另外一個構造函數。繼承方式太過複雜。

6. es6繼承 (使用最多)

實現方式:使用class關鍵字聲明類,經過extends關鍵字實現繼承。

`

class Animals {
	constructor(type, name) {
		this.type = type;
		this.name = name;
	}			
	eat () {
		console.log('這是一隻小'+ this.type + ': 它會吃東西')
			}
		}
		
    let animal = new Animals('狗', '旺財');
		
	class Car extends Animals {
		constructor(sex) {
		super('貓', '小花');   
		// 此處是重點在子類的構造函數中,只有調用super以後,才能夠使用this關鍵字,不然
		// 會報錯。這是由於子類實例的構建,基於父類實例,只有super方法才能調用父類實例
				this.sex = sex;
			}
		}			
	let car = new Car('公')
	car.eat()
複製代碼

` es6繼承:使用extends關鍵字實現繼承,語法上更加清晰明瞭,子類繼承父類後,子類的構造函數,必須執行super方法

new關鍵字

在上面的繼承演示中,無論是es5仍是es6,js建立一個類都必須使用new關鍵字去執行。 `

let Animals = function (type, name) {   // 定義父類
	this.type = type;
	this.name = name;
    }
    Animals.prototype.animal = '動物';
    Animals.prototype.eat = function () {
		console.log('這是一隻小'+ this.type + ': 它會吃東西')
    }
複製代碼

`

在這裏面,new關鍵字主要作了以下工做:

  • 一:建立了一個對象obj;
  • 二:將這個obj對象的__proto__成員指向了Animals函數的prototype;
  • 三:將Animals函數對象的this指針替換成obj;
  • 四:將obj對象返回出來,也就是咱們的實例;

總結

javaScript的面向對象與繼承,繼承的核心思想是複用,不須要在去編寫多餘代碼,還有就是代碼的管理。喜歡的朋友給個贊吧,謝謝。

相關文章
相關標籤/搜索