1. 程式人生 > >原生JavaScript中的原型鏈和繼承(程式碼例項詳解)

原生JavaScript中的原型鏈和繼承(程式碼例項詳解)

       本文通過例子將原生JavaScript中的原型鏈和繼承進行介紹,在學習時可以將程式碼執行下,檢視下控制檯相關的輸出;在寫程式碼的同時添加了很多註釋,方便理解和程式碼分塊。例項程式碼如下:

<!DOCTYPE html>
<html>
<head>
	<title></title>
</head>
<body>
<script type="text/javascript">
// ------
// 0.原型鏈
// ------
	// 基本模式
	function SuperType(){
		this.property = true;
	};

	SuperType.prototype.getSuperValue = function(){
		return this.property;
	};

	function SubType (){
		this.subproperty = false;
	};

	//繼承SuperType
	SubType.prototype = new SuperType;

	SubType.prototype.getSubValue = function(){
		return this.subproperty;
	};

	var instance = new SubType();
	console.log(instance.getSuperValue);

	// 以上繼承的實現是通過建立SuperType的例項,並將該例項賦值給SubTypr.prototype實現的。
	// 本質是重寫了原型物件,代之以一個新型別的例項。換句話說,原來屬於SuperType的例項中的屬性和方法,現在也存在於SubType.prototype中了。
	// 確認繼承關係後又給SubType.prototype添加了新的方法。
	// 例項以及建構函式和原型之間的關係如下:
	//  -------------------------------------
	//  -------------------------------------
	//      SuperType 
	//  prototype       @SuperType.prototype
	//  -------------------------------------
	//  	SuperType.prototype
	//  constructor	   @SuperType
	//  getSuperValue  (function)
	//  -------------------------------------
	//  	SubType
	//  prototype      @SubType.prototype
	//  -------------------------------------
	//  	SubType.prototype
	//	[[Prototype]]   @SuperType.prototype
	//	prototype       true
	//	getSubValue		(function)
	//	-------------------------------------
	//		instance
	//	[[Prototype]]	@SubType.prototype
	//	subproperty		false
	//	-------------------------------------
	//	-------------------------------------
	//	以上關係圖中,SubType的預設原型換成了一個新的原型--SuperTyrpe的例項。於是,新原型不僅具有了SuperType例項的全部屬性和方法,還內部還有一個新指標,指向
	//	了SuperType的原型。
	//	此外還需要注意的是instance.constructor現在指向的是SuperType,這是由於SubType.prototype被重寫,該原型指向了另一個物件——SuperType的原型,而該原型物件
	//	的constructor屬性指向了SuperType


	//	0.1
	//  不要忘記預設的原型
	//  -------------------------------------
	//  -------------------------------------
	//  	Object 
	//  prototype       @Object.prototype
	//  -------------------------------------
	//  	Object.prototype
	//  constructor	    @Object
	//  hasOwnProperty  (function)	
	//	toString		(function)
	//	valueOf			(function)
	//	-------------------------------------
	//      SuperType 
	//  prototype       @SuperType.prototype
	//  -------------------------------------
	//  	SuperType.prototype
	// 	[[Prototype]]	@Object.prototype
	//  constructor	    @SuperType
	//  getSuperValue   (function)
	//  -------------------------------------
	//  	SubType
	//  prototype       @SubType.prototype
	//  -------------------------------------
	//  	SubType.prototype
	//	[[Prototype]]   @SuperType.prototype
	//	prototype       true
	//	getSubValue		(function)
	//	-------------------------------------
	//		instance
	//	[[Prototype]]	@SubType.prototype
	//	subproperty		false
	//	-------------------------------------
	//	-------------------------------------
	//  確定原型和例項的關係
	//  instance instanceOf Object;  	//true
	//  instance instanceOf SuperType;  //true
	//  instance instanceOf SubType;  	//true
	//	Object.prototype.isPrototypeOf(instance); //true
	//	SuperType.prototype.isPrototypeOf(instance); //true
	//	SubType.prototype.isPrototypeOf(instance); //true

	//	謹慎定義方法
	//  子型別有時候需要覆蓋超類中的方法或者新增超類中不存在的方法。不管怎樣在給原型新增方法時一定放在替換原型的語句之後。

	//	原型鏈的問題
	//	在包含引用型別屬性的繼承中,通過原型繼承時,原型會變成另一種型別的例項,於是原先的例項屬性也會變成現在原型的屬性。
	//	此時,之前的引用型別的例項屬性在新的例項包含,該屬性會被所有子類例項共享。

