原型、原型對象的理解 及其原型鏈繼承

在 ES5 中,有的人可能對原型,原型對象,及其原型鏈不是很清楚,今天我就說說對這些的深刻認識下。(若是有什麼不懂得歡迎留言探討,固然若是有什麼寫的不恰當的也但願你們留言備註。)javascript

 

首先,再說原型與原型對象以前,固然有必要清楚構造函數,實例,原型與原型對象之間的關係。其實他們的關係也很簡單。java

  構造函數,實例,原型與原型對象之間的關係:chrome

  構造函數有它本身的屬性及其方法,其中包括本身定義的屬性和方法外,還有兩個特殊屬性(prototype、constructor);而每一個他的實例都會擁有它的全部屬性和方法(包括prototype、constructor)constructor則是指向每一個實例的構造函數,而prototype 原型 則是一個地址指向原型對象,這個原型對象建立了實例後,只會取得constructor屬性,其餘的都是從Object繼承而來;在Firefox 、 chrome在對象上都支持一個屬性"_proto_";這個原型對象的屬性和方法是全部該類實例共享的任何該類實例夠能夠訪問該原型對象的屬性和方法(後面會介紹訪問原型對象屬性和方法的三個方式)函數

如上圖,p1 ,p2的的實例都有Person的屬性和方法,而且prototype都指向原型對象,p1\p2共享prototype原型對象的屬性和方法,各自的constructor都指向Peson,這即是構造函數、實例、原型(對象)三者的關係。this

  如今我來講一說訪問原型對象屬性和方法的三個方式:spa

  1.經過Person.prototype 屬性prototype

    console.log(Person.prototype.name);//輸出----->person指針

  2.經過 屬性屏蔽 delete (屏蔽構造函數屬性或者方法)code

    p1.sayName();       //輸出----->構造函數對象對象

      delete p1.name;

    console.log(p1.name); //輸出----->原型屬性

    delete p1.sayName;

    p1.sayName(); //輸出 --->原型對象方法

   3.經過Object.getPrototypeOf(p1)

    console.log(Object.getPrototypeOf(p1).name);//輸出----->原型屬性

上面咱們須要注意就是當實例調用屬性或者方法時,有一個」屬性搜索機制「,所謂」屬性搜索機制「就是當實例訪問屬性或者方法時首先會如今自身的實例中搜索,看是否有對應屬性,有,則返回;若是沒有那麼它會經過prototype 到原型對象中尋找對應的屬性和方法

 

 

  原型鏈:

  其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。咱們知道,每一個構造函數都有一個原型對象,每一個原型對象都有一個指向構造函數的指針,而實例又包涵一個指向原型對象的內部指針。

  若是咱們讓原型對象(A.prototype) = 另外一個類型的實例(new B()),那麼,該原型對象(A.prototype)就有一個指向另外一個原型對象(B.prototype)的指針,相應的,另外一個原型對象(B.prototype)也包含指向另外一個構造函數(B)的指針。          ------------->若是另外一個的原型(B.prototype)又是另外一個類型(C)的實例,上訴關係依然成立,就構成了實例與原型的鏈條,這就是原型鏈。

實現原型鏈的基本方法:

  

	function Person () {
		this.name = "person";
	}

	Person.prototype.getPersonName = function () {
		return this.name;
	};

	function Student () {
		this.studentname = "student";
	}

	// 繼承了Person
	Student.prototype = new Person();
	Student.prototype.getStudentName = function () {
		return this.name;
	};

	var stu = new Student();
	console.log(stu.getPersonName());			//person

 

 如上就是經過將 Student()的prototype = new Person() 即子類的原型對象等於父類的實例,從而Student.prototype有了Person的全部屬性和方法,實現了繼承。經過實現原型鏈,再結合 「屬性搜索機制「,則

stu.getPersonName()

會通過三個階段:1.搜索實例 2.搜索Student.prototype 3.搜索Person.prototype;若是再沒就會到Object  對象的prototype對象上尋找。由於全部的function和對象等引用類型都繼承Object;這也就說明了爲何不是經過Object直接實例的對象(自定義類型)會有valueof(),toString()等方法。

 

須要注意的是,子類有時須要重寫父類的方法或者新增心得方法,這些都要 替換了原型以後(也就是實現繼承以後)。-----------stu 指向Student的原型,Student的原型又指向了Person的原型。從而實現了stu 繼承Student Student繼承Person

 

 

 

 

	function Person () {
		this.name = "person";
	}

	Person.prototype.getPersonName = function () {
		return this.name;
	};

	function Student () {
		this.studentname = "student";
	}

	// 繼承了Person
	Student.prototype = new Person();
       // 新增方法
	Student.prototype.getStudentName = function () {
		return this.name;
	};
       // 重寫父類方法
	Student.prototype.getPersonName = function () {
		return false;
	};

	var stu = new Student();
	console.log(stu.getPersonName());			//false

 

 還有一點須要注意的是:在經過原型鏈實現繼承時,不能用對象字面量建立原型方法,由於這樣會重寫原型鏈。 剛剛把Person的實例賦值給原型,緊接着使用字面量致使出錯。------->由於如今的原型包含的是一個Object的實例,不是Person的實例,原型鏈被切斷。

	function Person () {
		this.name = "person";
	}

	Person.prototype.getPersonName = function () {
		return this.name;
	};

	function Student () {
		this.studentname = "student";
	}

	// 繼承了Person
	Student.prototype = new Person();
	// 使用字面量添加新方法,會致使Student.prototype = new Person(); 無效
	Student.prototype = {
		getStudentName :function  () {
			return this.name;
		},
		otherMethod :function  () {
			return false;
		}

	};

	var stu = new Student();
	console.log(stu.getPersonName());			//stu.getPersonName is not a function

  

會報錯  stu.getPersonName is not a function 由於此時Student和Person已經沒有關係了。

 

  因此理想的繼承方式是」寄生組合式繼承「,所謂寄生組合式繼承經過借用構造函數來繼承屬性(父類構造函數裏的屬性+方法),經過原型鏈的形式繼承方法(父類原型裏的方法)。

	// 寄生組合式繼承(理想的繼承方式)
	function inherPrototype (Subobject,Superobject) {
		var prototype = Superobject.prototype;
		prototype.constructor = Subobject;
		Subobject.prototype = prototype;
	}


	function Person (name) {
		this.name = name,
		this.hand = ["right-hand","left-hand"],
		this.say = function () {
			alert("hi");
		}

	}
	Person.prototype.sayName = function () {
		alert(this.name);
	}

	function Student (name,age) {
		Person.call(this,name);
		this.age = age;
	}
	// 實現繼承
	inherPrototype(Student,Person);
	Student.prototype.sayAge = function () {
		alert(this.age);
	}

	var stu1 = new Student("jack",20);


	// 繼承了屬性(構造函數裏的屬性+方法)
	console.log(stu1.hand[0]);			        //----------->輸出right-hand
	stu1.say();							//輸出 hi
	//  繼承了原型裏的方法
	stu1.sayName();						//輸出 jack        

  

  要實現繼承無非就是擁有父類對象的方法和屬性,即擁有一個父類的」模板副本「,inherPrototype()方法作了以下三件事:1.建立父類的原型(prototype)副本,賦值給prototype  2.爲建立的prototype副本添加constructor屬性 ,彌補由於原型賦值而致使失去默認的constructor屬性3.把這個副本賦值給子類原型。這樣便實現了繼承。

相關文章
相關標籤/搜索