亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

帶你徹底理解JavaScript中的原型對(duì)象

 更新時(shí)間:2021年04月14日 11:23:39   作者:做人要厚道2013  
這篇文章主要介紹了帶你徹底理解JavaScript中的原型對(duì)象,文中有詳細(xì)的代碼介紹,對(duì)正在學(xué)習(xí)js的小伙伴們有一定的幫助,需要的朋友可以參考下

一、什么是原型

原型是Javascript中的繼承的基礎(chǔ),JavaScript的繼承就是基于原型的繼承。

1.1 函數(shù)的原型對(duì)象

​ 在JavaScript中,我們創(chuàng)建一個(gè)函數(shù)A(就是聲明一個(gè)函數(shù)), 那么瀏覽器就會(huì)在內(nèi)存中創(chuàng)建一個(gè)對(duì)象B,而且每個(gè)函數(shù)都默認(rèn)會(huì)有一個(gè)屬性 prototype 指向了這個(gè)對(duì)象( 即:prototype的屬性的值是這個(gè)對(duì)象 )。這個(gè)對(duì)象B就是函數(shù)A的原型對(duì)象,簡(jiǎn)稱函數(shù)的原型。這個(gè)原型對(duì)象B 默認(rèn)會(huì)有一個(gè)屬性 constructor 指向了這個(gè)函數(shù)A ( 意思就是說:constructor屬性的值是函數(shù)A )。

​ 看下面的代碼:

<body>
    <script type="text/javascript">
    	/*
    		聲明一個(gè)函數(shù),則這個(gè)函數(shù)默認(rèn)會(huì)有一個(gè)屬性叫 prototype 。而且瀏覽器會(huì)自動(dòng)按照一定的規(guī)則
    		創(chuàng)建一個(gè)對(duì)象,這個(gè)對(duì)象就是這個(gè)函數(shù)的原型對(duì)象,prototype屬性指向這個(gè)原型對(duì)象。這個(gè)原型對(duì)象
    		有一個(gè)屬性叫constructor 執(zhí)行了這個(gè)函數(shù)
			
			注意:原型對(duì)象默認(rèn)只有屬性:constructor。其他都是從Object繼承而來,暫且不用考慮。
		*/
	    function Person () {
	    	
	    }	    
    </script>
</body>

下面的圖描述了聲明一個(gè)函數(shù)之后發(fā)生的事情:

1.2 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象

​ 當(dāng)把一個(gè)函數(shù)作為構(gòu)造函數(shù) (理論上任何函數(shù)都可以作為構(gòu)造函數(shù)) 使用new創(chuàng)建對(duì)象的時(shí)候,那么這個(gè)對(duì)象就會(huì)存在一個(gè)默認(rèn)的不可見的屬性,來指向了構(gòu)造函數(shù)的原型對(duì)象。 這個(gè)不可見的屬性我們一般用 [[prototype]] 來表示,只是這個(gè)屬性沒有辦法直接訪問到。

​ 看下面的代碼:

<body>
    <script type="text/javascript">
	    function Person () {
	    	
	    }	
        /*
        	利用構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象,則這個(gè)對(duì)象會(huì)自動(dòng)添加一個(gè)不可見的屬性 [[prototype]], 而且這個(gè)屬性
        	指向了構(gòu)造函數(shù)的原型對(duì)象。
        */
      	var p1 = new Person();
    </script>
</body>

觀察下面的示意圖:

說明:

1.從上面的圖示中可以看到,創(chuàng)建p1對(duì)象雖然使用的是Person構(gòu)造函數(shù),但是對(duì)象創(chuàng)建出來之后,這個(gè)p1對(duì)象其實(shí)已經(jīng)與Person構(gòu)造函數(shù)沒有任何關(guān)系了,p1對(duì)象的[[ prototype ]]屬性指向的是Person構(gòu)造函數(shù)的原型對(duì)象。

2.如果使用new Person()創(chuàng)建多個(gè)對(duì)象,則多個(gè)對(duì)象都會(huì)同時(shí)指向Person構(gòu)造函數(shù)的原型對(duì)象。

3.我們可以手動(dòng)給這個(gè)原型對(duì)象添加屬性和方法,那么p1,p2,p3…這些對(duì)象就會(huì)共享這些在原型中添加的屬性和方法。

4.如果我們?cè)L問p1中的一個(gè)屬性name,如果在p1對(duì)象中找到,則直接返回。如果p1對(duì)象中沒有找到,則直接去p1對(duì)象的[[prototype]]屬性指向的原型對(duì)象中查找,如果查找到則返回。(如果原型中也沒有找到,則繼續(xù)向上找原型的原型—原型鏈。 后面再講)。