//	------------
//	借用建構函式
//	------------
	//	在子型別建構函式的內部呼叫超型別的建構函式。通過使用apply()和call()方法可以在新建立的物件上執行建構函式。
	function SuperType1(){
		this.colors = ['red','blue'];
	};

	function SubType1(){
		//繼承了SuperType1
		SuperType1.call(this);
	};

	var ins1 = new SubType1();
	ins1.colors.push('black');
	console.log(ins1.colors); //'red','blue','black'
	var ins2 = new SubType1();
	console.log(ins2.colors);	//'red','blue'
	// 用call()借調了SuperType的建構函式,在建立SubType例項時呼叫了SuperType的建構函式,會執行所有超類的初始化程式碼,並且每個子類物件都有自己的副本。
	// 同時,借用建構函式可以向超類傳遞引數
	function SuperType2(name){
		this.name = name;
	};

	function SubType2(){
		//繼承了SuperType1
		SuperType2.call(this,'jum');
		this.age = 18;
	};

	var ins3 = new SubType2();
	console.log(ins3.name); //'red','blue','black'
	console.log(ins3.age);	//'red','blue'

//	------------
//	組合繼承(偽經典繼承)(最常用的繼承方式)
//	------------
	//	對原型鏈和借用建構函式的組合使用,融合了兩者的優點,避開了各自的缺點
	//	使用原型鏈對原型屬性和方法的繼承,使用借用建構函式實現對例項屬性的繼承。
	//	組合式繼承結構如下:
	function SuperType3(name){
		this.name = name;
		this.colors = ['red','blue'];
	};

	SuperType3.prototype.sayName = function(){
		console.log(this.name);
	};

	function SubType3(name, age){
		//繼承屬性
		SuperType3.call(this,name);
		this.age = age;
	};

	//繼承方法
	SubType3.prototype = new SuperType3();
	SubType3.prototype.constructor = SubType3;
	SubType3.prototype.sayAge = function(){
		console.log(this.age);
	};

	var sub1 = new SubType3('jum3',18);
	sub1.colors.push('black');
	console.log(sub1.colors);
	sub1.sayName();
	sub1.sayAge();

	var sub12 = new SubType3('po',25);
	console.log(sub12.colors);
	sub12.sayName();
	sub12.sayAge();	

//	--------------
//	原型式繼承    by 克羅克福德
//	--------------
	//	此種方法沒有嚴格意義上的建構函式。藉助原型可以基於已有的物件建立新物件,同時還不必因此建立自定義型別。
	//	實現方法d原版如下:
	function object(o){
		function F(){};
		F.prototype = o;
		return new F();
	};
	// @1
	var person = {
		name: 'zhou',
		friends: ['a','b']
	};

	var person1 = object(person); //@2
	person1.name = 'zhoujum';
	person1.friends.push('c');

	var person2 = object(person); //@3
	person2.name = 'zhoujumpo';
	person2.friends.push('d');

	console.log(person.friends);
	console.log(person1.friends);
	console.log(person2.friends);

	// ECMAScript5在Object中增加了create()方法進行了規範,因此可以直接從上段程式碼 @1 處開始,
	// create()方法有兩個引數,一個用作新物件原型物件;另一個可選,為新物件定義額外屬性的物件。
	// 在傳入一個引數時 @2 和 @3 處可以改寫為    var person1 = Object.create(person);  和上段程式碼一樣。
	// 第二個引數和Object.defineProperties()方法的第二個引數格式相同,每個屬性都可以通過自己的描述符定義。
	var person3 = Object.create(person, {
		name: {
			value: 'name123'
		}
	});
	console.log(person3.name);

//	--------------
//	寄生式繼承		by 	克羅克福德
//	--------------
	//	寄生式繼承與寄生式建構函式和工廠模式類似,即建立一個僅用於封裝繼承過程的函式
	//	用途範圍:在主要考慮物件而不是自定義型別和建構函式的情況下都適用於這種模式。
	function createPerson4(o){
		var clone = Object(o);
		clone.sayHi = function(){
			console.log("Hi, 寄生式繼承");
		};
		return clone;
	};
	//	-------------------------------------------
	var person4 = {
		name: 'person4',
		friends: ['a', 'b']
	};
	var subPerson4 = createPerson4(person4);
	subPerson4.sayHi();

	//	使用寄生式繼承來為物件新增函式,會由於不能做到函式複用而降低效率,這與建構函式模式類似。

