亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JavaScript中的prototype原型學習指南

2019-11-20 10:07:42
字體:
來源:轉載
供稿:網友

原型是什么

Function 類型有一個屬性 prototype,直接翻譯過來就是原型。這個屬性就是一個指針,指向一個對象,這個對象包含一些屬性和方法,這些屬性和方法會被當前函數生成的所有實例(對象)所共享。

這句話根據前面所說的,細細琢磨下來,就可以得到下面代碼:

function Person(){ ...}Person.prototype = { country : 'china', sayName : function(){  ... }}

先創建了一個 Function 類型的實例 person,然后 person 的方法 prototype 是一個對象,就聲明指向了一個對象。這個對象里面的屬性和方法,會被當前 person 函數生成的實例所共享。也就是說:

person1 = new Person();person2 = new Person();

person1 和 person2 都是通過 Person 這個 Function 類型實例,再次生成的實例,它們倆都有共同的屬性 country 和方法 sayName,因為它們都有某個指針(__proto__),直接指向 Person.prototype 所指向的對象。不過要注意 __proto__ 這個指針是不標準的,只有 Chrome 和 Firefox 等瀏覽器自己定義的,實際中,也不會用到這個屬性,只是作為理解 prototype 來用:

201659145452510.png (668×270)

關于原型等用法,后面會更具體的講到。

創建對象的模式

下面,我們就來看下創建對象的方法和常用模式,以及它們之間的優缺點。

1.工廠模式

就像工廠一樣,抽象了創建具體對象的過程,用函數來封裝以特定接口創建對象的細節。通過使用函數代替部分重復工作,代碼如下:

function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){  alert(this.name); }; return o;}var person1 = createPerson("jiangshui","22","engineer");

這樣就創建出來了一個人,工廠模式解決了多個相似對象重復創建問題,但是沒有解決對象識別問題。只是單純的創建了一個對象,而不管這個對象是從人類模版還是動物模版創建的,無法區分這個對象的類型。

2.構造函數模式

創建一個自定義的構造函數,從而定義自定義對象類型的屬性和方法。

function Person(name, age, job){ this.name = name; this.age = age; this.job = jpb; this.sayName = function(){  alert(this.name); };};var person1 = new Person(...);

3.構造函數模式與工廠模式區別:

  • 沒有顯式的創建對象。
  • 直接將屬性和方法賦值 this 對象。
  • 沒有 return 語句。

Person 是 Function 類型的對象,new 之后,會繼續產生一個對象,但這個新產生的對象,由于在函數中傳遞進去參數,并賦值給了 this 指針,那么傳遞進去的內容,就變成了新產生對象的屬性或方法。

構造函數默認習慣是首字母大寫,上面代碼執行經歷了下面幾個步驟:

  • 創建一個新對象
  • 將構造函數作用域賦值給新對象
  • 執行構造函數中的代碼
  • 返回新對象

這樣生成的實例中,都默認包含一個 constructor 屬性指向構造函數,例如:

alert(person1.constructor == Person);

所以用構造函數模式,有類型的區分,可以將它的實例標識為一種特定的類型。

此外,構造函數就是普通的函數,因為要反饋得到新對象,所以用 new 來調用。如果不用的話,直接執行就跟普通函數一樣,例如上面,執行 Person.sayName() 會彈出 window.name,因為函數在 window 下面執行,所以 this 指向 window。

構造函數模式也是有缺陷的,構造函數模式里面的方法,在每個實例上都重新創建了一遍,因此不同實例上的同名函數是不相等的。例如:

person1.sayName == person2.sayName; //false

也就是說,由構造函數生成的每個對象實例,屬性和方法都是獨有的,都是復制了一遍。屬性獨有是必須的,因為這正是對象之間不同的地方,但是很多方法功能和代碼都是一樣的,重復復制多次,顯然就會浪費資源。

所以我們可以把函數放在外面,然后在構造函數里面,用指針指向這個函數,那么生成的實例中,方法存儲的就是一個指向某函數的指針,也就共用一個函數了:

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

但是這樣,這個函數就變成了全局函數,而且與 Person 構造函數關聯性不強,沒有封裝性可言。

下面有請原型模式登場。

原型模式

前面已經介紹了一部分關于原型的基礎知識。簡單的說,就是每個函數都有一個 prototype 屬性,指向一個對象(原型對象),這個對象里面可以放一些屬性或者方法。然后這個函數生成的實例,會有一個不規范的屬性(__proto__)指向原型。

由此來看,你應該可以理解:prototype 產生的屬性和方法是所有實例共享的。