5.如果通過p1對(duì)象添加了一個(gè)屬性name,則p1對(duì)象來說就屏蔽了原型中的屬性name。 換句話說:在p1中就沒有辦法訪問到原型的屬性name了。

6.通過p1對(duì)象只能讀取原型中的屬性name的值,而不能修改原型中的屬性name的值。 p1.name = “李四”; 并不是修改了原型中的值,而是在p1對(duì)象中給添加了一個(gè)屬性name。

看下面的代碼:

<body>
    <script type="text/javascript">
	    function Person () {    	
	    }
      	// 可以使用Person.prototype 直接訪問到原型對(duì)象
	    //給Person函數(shù)的原型對(duì)象中添加一個(gè)屬性 name并且值是 "張三"
	    Person.prototype.name = "張三";
	    Person.prototype.age = 20;

	   	var p1 = new Person();
	   	/*
	   		訪問p1對(duì)象的屬性name,雖然在p1對(duì)象中我們并沒有明確的添加屬性name,但是
	   		p1的 [[prototype]] 屬性指向的原型中有name屬性,所以這個(gè)地方可以訪問到屬性name
	   		就值。
	   		注意:這個(gè)時(shí)候不能通過p1對(duì)象刪除name屬性,因?yàn)橹荒軇h除在p1中刪除的對(duì)象。
	   	*/
	   	alert(p1.name);  // 張三

	   	var p2 = new Person();
	   	alert(p2.name);  // 張三  都是從原型中找到的,所以一樣。

	   	alert(p1.name === p2.name);  // true

	   	// 由于不能修改原型中的值,則這種方法就直接在p1中添加了一個(gè)新的屬性name,然后在p1中無法再訪問到
	   	//原型中的屬性。
	   	p1.name = "李四";
	   	alert("p1:" + p1.name);
	   	// 由于p2中沒有name屬性,則對(duì)p2來說仍然是訪問的原型中的屬性。	
	   	alert("p2:" + p2.name);  // 張三  
    </script>
</body>

二、與原型有關(guān)的幾個(gè)屬性和方法

2.1 prototype屬性

​ prototype 存在于構(gòu)造函數(shù)中 (其實(shí)任意函數(shù)中都有,只是不是構(gòu)造函數(shù)的時(shí)候prototype我們不關(guān)注而已) ,他指向了這個(gè)構(gòu)造函數(shù)的原型對(duì)象。

​ 參考前面的示意圖。

2.2 constructor屬性

​ constructor屬性存在于原型對(duì)象中,他指向了構(gòu)造函數(shù)

看下面的代碼:

<script type="text/javascript">
	function Person () {
	}
	alert(Person.prototype.constructor === Person);	// true
	var p1 = new Person();
  	//使用instanceof 操作符可以判斷一個(gè)對(duì)象的類型。  
  	//typeof一般用來獲取簡(jiǎn)單類型和函數(shù)。而引用類型一般使用instanceof,因?yàn)橐妙愋陀胻ypeof 總是返回object。
	alert(p1 instanceof Person);	// true
</script>

我們根據(jù)需要,可以Person.prototype 屬性指定新的對(duì)象,來作為Person的原型對(duì)象。

但是這個(gè)時(shí)候有個(gè)問題,新的對(duì)象的constructor屬性則不再指向Person構(gòu)造函數(shù)了。

看下面的代碼:

<script type="text/javascript">
	function Person () {
		
	}
	//直接給Person的原型指定對(duì)象字面量。則這個(gè)對(duì)象的constructor屬性不再指向Person函數(shù)
	Person.prototype = {
		name:"志玲",
		age:20
	};
	var p1 = new Person();
	alert(p1.name);  // 志玲

	alert(p1 instanceof Person); // true
	alert(Person.prototype.constructor === Person); //false
  	//如果constructor對(duì)你很重要,你應(yīng)該在Person.prototype中添加一行這樣的代碼:
  	/*
  	Person.prototype = {
      	constructor : Person	//讓constructor重新指向Person函數(shù)
  	}
  	*/
</script>

2.3 __proto__ 屬性(注意:左右各是2個(gè)下劃線)

​ 用構(gòu)造方法創(chuàng)建一個(gè)新的對(duì)象之后,這個(gè)對(duì)象中默認(rèn)會(huì)有一個(gè)不可訪問的屬性 [[prototype]] , 這個(gè)屬性就指向了構(gòu)造方法的原型對(duì)象。

​ 但是在個(gè)別瀏覽器中,也提供了對(duì)這個(gè)屬性[[prototype]]的訪問(chrome瀏覽器和火狐瀏覽器。ie瀏覽器不支持)。訪問方式:p1.__proto__