//	-------------------
//	寄生組合式繼承
//	-------------------
	//	組合繼承式JavaScript最常用的繼承方式,但是最大的問題是無論什麼情況下都會呼叫兩次超類的
	//	建構函式:一次是在建立子型別的原型的時候,另一次是在子型別建構函式內部,構造和原型組合繼承如下:

	function SuperType4(name){
		this.name = name;
		this.colors = ["red","blue"];
	};

	SuperType4.prototype.sayName = function(){
		console.log(this.name);
	};

	function SubType4(name, age){
		SuperType4.call(this, name);	//第二次呼叫SuperType4

		this.age = age;
	};

	SubType4.prototype = new SuperType4();  //第一次呼叫SuperType4
	SubType4.prototype.sayAge = function(){
		console.log(this.age);
	};
	// * 第一次呼叫時SubType4.prototype會得到SuperType4的兩個例項屬性name和colors,此時兩個屬性存在於
	// SubType4的原型中。當呼叫SubType4的建構函式時,又會第二次呼叫SuperType4的建構函式,這次又在新物件
	// 上建立了例項屬性name和colors,於是這兩個屬性就遮蔽了原型中的兩個同名屬性。
	// * 此時name和colors屬性在SubType4的原型和例項中都存在,這就是呼叫兩次SuperType4的結果。
	// 解決這個問題可以使用寄生組合繼承。
	
	// 寄生組合繼承,即借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。
	// 基本思路是:不必為了指定子型別的類原型而呼叫超類的建構函式,我們需要的不過是超型別原型的一個副本
	// 而已。本質上就是使用寄生式繼承來繼承超型別的原型,然後將結果指定給子型別的原型。
	// 基本模式如下:
	function inheritPrototype(SubType4, SuperType4){
		var prototype = object(SuperType4.prototype);//建立物件
		prototype.constructor = SubType4;//增強物件
		SubType4.prototype = prototype;//指定物件
	};
	// inheritPrototype()方法實現了最簡單形式的寄生組合繼承。藉此可替代上方程式碼給子型別原型賦值的語句。
	// 改正後上方程式碼中
	//	SubType4.prototype = new SuperType4();  //第一次呼叫SuperType4
	// 換為
	//	inheritPrototype(SubType4, SuperType4);

	// * 借用寄生組合繼承的高效之處是對SuperType4()函式的一次呼叫;並且避免了在SubType4.prototype上建立
	// 不必要的多餘屬性;與此同時,還保證了原型鏈的不變。


	// YUI 的YAHOO.lang.extend()方法採用了寄生組合繼承,從而讓該模式首次出現在了一個應用非常廣泛的
	// JavaScript庫中。


</script>
</body>
</html>
THIS END

相關推薦

原生JavaScript原型繼承程式碼例項

       本文通過例子將原生JavaScript中的原型鏈和繼承進行介紹,在學習時可以將程式碼執行下,檢視下控制檯相關的輸出;在寫程式碼的同時添加了很多註釋,方便理解和程式碼分塊。例項程式碼如下:<!DOCTYPE html> <html> <

js 原型繼承

部分 聲明 ole 重復 操作 param urn 構造 cti 在理解繼承之前,需要知道 js 的三個東西: 什麽是 JS 原型鏈 this 的值到底是什麽 JS 的 new 到底是幹什麽的 1. 什麽是 JS 原型鏈? 我們知道 JS 有對象,比如 va

我們為什麼要使用ListSetList,Set

  1、集合概述 類圖 集合和陣列的區別? 集合基本方法 集合特有的遍歷方式?

JavaScript原型繼承

