在javascript中,像原型,閉包這樣的概念,只要我們能領悟其中的原理,一切都會顯得格外清晰與明瞭。 原型屬性(prototype): 下麵我們簡單定義一個函數 在這些函數一定義就被賦予的屬性中,就包括prototype屬性,她的初始化是一個空對象: 當然我們也可以自己添加該屬性: 而且我們可以 ...
在javascript中,像原型,閉包這樣的概念,只要我們能領悟其中的原理,一切都會顯得格外清晰與明瞭。
原型屬性(prototype):
下麵我們簡單定義一個函數
function her(a, b){ return a + b; }
在這些函數一定義就被賦予的屬性中,就包括prototype屬性,她的初始化是一個空對象:
typeof her.prototype // Object
當然我們也可以自己添加該屬性:
her.prototype = {};
而且我們可以賦予這個空對象一些屬性和方法,這並不會對foo函數產生神馬影響,以為只有her()函數被當做構造函數來使用的情況下,這些屬性才起作用。
來個例子:
function her(){ this.name = 'Anna', this.child = 'Jok', this.say = function(){ return 'My name is' + this.name + 'My child is' + this.child; } }
上面是一個簡單的構造函數,我們就用構造器屬性prototype屬性來增加她的屬性和方法,您可以這樣:
her.prototype.sex = 'women'; her.prototype.height = '175cm'; her.prototype.doing = function(){ return 'I is a' + this.sex + 'I am height is' + this.height; }
使用原型的屬性和方法:
var she = new her(); she.name; // Anna she.child; // Jok
如上所講如果想用以上的屬性和方法,那必須new一個上述構造函數her()的一個對象實例。
對於原型來說,最重要的一點就是理解她的‘實時性’,由於在javascript中幾乎所有對象都是通過引用的方式傳遞的。因此我們創建出來的新對象並沒有屬於自己的原型副本。這也就意味著我們可以隨時修改prototype的屬性和方法,並且由同一構造函數創建出來所有對象的prototype都會同時改變。 (甚至還會影響到修改之前就已經創建好的對象)。
繼續之前的例子:
her.prototype.eat = 'water'; she.eat; // water
哪怕she之前就被創建了,我仍然還會在這個對象中訪問到eat屬性。
自身屬性與原型屬性
上述的創建doing()方法那個示例,其實直接引用一次her.prototype也可以完成上述工作:
function her(){ this.name = 'Anna', this.child = 'Jok', this.say = function(){ return 'My name is' + this.name + 'My child is' + this.child; }, this.sex = 'women', this.eat = 'water' }
her.prototype.doing = function(){ return 'I is a' + her.prototype.sex + 'I am height is' + her.prototype.height; }
這樣有神馬不同嗎?要想知道這個問題就必須深入瞭解原型的工作原理。
var she = new her();
當我們訪問she對象的某個屬性時,例如she.name時,javascript引擎會遍歷該對象的所有屬性,並查找出name屬性,如果找到了就會直接返回,否則為undefined.
這回我們訪問she.sex會發生神馬呢,javascript引擎依舊會查詢she的所有屬性,但是這回找不到一個叫sex的屬性了,接下來javascript引擎就會去查找用於創建該對象的構造函數的原型(等價於我們直接訪問到she.constructor.prototype),如果在原型中找到了該屬性,就立刻使用該屬性。
這種方式與直接訪問原型屬性是一樣的。每個對象都有屬於自己的構造器屬性,這個屬性引用的就是創建該對象的構造函數,所以:
she.constructor === her; // true she.constructor.prototype.sex; // women
現在,我們回顧一下整個過程。我們知道每個對象都有一個構造器,而原型本身也是一個對象,必然也會有自己的構造器,而這個構造器又會有自己的原型。於是這種結構會一直持續下去,並取決於原型鏈的長度。她們的最後一環必然是Object()內建對象,她是最高級的父及對象(始祖)。
she.toString(); // Object
利用自身屬性重寫原型屬性
通過上述我們知道一個對象自身屬性中沒有找到指定的屬性,前提是這個屬性存在,那麼她就會順著原型鏈去找。但是遇到對象的自身的屬性和原型屬性重名怎麼辦呢?
答案是自身屬性的優先順序要高於原型屬性(自己的利益至上)。
來個梨子:
function her(){ this.name = 'Anna'; } var she = new her(); she.name = 'Lous'; she.name; // Lous
我們可以通過hasOwnProperty()方法來判斷一個屬性是自身屬性還是來自於原型屬性。
she.hasOwnProperty('name'); // true
如果這時候我們把自身屬性刪掉,那麼同名的原型屬性又會浮出水面:
delete she.name; //true
she.name // Anna
she.hasOwnProtype('name') // false
當然我們可以重建這個屬性:
she.name = 'Lous'; she.name; // Lous; she.hasOwnProtype('name'); // true
如何判斷一個屬性到底是原型鏈中那個原型的屬性呢?答案還是用hasOwnProperty()屬性。例如我們想知道toString()這個方法來自與哪裡:
she.hasOwnProperty('toString'); // false she.constructor.hasOwnProperty('toString'); // false she.constructor.prototype.hasOwnProperty('toString'); // false Object.hasOwnProperty('toString'); // false Object.prototype.hasOwnProperty('toString'); // true
呵呵(*Φ皿Φ*)!!
對象的枚舉屬性
如果想要獲得一個對象的所有屬性的列表,那麼就用for-in迴圈吧!(for迴圈適合數組,for-in迴圈更適合對象),來個慄子:
var params = { name : 'Anna', sex : 'women' } var url = 'http://www.baidu.com?', i, query = []; for(i in params){ query.push(i + '=' + params[i]); } url += query.join('&'); // 'http://www.baidu.com?name=Anna&sex:women';
在這裡有幾個細節需要註意:
1. 不適所有的對象屬性都是可以枚舉的,例如length,constructor等就不會被顯示,那些會顯示的屬性被稱為可枚舉的,我們可以通過各個對象所提供的propertyIsEnumerable()方法來判斷對象的某個屬性是不是可枚舉的。
2. 原型鏈中的各個屬性也會被顯示出來,當然她們是被可枚舉的。
來個慄子:
function her(){ this.name = 'Anna'; this.sex = 'women'; this.say = function(){'My name is' + this.name;} } var she = new her(); she.eat = 'water'; for(var i in she){ console.log(i + '=' + she[i]); } name = 'Anna' sex = 'women' say = function(){'My name is' + this.name;} eat = 'water'
我們再來一次,這次輸出自身屬性
for(var i in she){ if(she.hasOwnProperty(i)){ console.log(i +'='+she[i]); } } name = 'Anna' sex = 'women' say = function(){'My name is' + this.name}
未完待續。。。。。。