​ 但是開發(fā)者盡量不要用這種方式去訪問,因?yàn)椴僮鞑簧鲿?huì)改變這個(gè)對(duì)象的繼承原型鏈。

<script type="text/javascript">
	function Person () {
		
	}
	//直接給Person的原型指定對(duì)象字面量。則這個(gè)對(duì)象的constructor屬性不再指向Person函數(shù)
	Person.prototype = {
		constructor : Person,
		name:"志玲",
		age:20
	};
	var p1 = new Person();

	alert(p1.__proto__ === Person.prototype);	//true
	
</script>

2.4 hasOwnProperty() 方法

​ 大家知道,我們用去訪問一個(gè)對(duì)象的屬性的時(shí)候,這個(gè)屬性既有可能來自對(duì)象本身,也有可能來自這個(gè)對(duì)象的[[prototype]]屬性指向的原型。

​ 那么如何判斷這個(gè)對(duì)象的來源呢?

​ hasOwnProperty方法,可以判斷一個(gè)屬性是否來自對(duì)象本身。

<script type="text/javascript">
	function Person () {
		
	}
	Person.prototype.name = "志玲";
	var p1 = new Person();
	p1.sex = "女";
  	//sex屬性是直接在p1屬性中添加,所以是true
	alert("sex屬性是對(duì)象本身的:" + p1.hasOwnProperty("sex"));
  	// name屬性是在原型中添加的,所以是false
	alert("name屬性是對(duì)象本身的:" + p1.hasOwnProperty("name"));
  	//  age 屬性不存在,所以也是false
	alert("age屬性是存在于對(duì)象本身:" + p1.hasOwnProperty("age"));
	
</script>

所以,通過hasOwnProperty這個(gè)方法可以判斷一個(gè)對(duì)象是否在對(duì)象本身添加的,但是不能判斷是否存在于原型中,因?yàn)橛锌赡苓@個(gè)屬性不存在。

也即是說,在原型中的屬性和不存在的屬性都會(huì)返回fasle。

如何判斷一個(gè)屬性是否存在于原型中呢?

2.5 in 操作符

​ in操作符用來判斷一個(gè)屬性是否存在于這個(gè)對(duì)象中。但是在查找這個(gè)屬性時(shí)候,現(xiàn)在對(duì)象本身中找,如果對(duì)象找不到再去原型中找。換句話說,只要對(duì)象和原型中有一個(gè)地方存在這個(gè)屬性,就返回true

<script type="text/javascript">
	function Person () {
		
	}
	Person.prototype.name = "志玲";
	var p1 = new Person();
	p1.sex = "女";
	alert("sex" in p1);		// 對(duì)象本身添加的,所以true
	alert("name" in p1);	//原型中存在,所以true
	alert("age" in p1); 	//對(duì)象和原型中都不存在,所以false
	
</script>

回到前面的問題,如果判斷一個(gè)屬性是否存在于原型中:

如果一個(gè)屬性存在,但是沒有在對(duì)象本身中,則一定存在于原型中。

<script type="text/javascript">
	function Person () {
	}
	Person.prototype.name = "志玲";
	var p1 = new Person();
	p1.sex = "女";
	
	//定義一個(gè)函數(shù)去判斷原型所在的位置
	function propertyLocation(obj, prop){
		if(!(prop in obj)){
			alert(prop + "屬性不存在");
		}else if(obj.hasOwnProperty(prop)){
			alert(prop + "屬性存在于對(duì)象中");
		}else {
			alert(prop + "對(duì)象存在于原型中");
		}
	}
	propertyLocation(p1, "age");
	propertyLocation(p1, "name");
	propertyLocation(p1, "sex");
</script

三、組合原型模型和構(gòu)造函數(shù)模型創(chuàng)建對(duì)象

3.1 原型模型創(chuàng)建對(duì)象的缺陷

​ 原型中的所有的屬性都是共享的。也就是說,用同一個(gè)構(gòu)造函數(shù)創(chuàng)建的對(duì)象去訪問原型中的屬性的時(shí)候,大家都是訪問的同一個(gè)對(duì)象,如果一個(gè)對(duì)象對(duì)原型的屬性進(jìn)行了修改,則會(huì)反映到所有的對(duì)象上面。

​ 但是在實(shí)際使用中,每個(gè)對(duì)象的屬性一般是不同的。張三的姓名是張三,李四的姓名是李四。

​ **但是,這個(gè)共享特性對(duì) 方法(屬性值是函數(shù)的屬性)又是非常合適的。**所有的對(duì)象共享方法是最佳狀態(tài)。這種特性在c#和Java中是天生存在的。

3.2 構(gòu)造函數(shù)模型創(chuàng)建對(duì)象的缺陷