工具 call logs 不難 code 也會 str 最簡 創建子類 理解原型鏈 在 JavaScript 的世界中,函數是一等公民。 上面這句話在很多地方都看到過。用我自己的話來理解就是:函數既當爹又當媽。“當爹”是因為我們用函數去處理各種“粗活累活”(各種工具函

JavaScript原型繼承

eat 過渡 存在 es6 向上 reset 保護 foo 創建 1.概念   JavaScript並不提供一個class的實現,在ES6中提供class關鍵字,但是這個只是一個語法糖,JavaScript仍然是基於原型的。JavaScript只有一種結構:對象。每

JavaScript原型的那些事

單鏈表 分享圖片 mage classes 替換 afa 就是 區別 urn 引言 在面向對象的語言中繼承是非常重要的概念,許多面向對象語言都支持兩種繼承方式:接口繼承和實現繼承。接口繼承制只繼承方法簽名,而實現繼承繼承實際的方法。在ECMAScript中函數沒有簽名,所以

關於原型原型繼承的討論

寫部落格也有不短的時間了,經常做一些自己的總結和技術的分享。但是之前的由於之前的備用號碼丟失,也正好準備換個備用卡,所以直接登出了,沒有找回。所以就開了個新的部落格。以後,我會經常在這裡分享一下心得,和和大家做一些技術探討。今天就原型鏈做一個總結。 在剛接觸到js原型和原型鏈的時候,可能

原型繼承:六種常見繼承模式

一、原型鏈繼承: function SuperType(){ this.colors = ["red", "blue", "green"]; } SuperType.prototype.Fun = function(){ }; function SubType(){ } //

【學習筆記】在原生javascript使用ActiveX外掛

什麼是外掛 現在的瀏覽器提供了大量的內建功能,但仍然有一些工作無法完成,如播放音訊和視訊。外掛及其擴充套件瀏覽器功能就尤為重要。 外掛是可下載的應用程式,可以插入到瀏覽器中,現在有很多不同的外掛,常用的有Adobe Flash  Palyer ,Microsoft的Silv

原型繼承:理解Object.create()

Object.create(proto[, propertiesObject]):接受兩個引數,返回一個物件引數一:proto應該是新建立物件的prototype引數二:可選。該引數物件是一組屬性與值,該物件的屬性名稱將是新建立的物件的屬性名稱,值是屬性描述符(這些屬性描述符

c++ 去除字串的空格標點符號 remove_if 函式的用法

C++中提供了自動刪除空格和標點符號的函式,使用如下: #include <ctype.h> #include <algorithm> str_testing.erase(

Android字型設定calligraphy高效載入字型包用法

Android預設字型 TextView 的 typeface 屬性支援 “Sans”,”serif”,”monospace” 這三種字型,如果在沒有指定字型的情況下,系統預設會使用 “Sans” 作為文字顯示的字型。但這三種字型只支援英文,也就是說只要你顯

c# 的封裝、繼承、多態

作用 整體 自己 實現 擴展 封裝 長方形 產生 同名 面向對象有封裝、繼承、多態這三個特性,面向對象編程按照現實世界的特點來管理復雜的事物,把它們抽象為對象,具有自己的狀態和行為,通過對消息的反應來完成任務。這種編程方法提供了非常強大的多樣性,大大增加了代碼的重用機會,增

javascript的事件委托或是事件代理

開始 reat 事件 網上 html 比較 移動 由於 move 概述: 那什麽叫事件委托呢?它還有一個名字叫事件代理,JavaScript高級程序設計上講:事件委托就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。那這是什麽意思呢?網上的各位大牛們講

Javascript函數

弱類型語言 9.1 指定 fun 並不會 val 避免 中國 width 一.函數 Javascript是一門基於對象的腳本語言,代碼復用的單位是函數,但它的函數比結構化程序設計語言的函數功能更豐富。JavaScript語言中的函數是“一等公民”,它可

虛擬機器,伺服器原來是這麼部署的Tomcat篇

在別的系統中例如redhat,centos系統中就可以直接放到指定位置直接解壓安裝了。 因為在烏班圖的系統中由於只能註冊普通的使用者,不能註冊root使用者。 所以需要先把apache-tomcat-7.0.67.tar.gz的安裝包拷貝到普通使用者的許可權中去。 我們可以使用W

第六章樹二叉樹--樹森林-計算機17級 7-1 樹的同構 25 分答案超

7-1 樹的同構 (25 分) 給定兩棵樹T1和T2。如果T1可以通過若干次左右孩子互換就變成T2,則我們稱兩棵樹是“同構”的。例如圖1給出的兩棵樹就是同構的,因為我們把其中一棵樹的結點A、B、G的左右孩子互換後,就得到另外一棵樹。而圖2就不是同構的。  

js原生之scrollTop、offsetHeightoffsetTop等屬性用法

本文轉載自:https://www.cnblogs.com/koleyang/p/4939853.html **scrollTop、offsetHeight和offsetTop等屬性用法詳解:** 標題中的幾個相關相關屬性在網頁中有這大量的應用,尤其是在運動框架中,但是由於有些屬性相互之間的

synchronized lock 的使用分析優缺點對比

1. synchronized 加同步格式 synchronized (需要一個任意的物件(鎖)){ 程式碼塊中放操作共享資料的程式碼。 } synchronized 缺陷 synchronized 是java 中的一個關鍵字,也就是說是java 語言的內建的特性 如果一

AndroidMVC架構MVP架構的實踐 通俗易懂的Demo

前言 相信從事軟體開發的夥計們肯定熟悉或者聽說過專案架構,比如要新開發一個APP或者Web專案,首先考慮的就是專案需要設計什麼樣的架構,MVC還是MVP呢?MVC和MVP具體是怎麼體現的,有哪些優點,哪些缺點呢? 為什麼需要架構設計 假如我們不需要架構設計,那