看完這篇文章,你應該懂什麼叫繼承了吧

說到繼承呢?確定有不少作java的朋友都以爲是一個比較簡單的東西了。畢竟面向對象的三大特徵就是:封裝、繼承和多態嘛。可是真正對於一個javascript開發人員來講,不少時候其實你使用了繼承,但其實你不知道這叫繼承。今天我就借這篇文章來談一談繼承在前端的幾種實現方式。javascript

1、 原型繼承

function Animal(name = 'animal'){
	this.name = name
}
Animal.prototype.eat = function(food){
	console.log('dasdsa')
	return `${this.name} eat ${food}`;
}

function Dog(){
}
Dog.prototype = new Animal();

var instance = new Dog();
instance.name = 'dog';
console.log(instance.eat('bone'));
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
複製代碼

可是原型繼承有有有些缺點,來看下面一段代碼:前端

function Animal(name = 'animal'){
	this.name = name
	this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
	return `${this.name} eat ${food}`;
}

function Dog(){
}
Dog.prototype = new Animal();

var instance = new Dog();
instance.name = 'keji';
instance.skinColors.push('red');
console.log(instance.eat('bone'));
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true

var instance1 = new Dog()
console.log(instance1.skinColors) // [ 'black', 'white', 'red' ]
複製代碼

從上面的代碼,咱們能夠清楚的發現:全部的實例都會公用一個原型鏈,若是一個實例中修改原型 那麼全部實例的值都會被修改。java

2、 構造函數繼承

針對前面原型鏈繼承可能會存在公用一個原型鏈的問題,那麼咱們能夠給你們介紹一種方式:構造函數的繼承。構造函數的繼承至關於將父類複製給子類。es6

function Animal(name = 'animal'){
	this.name = name
	this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
	return `${this.name} eat ${food}`;
}

function Dog(){
	Animal.call(this);
}

var instance = new Dog();
instance.name = 'keji';
instance.skinColors.push('red');
console.log(instance.eat('bone')); // TypeError: instance.eat is not a function
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true

var instance1 = new Dog();
console.log(instance1.skinColors); // [ 'black', 'white' ]
複製代碼

可是這種方法也有本身缺點:bash

  • 不能繼承原型上面的屬性和方法
  • 複製的處理,至關於在子類中實現了全部父類的方法,影響子類的性能。

3、 組合繼承

原型鏈繼承能繼承父類原型鏈上的屬性,可是可能會存在篡改的問題;而構造函數繼承不會存在篡改的問題,可是不能繼承原型上面的屬性。那麼咱們是否是能夠將二者進行結合呢?函數

function Animal(name = 'animal'){
	this.name = name
	this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
	return `${this.name} eat ${food}`;
}

function Dog(){
	Animal.call(this);
}
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;


var instance = new Dog();
instance.name = 'keji';
instance.skinColors.push('red');
console.log(instance.eat('bone'));
console.log(instance.skinColors) // [ 'black', 'white', 'red' ]
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true

var instance1 = new Dog()
console.log(instance1.skinColors) // [ 'black', 'white' ]
複製代碼

這種方法呢?調用了兩次父類的構造函數,有些許損耗性能,而且子類的構造函數的屬性會和原型上面的屬性相重合。(優先原用構造函數的屬性)性能

4、 原型式繼承

function object(obj){
  function F(){}
  F.prototype = obj;
  return new F();
}


let Programmer = {
	features:["tutou","jiaban","single"]
}

// 方式一:最原始的作法
var programmer1 = object(Programmer);
programmer1.features.push('meiqian');
console.log(programmer1.features); // [ 'tutou', 'jiaban', 'single', 'meiqian' ]
var programmer2 = object(Programmer);
console.log(programmer2.features); // [ 'tutou', 'jiaban', 'single', 'meiqian' ]

// 方式二 es中的Object.create
var programmer3 = Object.create(Programmer);
console.log(programmer3.features); // [ 'tutou', 'jiaban', 'single', 'meiqian' ]
複製代碼

從上面的代碼很明顯的能夠發現:和構造函數繼承同樣也存在被篡改的可能,而且也不能傳遞參數。ui

5、 寄生式繼承

在原型式繼承的基礎上面加強了對象,並返回構造函數。this

function pFactory(obj){
	let clone = Object.create(obj);
	clone.motto = function(){
		console.log('hardworking and not lazy!!')
	}
	return clone;
}

var programmer1 = new pFactory(Programmer);
console.log(programmer1.motto()); // hardworking and not lazy!!
console.log(programmer1.features); // [ 'tutou', 'jiaban', 'single' ]
複製代碼

這種繼承的方法一樣和原型繼承同樣,存在被篡改的可能。es5

6、 寄生組合式繼承

前面說了這麼多,每種繼承方式都有本身的優勢和缺點,那麼是否是能夠將這些繼承的方式作一個合併:以他之長補己之短呢?來看下面一段代碼:

function Animal(name = 'animal'){
	this.name = name
	this.skinColors = ['black','white']
}
Animal.prototype.eat = function(food){
	return `${this.name} eat ${food}`;
}

function inheritPrototype(subType, superType){
  var prototype = Object.create(superType.prototype);
  prototype.constructor = subType; 
  subType.prototype = prototype;
}


function Dog(name,sound){
	Animal.call(this,name);
	this.sound = sound;
}

inheritPrototype(Dog,Animal);

Dog.prototype.getSound = function(){
	console.log(`${this.name} ${this.sound}`);
}

var instance = new Dog('keji','wangwangwang!!!');
instance.skinColors.push('red');
console.log(instance.eat('bone'));
console.log(instance.skinColors) // [ 'black', 'white', 'red' ]
console.log(instance instanceof Dog); // true
console.log(instance instanceof Animal); // true
console.log(instance.getSound()) // keji wangwangwang!!!

var instance1 = new Dog('haha','wangwang!!!')
console.log(instance1.skinColors) // [ 'black', 'white' ]
console.log(instance1.getSound()) // haha wangwang!!!
複製代碼

這個例子的效率的體如今它只調用了一次父類的構造函數,這很大程度上面減小建立了沒必要要多餘的屬性。而且還能繼承原型鏈上面的方法。這個方法是如今庫的實現方法。

7、 es6的繼承方法

class Animal {
	constructor(name){
		this.name = name;
	}

	get getName(){
		return this.animalName()
	}

	animalName(){
		return this.name;
	}

}

class Dog extends Animal{
	constructor(name,sound){
		super(name);
		this.sound = sound;
	}
	get animalFeature(){
		return `${this.getName} ${this.sound}`
	}
}

let dog = new Dog('keji','wangwangwang!');
console.log(dog.animalFeature); // keji wangwangwang!
複製代碼

其實咱們曉得,class語法也是由es5語法來寫的,其繼承的方法和寄生組合式繼承的方法同樣。關於es6的類,我在代碼自檢的時候遇到的兩個重點,值得注意下的是:

  • 函數聲明會提高,類聲明不會。
  • ES5的繼承實質上是先建立子類的實例對象,而後再將父類的方法添加到this上。可是es6是先建立父類的實例對象this,而後再用子類的構造函數修改this。

說在最後

好像什麼都沒寫就差很少快12點了,最近在瘋狂的複習。可是卻發現越學東西越多,感受有點學不完的意味在裏面 外加上最近好像有點高考考砸以後的失眠綜合症,搞的我整我的都不怎麼舒服。明明高考都過去差很少6年了,還一直困擾着我,賊恐怖,算了 算了 先不寫了 睡覺去了。

相關文章
相關標籤/搜索