ECMAScript沒有類的概念,所以面向對象與傳統的靜態語言有很大不一樣。java
建立自定義對象簡單的方式有兩種:app
var person1 = new Object(); //建立object實例 var person2 = {}; //對象字面量
而後能夠給對象動態的添加須要的屬性和方法,就能夠得到對象的屬性,調用對象的方法了。函數
對於這兩種建立對象的方式沒有什麼區別。不過對象字面量表示法更受喜歡,由於代碼量少,並且在建立的時候能夠一次性將屬性和方法定義好:this
1 var person2 = { 2 name:'zhangsan', 3 sayName:function(){ 4 alert('my name is' + this.name); 5 } 6 };
可是這仍是有個問題:若是要建立多個person對象,都有name屬性和sayName方法,每建立一個對象都得這麼寫一次,太麻煩了!spa
好在前輩們已經總結出了幾種種建立對象的方式:prototype
1. 工廠模式指針
function createPerson(name){ var o = new Object(); o.name = name; o.sayName = function(){ alert('my name is' + this.name); }; return o; }; var person1 = createPerson('zhangsan'); var person2 = createPerson('lisi'); person1.sayName(); //zhangsan person2.sayName(); //lisi
感受跟調用java方法差很少。雖然比剛開始那種方式好點,但也是優缺點並存:code
優勢:解決了建立類似對象的問題,減小代碼量。對象
缺點:沒法知道一個對象的類型,所有都是Object。blog
2. 構造函數模式
function Person(name, age){ this.name = name; this.age = age; this.sayName = function(){ alert('hello:' + name + ',' + age); } } var person1 = new Person('zhangsan',20); var person2 = new Person('lisi', 30); person1.sayName(); person2.sayName(); console.log(person1 instanceof Person); //true console.log(person2 instanceof Person); //true console.log(person1 instanceof Object); //true console.log(person2 instanceof Object); //true
Person函數的特色:
1.沒有顯式的建立對象,即沒用new 關鍵字。
2.屬性都賦值了給了this對象。
3.沒有return.
要建立實例,必須用new關鍵字。new作了如下四件事情:
1.建立一個新的對象,此時對象就是個空對象。
2.將構造函數的做用域賦值給新對象,此時this就指向剛纔那個空對象了。
3.執行Person函數,其實就是在給那個新對象一個一個地動態添加屬性。
4.將添加完屬性的這個新對象返回,因此person1,person2就指向新對象了。
建立出來的示例既是Person的實例,也是Object的實例。
構造函數終歸函數函數,能夠被看成普通函數調用,不過this對象指向全局的Global對象(window對象),以後就能夠經過window對象來調用了。
//做爲普通函數調用 Person('wangwu',30); //屬性和方法都會被添加給全局的window對象 window.sayName(); //wangwu
構造函數也能夠在另外一個對象的做用域中調用:
//在另外一個對象的做用域中調用 var obj= new Object(); var obj1 = new Object(); Person.call(obj, 'zhaoliu',40); Person.apply(obj1,['zhaoqi',50]); obj.sayHello(); obj1.sayHello();
使用構造函數的方法會有什麼問題呢?它將全部的對象的屬性和方法都建立了一遍,每一個對象的方法都是一個Function實例。
obj.sayHello == obj1.sayHello; //false
結果是false就論證了這一點。
按道理說屬性應該是私有的,方法應該是共享的纔對。爲了解決這個問題,原型模式該登場了。
3. 原型模式
什麼是原型?
js中每一個函數都有prototype(原型)屬性,它是一個對象類型的指針。而這個對象包含特定類型的全部實例的共享屬性和方法。
既然每一個函數都有prototype屬性,並且仍是對象類型的,那麼就能夠這麼使用了:
function Person(){ } Person.prototype.name = 'zhangsan'; Person.prototype.age = 20; Person.prototype.sayHello = function(){ alert('name:' + this.name + ',age:' + this.age); }; var person1 = new Person(); person1.sayHello();
Person函數定義並無什麼內容,根據new關鍵字的特性,因此其實person1實例本身也沒什麼內容的,這些屬性和方法都是從原型中尋找的。
原型有如下幾個特徵:
1.全部的原型對象(prototype指向的這個對象)都有一個constructor屬性,這個屬性指向 含有prototype屬性 所在函數。Person.prototype表示Person函數的原型對 象,而Person函數有還有prototype這個屬性,因此Person.prototype.constructor===Person 成立。
2.當爲對象實例添加一個屬性時,這個屬性會屏蔽原型對象中保存的同名屬性,這個屬性只是該實例特有的。
3.代碼讀取某個實例的屬性時,先從實例自己搜索,若是找不到,就搜索對應的原型對象。
4.原型具備動態性,每次對原型對象的修改都能當即從實例上反應出來。
第四點能夠用代碼驗證:
function Person(){ } var person1 = new Person(); //調用構造函數,該實例會有一個指向最初原型對象的指針 Person.prototype.sayHello = function(){ alert('hello'); } person1.sayHello(); //ok Person.prototype = { //修改原型對象的指向 constructor:Person, //重寫原型對象會使得constructor指向Objec構造函數,所以要動態指定 name:'lisi', age:30, sayName:function(){ alert('name:' + this.name); } }; //person1.sayName(); //Error //由於person1的原型指針仍然指向最初的原型對象,她沒有sayName屬性 var person2 = new Person(); person2.sayName(); //ok //person2是從新new出來的,它的原型指針指向的是新的原型對象
重寫原型對象切斷了現有原型與任何以前已經存在對象實例之間的關係,之前的實例仍然引用的是最初的原型。
原型對象的問題:
對全部實例共享,若是原型對象的屬是引用類型的屬性,若是某一實例修改了該屬性,則也會影響其餘實例:
解決辦法:
結合構造函數和原型,屬性私有,方法公有。
寄生式構造感受就是結合和工廠和構造函數模式,new出來的對象還不用,返回一個本身創造的對象。我的感受更接近於工廠模式。
4. 穩妥構造
特徵:不用new操做符調用構造函數;建立對象的方法中沒有this。
function Person(name, age, job){ var o = new Object(); o.sayName = function(){ alert(name); } //構造函數傳入的值沒法修改,只能訪問 return o; } var person = new Person('zhangsan', '20', 'programer'); person.sayName();
這樣傳入構造函數的值就不修改,由於它是傳入的參數,並無屬性把這個值存儲起來。