這樣正好解決了上面構造函數模式中,實例中函數的共用問題。例如下面代碼:

function Person(){ ....}Person.prototype.name = "jiangshui";Person.prototype.sayName = function(){ alert(this.name);};var person1 = new Person();person1.sayName(); //jiangshui

或者

Person.prototype = { constructor : Person, name : "jiangshui", sayName : function(){  alert(this.name); }};

第二種方法覆蓋了整個 prototype 對象,所以需要手動指定 constructor 屬性,指向構造函數否則會指向 Object。

梳理一下它們的關系:

  • Person.prototype -》 原型對象,可以定義一些屬性或者參數,被所有實例共用。
  • Person.prototype.constructor == Person ―》 原型對象有個默認的屬性 constructor 指向該原型對象所屬的構造函數(注意另一種寫法會覆蓋掉這個屬性,需要重新指定)。
  • person1 = new Person() -》構造函數生成實例,實例包含了構造函數的內容和原型對象的內容。
  • person1.__proto__ -》指向創建這個實例的原型對象(不規范,不要用)。

使用 isPrototypeOf() 可以確定對象之間的關系。例如:

Person.prototype.isPrototypeOf(person1);

當代碼讀取某個對象的某個屬性,會執行搜索。先從當前對象開始,如果沒有,則搜索指針指向的原型對象,而不會搜索構造函數。對象實例可以訪問但是不能重寫原型對象的值。如果實例中設置了與原型對象同名的屬性,則搜索過程,在實例中結束而不會訪問原型對象,所以達到覆蓋的目的。因此即使這個屬性設置為 null,也表示在實例中已經存在該屬性,而不會取消掉這個屬性,從而可以訪問原型對應屬性。

所以需要使用 delete 操作符,完全刪除實例屬性,從而可以重新訪問原型。

原型是動態的,對原型對象所做的任何修改,都能立即從實例上反映出來。原因是實例與原型之間的松散鏈接關系,每次調用實例的屬性方法,都會進行一次查詢,如果原型變了,查詢結果也就變了。

了解原型之后,我們也可以對原生對象添加新方法或屬性。Object、Array、String 等原生引用類型,與上面構造函數類似,我們可以用 prototype 擴充它們的方法。例如:

String.prototype.startsWith = function(text){ return this.indexOf(text) == 0;};var msg = "Hello World";msg.startsWith("Hello");

這段代碼為 String 這個原生引用類型,增加了一個 startsWith 方法,功能就是傳遞進去一個參數,看看要測試的字符串是否以參數開始。由于原型的動態性,所以只要執行一下,所有字符串類型的變量全都獲得了這個方法。

但是不推薦使用這個方法,如果用的太多,代碼太多,會導致維護困難、代碼混亂等情況。一般情況下,會先繼承某個原生引用類型,然后再在新自定義的類型上創建。關于繼承,后面會再總結。

原型模式也不是萬能的,原型中的所有屬性和方法是被所有實例共享的,所以對于函數之類非常合適,而對于包含引用類型的屬性來說,就會產生一些沖突。例如:

function Person(){}Person.prototype = { constructor : Person, friends : ["greg","jack"]};var person1 = new Person();var person2 = new Person();person1.friends.push("tom");console.log(person2.friends);

你會在 console 中看到,person2 的 friends 多了一個 tom,這并不是我想要的,但是對 person1 定義他的朋友時,的確影響到了實例 person2。

所以我們要結合原型模式和構造函數模式來使用。

組合使用構造函數模式和原型模式

這就是最常用的模式,構造函數用來定義實例屬性,通過傳遞參數實現自定義;原型用來定義方法或者需要所有實例共享的屬性。這樣,既實現了自定義,又保證了共用,還避免了問題。

function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["greg","jack"];}Person.prototype = { constructor : Person, sayName : function(){  alert(this.name); }};var jiangshui = new Person("jiangshui","22","engineer");

實際應用示例

OK,到了這里,你可能會看懂原型是啥,以及如何創建對象,可是,這些又有什么用?確實,我之前的工作,一直也就是用 jQuery 寫一些代碼就可以了,根本用不到封裝然后生成對象實現功能等。那這些究竟有什么用?

這種開發方式主要用于模塊化和組建化的開發。比如你常用的彈窗功能,你當然可以把彈窗有關代碼,每次都粘貼復制,然后修改一下就可以用在項目里面了。更好的選擇是把你的彈窗功能代碼,抽象封裝成這樣的一個組件,這樣當你需要用彈窗的時候,只需要傳遞參數生成一個彈窗實例,就可以調用了。

