標籤:工廠 src 顯示 names 參考型別 round 第一篇 har com
前文
新年第一篇,先說句新年一個星期後快樂,然後再開始我的本文。上個一篇主要說了物件導向中,建立對象的三種模式,原廠模式、建構函式模式還有原型模式,其中比較重要的是原型模式。原型模式中起作用的是原型對象,這次就對原型進行一番深入的學習。
內容
一、in操作符
首先我們來瞭解一下in操作符。通過in操作符能夠訪問到一個對象中的屬性。也就是說,我們可以用過in操作符來查看到底這個對象中是不是有這個屬性,以及確定這個屬性是來自執行個體還是原型。
講一下in操作符的兩種使用方法
1. 單獨使用:通過對象能訪問到屬性是就會返回true,不管是在對象的實力上還是在原型上。
function Person(){}Person.prototype.name = ‘jiang‘;Person.prototype.age = 19;Person.prototype.job = ‘sf‘;Person.prototype.sayName = function(){console.log(this.name);}var p1 = new Person();‘name‘ in p1;//true
通過in操作符檢測到的name屬性,傳回值為true,這說明在p1的執行個體中可以檢測到name屬性,但是我們初始化時並沒有給執行個體一個name屬性,說明這個屬性是來自與原型對象的。
p1.name=‘z‘;‘name‘ in p1;//true
當我們給執行個體添加一個name屬性的時候,此時依舊還是true。但是這個很明顯就知道這是來自執行個體中的屬性。這兩個例子說明,單單只是通過in操作符是無法知道我們要檢測的屬性到底是來自原型對象還是執行個體本身。
p1.hasOwnProperty(‘name‘);//truedelete p1.name;p1.hasOwnProperty(‘name‘);//false
通過調用hasOwnProperty()方法可以知道這個屬性有沒有存在在執行個體本身,但是無法知道這個屬性有沒有存在在原型中。因此可以通過將這兩種方法封裝一起就可以知道到底是來自原型還是執行個體。
function hasPrototypeProperty(obj, name){return !obj.hasOwnProperty(name) && name in obj;}var p2 = new Person();hasPrototypeProperty(p2, ‘name‘);//falsep2.name = ‘z‘;hasPrototypeProperty(p2, ‘name‘);//true
只要當hasOwnProperty()返回false,那麼就可以確定這個屬性是來自原型,如果返回true就來自對象的執行個體。
2.for-in:返回的是可通過對象訪問到的、可枚舉的屬性,包括原型和執行個體中的屬性。屏蔽了原型中不可枚舉屬性的執行個體屬性也可以在for-in迴圈中返回,所有開發人員自訂的屬性都是可枚舉的。
var o = {toString : function(){return "myObject";}}for(var prop in o){if(prop == ‘toString‘){console.log(prop);//在ie中不會顯示}}
注意:ie早期版本存在一個bug,即屏蔽不可枚舉屬性的執行個體屬性不會在for-in迴圈中
3.Object.keys()方法。通過調用這個方法,可以返回對象中包含的所有可枚舉屬性的字串數組
返回了所有原型對象中可枚舉屬性字串
var keys = Object.keys(Person.prototype);console.log(key);//name, age, job, sayName返回了執行個體中的可枚舉屬性var key = Object.keys(p1);console.log(key);//name這裡瞭解一下getOwnPropertyNames()方法var keys1 = Object.getOwnPropertyNames(Person.prototype);console.log(key);//constructor,name, age, job, sayName//這個方法連不可枚舉屬性constructor也顯示出來了
二、原型的動態性
動態性就是說,我們在原型對象上的修改,都可以立即在執行個體中反映出來,即使是先建立了對象的執行個體再修改。
function Person(){} Person.prototype.name = ‘jiang‘; Person.prototype.age = 15; Person.prototype.job = ‘sf‘; Person.prototype.sayName = function(){ console.log(this.name); }; var person = new Person(); Person.prototype.sayHi = function(){ console.log("hi"); };//向原型中添加一個函數 person.sayHi();
在上面的例子中,我們先是在個Person對象的原型上添加了屬性和方法,並進行了執行個體化,然後我們對Person,prototype進行了修改,添加了一個名為sayHi的方法。我們在後面通過執行個體始終還是能夠訪問到這個方法,這是由於原型和執行個體之間的鬆散關係。但是如果我們用另外一種更簡單的原型文法來重構這個對象,那效果就會不一樣了。
var friend = new Person();Person.prototype = {name: ‘jiang‘,age: 14,job: ‘sf‘,sayName: function(){console.log(this.name);}}
這種簡單的原型文法也就是通過字面量的文法來重構,這樣子相當於重新定義了一個新對象,通過重構後,會切斷原型與對象之間的聯絡,原型鏈斷開,原型中的constructor指標不會再指向Person,而是指向了Object。在之前也有說過,當我們聲明一個函數時,prototype對象會同時被建立,而這個對象也會自動獲得constructor屬性,並指向原來的函數,通過上面的方式無異於切斷了兩者之間的聯絡。但是我們可以通過在對象中添加constructor:Person,來重新獲得串連。但是如果我們調用了friend.sayName(),就會報錯,這是因為friend中最初的[[prototype]]已經不再是指向原來的原型對象了。
修改前:
修改後:
儘管重修原型對象切換了現有的原型與已經建立的執行個體之間的關係,但是引用的依舊是最初的原型。
三、原生對象的原型
原生對象有哪些:Object、String、Array等等都是原生對象,這些原生的參考型別都是採用原型模式,在其建構函式的原型上定義了方法,比如Array.prototype可以找到sort()方法。通過原生對象的原型,不僅僅可以擷取預設方法的引用,也可以在其原型上定義方法。但是最好就不要這麼做。
四、原型對象的問題
並不是說原型就是完美的,問題一:忽略參數傳遞初始化參數這一塊,結果所有的執行個體在預設情況下取得的值是相同的。問題二:共用性帶來的問題,也是最大的問題。
function Person(){}Person.prototype ={name : ‘jiang‘,aga : 14,job : ‘sf‘,friend : [‘s‘, ‘f‘],sayName : function(){console.log(this.name);}}var p1 = new Person();var p2 = new Person();p1.friend.push(‘v‘);console.log(p1.friend);//"s","f","v"console.log(p2.friend);//"s","f","v"
由執行個體p1中沒有friend這個屬性,所以搜尋到原型中有同名屬性,所以在p1中添加的值實質上是添加到了原型的屬性中,而執行個體p2同樣沒有這個屬性,所以p1的修改影響到了p2。這當然是我們不願意看到的,當然共用函數我們是願意的,而共用屬性的話那麼就問題就大了去,就好比每個人都有自己隱私,但是因為共用的問題,我的隱私被侵犯了,不就像是被偷窺了一樣嚴重。
夜深人靜了,是時候要就寢了,今晚的內容就這樣吧。晚安~
javascript--物件導向學習(原型的深入學習)