​ 在構(gòu)造函數(shù)中添加的屬性和方法,每個(gè)對(duì)象都有自己獨(dú)有的一份,大家不會(huì)共享。這個(gè)特性對(duì)屬性比較合適,但是對(duì)方法又不太合適。因?yàn)閷?duì)所有對(duì)象來說,他們的方法應(yīng)該是一份就夠了,沒有必要每人一份,造成內(nèi)存的浪費(fèi)和性能的低下。

<script type="text/javascript">
	function Person() {
	    this.name = "李四";
	    this.age = 20;
	    this.eat = function() {
	        alert("吃完?yáng)|西");
	    }
	}
	var p1 = new Person();
	var p2 = new Person();
	//每個(gè)對(duì)象都會(huì)有不同的方法
	alert(p1.eat === p2.eat); //fasle
</script>

可以使用下面的方法解決:

<script type="text/javascript">
	function Person() {
	    this.name = "李四";
	    this.age = 20;
	    this.eat = eat;
	}
  	function eat() {
	    alert("吃完?yáng)|西");
    }
	var p1 = new Person();
	var p2 = new Person();
	//因?yàn)閑at屬性都是賦值的同一個(gè)函數(shù),所以是true
	alert(p1.eat === p2.eat); //true
</script>

但是上面的這種解決方法具有致命的缺陷:封裝性太差。使用面向?qū)ο?,目的之一就是封裝代碼,這個(gè)時(shí)候?yàn)榱诵阅苡忠汛a抽出對(duì)象之外,這是反人類的設(shè)計(jì)。

3.3 使用組合模式解決上述兩種缺陷

​ 原型模式適合封裝方法,構(gòu)造函數(shù)模式適合封裝屬性,綜合兩種模式的優(yōu)點(diǎn)就有了組合模式。

<script type="text/javascript">
	//在構(gòu)造方法內(nèi)部封裝屬性
	function Person(name, age) {
	    this.name = name;
	    this.age = age;
	}
	//在原型對(duì)象內(nèi)封裝方法
	Person.prototype.eat = function (food) {
		alert(this.name + "愛吃" + food);
	}
	Person.prototype.play = function (playName) {
		alert(this.name + "愛玩" + playName);
	}
    
	var p1 = new Person("李四", 20);
	var p2 = new Person("張三", 30);
	p1.eat("蘋果");
	p2.eat("香蕉");
	p1.play("志玲");
	p2.play("鳳姐");
</script>

四、動(dòng)態(tài)原型模式創(chuàng)建對(duì)象

​ 前面講到的組合模式,也并非完美無缺,有一點(diǎn)也是感覺不是很完美。把構(gòu)造方法和原型分開寫,總讓人感覺不舒服,應(yīng)該想辦法把構(gòu)造方法和原型封裝在一起,所以就有了動(dòng)態(tài)原型模式。

​ 動(dòng)態(tài)原型模式把所有的屬性和方法都封裝在構(gòu)造方法中,而僅僅在需要的時(shí)候才去在構(gòu)造方法中初始化原型,又保持了同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn)。

看下面的代碼:

<script type="text/javascript">
	//構(gòu)造方法內(nèi)部封裝屬性
	function Person(name, age) {
		//每個(gè)對(duì)象都添加自己的屬性
	    this.name = name;
	    this.age = age;
	    /*
	    	判斷this.eat這個(gè)屬性是不是function,如果不是function則證明是第一次創(chuàng)建對(duì)象,
	    	則把這個(gè)funcion添加到原型中。
	    	如果是function,則代表原型中已經(jīng)有了這個(gè)方法,則不需要再添加。
	    	perfect!完美解決了性能和代碼的封裝問題。
	    */
	    if(typeof this.eat !== "function"){
	    	Person.prototype.eat = function () {
	    		alert(this.name + " 在吃");
	    	}
	    }
	}
	var p1 = new Person("志玲", 40);
	p1.eat();	
</script>

到此這篇關(guān)于帶你徹底理解JavaScript中的原型對(duì)象的文章就介紹到這了,更多相關(guān)理解js原型對(duì)象內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 微信小程序 轉(zhuǎn)發(fā)功能的實(shí)現(xiàn)

    微信小程序 轉(zhuǎn)發(fā)功能的實(shí)現(xiàn)

    這篇文章主要介紹了微信小程序 轉(zhuǎn)發(fā)功能的實(shí)現(xiàn)的相關(guān)資料,這里提供實(shí)現(xiàn)方法及實(shí)例幫助大家學(xué)習(xí)理解,需要的朋友可以參考下
    2017-08-08
  • Intersection?Observer交叉觀察器示例解析

    Intersection?Observer交叉觀察器示例解析

    這篇文章主要為大家介紹了Intersection?Observer交叉觀察器示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 最新評(píng)論