原型對象和原型鏈

在Javascript中,萬物皆對象,但對象也有區別,大致可以分為兩類,即:普通對象(Object)和函數對象(Function)。

一般而言,通過new Function產生的對象是函數對象,其他對象都是普通對象。

舉例說明:

function f1(){  //todo}var f2 = function(){  //todo};var f3 = new Function('x','console.log(x)'); var o1 = {};var o2 = new Object();var o3 = new f1(); console.log(  typeof f1,//function  typeof f2,//function  typeof f3,//function  typeof o1,//object  typeof o2,//object  typeof o3 //object);>> function function function object object object

f1屬于函數的聲明,最常見的函數定義方式,f2實際上是一個匿名函數,把這個匿名函數賦值給了f2,屬于函數表達式,f3不常見,但也是一種函數對象。

Function是JS自帶的對象,f1,f2在創建的時候,JS會自動通過new Function()的方式來構建這些對象,因此,這三個對象都是通過new Function()創建的。

在Javascript中創建對象有兩種方式:對象字面量和使用new表達式,o1和o2的創建恰好對應了這兩種方式,重點講一下o3, 如果用Java和C#的思路來理解的話,o3是f1的實例對象,o3和f1是同一類型,至少我以前這么認為,其實不然…

那么怎么理解呢? 很簡單,看o3是不是通過new Function產生的, 顯然不是,既然不是函數對象,那就是普通對象 。

通過對函數對象和普通對象的簡單理解之后,我們再來了解一下Javascript中的原型和原型鏈:

在JS中,每當創建一個函數對象f1 時,該對象中都會內置一些屬性,其中包括prototype和__proto__,  prototype即原型對象,它記錄著f1的一些屬性和方法。

需要注意的是,prototype 對f1是不可見的,也就是說,f1不會查找prototype中的屬性和方法。

function f(){}f.prototype.foo = "abc";console.log(f.foo); //undefined

那么,prototype有什么用呢? 其實prototype的主要作用就是繼承。 通俗一點講,prototype中定義的屬性和方法都是留給自己的“后代”用的,因此,子類完全可以訪問prototype中的屬性和方法。

想要知道f1是如何把prototype留給“后代”,我們需要了解一下JS中的原型鏈,此時,JS中的 __proto__ 入場了,這哥們長的很奇特,隱藏的也很深,以致于你經常見不到它,但它在普通對象和函數對象中都存在, 它的作用就是保存父類的prototype對象,JS在通過new 表達式創建一個對象的時候,通常會把父類的prototype賦值給新對象的__proto__屬性,這樣,就形成了一代代傳承…

function f(){}f.prototype.foo = "abc";var obj = new f();console.log(obj.foo); //abc

現在我們知道,obj中__proto__保存的是f的prototype, 那么f的prototype中的__proto__中保存的是什么呢? 看下圖:

201659150626664.png (515×122)

如圖所示,f.prototype的__proto__中保存的是Object.prototype,Object.prototype對象中也有__proto__,而從輸出結果看,Object.prototype.__proto__ 是null,表示obj對象原型鏈的終結。如下圖所示:

201659150641500.png (607×148)

obj對象擁有這樣一個原型鏈以后,當obj.foo執行時,obj會先查找自身是否有該屬性,但不會查找自己的prototype,當找不到foo時,obj就沿著原型鏈依次去查找…

在上面的例子中,我們在f的prototype上定義了foo屬性,這時obj就會在原型鏈上找到這個屬性并執行。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
综合国产在线观看| 日韩精品在线免费| 久久久亚洲欧洲日产国码aⅴ| 国产中文字幕日韩| 日韩成人高清在线| 成人黄色激情网| 一区二区三区视频免费| 国产又爽又黄的激情精品视频| 91免费人成网站在线观看18| 日韩av在线免费| 国产精品久久久av| 国产免费亚洲高清| 久久夜色撩人精品| 久久不射热爱视频精品| 国产精品毛片a∨一区二区三区|国| 亚洲免费伊人电影在线观看av| 欧美精品久久久久a| 亚洲精品一区二区三区婷婷月| 福利二区91精品bt7086| 欧美日韩中国免费专区在线看| 久久夜色撩人精品| 中文日韩电影网站| 亚洲一区二区自拍| 欧美理论片在线观看| 欧美做爰性生交视频| 国语自产精品视频在免费| 久久国产天堂福利天堂| 亚洲精品视频免费在线观看| 国产精品高潮视频| 久久亚洲精品小早川怜子66| 91久久精品视频| 欧美性xxxx极品hd满灌| 丰满岳妇乱一区二区三区| 26uuu另类亚洲欧美日本一| 亚洲最新av在线| 日本午夜在线亚洲.国产| 亚洲精品在线不卡| 久久久久99精品久久久久| 久久中文字幕一区| 精品露脸国产偷人在视频| 国产日韩在线看| 97精品国产aⅴ7777| 国产精品小说在线| 在线精品视频视频中文字幕| 欧美成人精品在线观看| 亚洲美女视频网站| 97精品国产aⅴ7777| 18久久久久久| 日韩av在线直播| 日韩欧美在线中文字幕| 国产丝袜高跟一区| 国产精品久久久亚洲| 精品国内亚洲在观看18黄| 久久久久免费视频| 爱福利视频一区| 国产在线999| 国产97免费视| 97超级碰碰碰久久久| 国产精品精品久久久| 日韩在线中文字幕| 亚洲欧美在线免费观看| 国产一区二区欧美日韩| 日韩美女免费线视频| 青青在线视频一区二区三区| 亚洲成色777777在线观看影院| 国产精品夜色7777狼人| 国产精品嫩草影院一区二区| 久久999免费视频| 欧美国产一区二区三区| zzijzzij亚洲日本成熟少妇| 欧美日在线观看| 亚洲无限乱码一二三四麻| 国产精品1区2区在线观看| xxx成人少妇69| 一级做a爰片久久毛片美女图片| 亚洲一区二区三区成人在线视频精品| 一区二区三区视频在线| 国内精品一区二区三区| 国产美女精彩久久| 奇米成人av国产一区二区三区| 91色p视频在线| 亚洲毛茸茸少妇高潮呻吟| 日韩激情av在线免费观看| 亚洲老头同性xxxxx| 国产精品九九九| 一本大道久久加勒比香蕉| 亚洲国产成人久久| 亚洲精品国产suv| 亚洲欧洲日本专区| 日韩免费av片在线观看| 91青草视频久久| 精品国产一区二区三区在线观看| 伊人久久久久久久久久| 欧美最猛性xxxx| 亚洲欧洲午夜一线一品| 黄色成人在线播放| 日本aⅴ大伊香蕉精品视频| 亚洲二区中文字幕| 欧美在线观看www| 欧美精品免费播放| 欧美诱惑福利视频| 欧美综合国产精品久久丁香| 亚洲三级免费看| 成人免费视频a| 久久亚洲精品成人| 日本在线观看天堂男亚洲| 日韩欧美中文第一页| 久久天天躁日日躁| 亚洲三级av在线| 国产精品久久中文| 欧美精品videofree1080p| 3344国产精品免费看| 一区二区国产精品视频| 欧美一区三区三区高中清蜜桃| 欧美午夜www高清视频| 日韩男女性生活视频| 欧美在线视频网| 国产精品无av码在线观看| 日韩精品极品在线观看播放免费视频| 成人国产精品免费视频| 精品国产一区二区三区久久| 亚洲第一视频网站| 性欧美办公室18xxxxhd| 岛国av一区二区在线在线观看| 色噜噜久久综合伊人一本| 国产日韩综合一区二区性色av| 最近2019年手机中文字幕| 欧美精品18videos性欧| 久久99久久久久久久噜噜| 色吧影院999| 亚洲国产中文字幕久久网| 国内精品久久久久| 一区二区三区www| 久久99久久99精品中文字幕| 久久国产精品影视| 国产成人综合精品| 91最新在线免费观看| 亚洲国产精品久久| 亚洲欧美精品一区二区| www.日韩不卡电影av| 精品在线小视频| 亚洲美女自拍视频| 另类视频在线观看| 久久99精品国产99久久6尤物| 国产精品免费网站| 欧美在线视频免费观看| 日韩av在线网| 最新69国产成人精品视频免费| 色一情一乱一区二区| 欧美老少做受xxxx高潮| 秋霞成人午夜鲁丝一区二区三区| 日韩在线高清视频| 欧美成人中文字幕在线| 色播久久人人爽人人爽人人片视av| 在线成人免费网站| 欧美日本中文字幕| www.久久久久| 国产日韩精品综合网站| 中文字幕九色91在线| 久久国产精品视频| 久久天天躁狠狠躁夜夜爽蜜月| 中文字幕亚洲欧美日韩在线不卡| 亚洲男人第一av网站| 欧美亚洲一级片|