044_定义类或对象
1. 純構(gòu)造函數(shù)方式
1.1. 使用構(gòu)造函數(shù)創(chuàng)建對象, 第一步選擇類名, 即構(gòu)造函數(shù)的名字。根據(jù)慣例, 這個(gè)名字的首字母大寫, 以使它與首字母通常是小寫的函數(shù)名分開。請考慮下面的例子:
function Car(color, doors, mpg) {this.color = color;this.doors = doors;this.mpg = mpg;this.showColor = function() {alert(this.color);}; }var car1 = new Car("red", 4, 23); var car2 = new Car("blue", 3, 25);1.2. 上例使用純構(gòu)造函數(shù)方式創(chuàng)建對象的問題: 構(gòu)造函數(shù)會重復(fù)生成函數(shù), 為每個(gè)對象都創(chuàng)建獨(dú)立的函數(shù)版本。
2. 純原型方式
2.1. 該方式利用了對象的prototype屬性, 可以把它看成創(chuàng)建新對象所依賴的原型。
2.2. 這里, 首先用空構(gòu)造函數(shù)來設(shè)置類名。然后所有的屬性和方法都被直接賦予prototype屬性。我們重寫了前面的例子, 代碼如下:
function Car() {}Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.showColor = function() {alert(this.color); };var car1 = new Car(); var car2 = new Car();2.3. 原型方式的問題
2.3.1. 首先, 這個(gè)構(gòu)造函數(shù)沒有參數(shù)。使用原型方式, 不能通過給構(gòu)造函數(shù)傳遞參數(shù)來初始化屬性的值, 因此car1和car2的color屬性都等于"blue", doors屬性都等于4, mpg屬性都等于 25。這意味著必須在對象創(chuàng)建后才能改變屬性的默認(rèn)值, 這點(diǎn)很令人討厭, 但還沒完。真正的問題出現(xiàn)在屬性指向的是對象, 而不是函數(shù)時(shí)。函數(shù)共享不會造成問題, 但對象卻很少被多個(gè)實(shí)例共享。請思考下面的例子:
function Car() {}Car.prototype.color = "blue"; Car.prototype.doors = 4; Car.prototype.mpg = 25; Car.prototype.drivers = new Array("Mike", "John"); Car.prototype.showColor = function() {alert(this.color); };var car1 = new Car(); var car2 = new Car();car1.drivers.push("Bill");alert(car1.drivers); // 輸出"Mike, John, Bill" alert(car2.drivers); // 輸出"Mike, John, Bill"2.3.2. 上面的代碼中, 屬性drivers是指向Array對象的指針, 該數(shù)組中包含兩個(gè)名字"Mike"和"John"。由于drivers是引用值, Car的兩個(gè)實(shí)例都指向同一個(gè)數(shù)組。這意味著給car1.drivers添加值"Bill", 在car2.drivers 中也能看到。輸出這兩個(gè)指針中的任何一個(gè), 結(jié)果都是顯示字符串"Mike, John, Bill"。
3. 混合的構(gòu)造函數(shù)和原型方式
3.1. 聯(lián)合使用構(gòu)造函數(shù)和原型方式, 就可像用其他程序設(shè)計(jì)語言一樣創(chuàng)建對象。這種概念非常簡單, 即用構(gòu)造函數(shù)定義對象的所有非函數(shù)屬性, 用原型方式定義對象的函數(shù)屬性(方法)。結(jié)果是, 所有函數(shù)都只創(chuàng)建一次, 而每個(gè)對象都具有自己的對象屬性實(shí)例。
3.2. 我們重寫了前面的例子, 代碼如下:
function Car(color, doors, mpg) {this.color = color;this.doors = doors;this.mpg = mpg;this.drivers = new Array("Mike", "John"); }Car.prototype.showColor = function() {alert(this.color); };var car1 = new Car("red", 4, 23); var car2 = new Car("blue", 3, 25);car1.drivers.push("Bill");alert(car1.drivers); // 輸出"Mike, John, Bill" alert(car2.drivers); // 輸出"Mike, John"3.3. 這種方式是JavaScript采用的主要方式, 它具有其他方式的特性, 卻沒有他們的副作用。
4. 動態(tài)原型方法
4.1. 動態(tài)原型方法的基本想法與混合的構(gòu)造函數(shù)和原型方式相同, 即在構(gòu)造函數(shù)內(nèi)定義非函數(shù)屬性, 而函數(shù)屬性則利用原型屬性定義。唯一的區(qū)別是賦予對象方法的位置。下面是用動態(tài)原型方法重寫的Car類:
function Car(color, doors, mpg) {this.color = color;this.doors = doors;this.mpg = mpg;this.drivers = new Array("Mike", "John");if (typeof Car._initialized == "undefined") {Car.prototype.showColor = function() {alert(this.color);};Car._initialized = true;} }4.2. 直到檢查typeof Car._initialized是否等于"undefined"之前, 這個(gè)構(gòu)造函數(shù)都未發(fā)生變化。這行代碼是動態(tài)原型方法中最重要的部分。如果這個(gè)值未定義, 構(gòu)造函數(shù)將用原型方式繼續(xù)定義對象的方法, 然后把 Car._initialized設(shè)置為true。如果這個(gè)值定義了(它的值為true時(shí), typeof的值為 Boolean), 那么就不再創(chuàng)建該方法。簡而言之, 該方法使用標(biāo)志(_initialized)來判斷是否已給原型賦予了任何方法。該方法只創(chuàng)建并賦值一次, 傳統(tǒng)的OOP開發(fā)者會高興地發(fā)現(xiàn), 這段代碼看起來更像其他語言中的類定義了。
5. 字符串連接
5.1. 對象令人感興趣的一點(diǎn)是用它們解決問題的方式。JavaScript中最常見的一個(gè)問題是字符串連接的性能。與其他語言類似, JavaScript的字符串是不可變的, 即它們的值不能改變。請考慮下面的代碼:
var str = "hello "; str += "world";5.2. 實(shí)際上, 這段代碼在幕后執(zhí)行的步驟如下:
5.2.1. 創(chuàng)建存儲"hello "的字符串。
5.2.2. 創(chuàng)建存儲"world"的字符串。
5.2.3. 創(chuàng)建存儲連接結(jié)果的字符串。
5.2.4. 把str的當(dāng)前內(nèi)容復(fù)制到結(jié)果中。
5.2.5. 把"world"復(fù)制到結(jié)果中。
5.2.6. 更新str, 使它指向結(jié)果。
5.3. 每次完成字符串連接都會執(zhí)行步驟5.2.2到5.2.6, 使得這種操作非常消耗資源。如果重復(fù)這一過程幾百次, 甚至幾千次, 就會造成性能問題。解決方法是用Array對象存儲字符串, 然后用join() 方法(參數(shù)是空字符串)創(chuàng)建最后的字符串。想象用下面的代碼代替前面的代碼:
var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");5.4. 這樣, 無論數(shù)組中引入多少字符串都不成問題, 因?yàn)橹辉谡{(diào)用join()方法時(shí)才會發(fā)生連接操作。此時(shí), 執(zhí)行的步驟如下:
5.4.1. 創(chuàng)建存儲結(jié)果的字符串。
5.4.2. 把每個(gè)字符串復(fù)制到結(jié)果中的合適位置。
5.5. 雖然這種解決方案很好, 但還有更好的方法。問題是, 這段代碼不能確切反映出它的意圖。要使它更容易理解, 可以用StringBuffer類打包該功能:
function StringBuffer () {this._strings_ = new Array(); }StringBuffer.prototype.append = function(str) {this._strings_.push(str); };StringBuffer.prototype.toString = function() {return this._strings_.join(""); };5.6. 這段代碼首先要注意的是strings屬性, 本意是私有屬性。它只有兩個(gè)方法, 即append()和toString()方法。append()方法有一個(gè)參數(shù), 它把該參數(shù)附加到字符串?dāng)?shù)組中, toString()方法調(diào)用數(shù)組的join 方法, 返回真正連接成的字符串。要用StringBuffer對象連接一組字符串, 可以用下面的代碼:
var buffer = new StringBuffer(); buffer.append("hello "); buffer.append("world"); var result = buffer.toString();5.7. 例子
5.7.1. 代碼
<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="utf-8" /><title>字符串連接</title> </head> <body><script type="text/javascript">function StringBuffer () {this._strings_ = new Array();}StringBuffer.prototype.append = function(str) {this._strings_.push(str);};StringBuffer.prototype.toString = function() {return this._strings_.join("");};var buffer = new StringBuffer();buffer.append('<h1>華為Mate40 RS 5G保時(shí)捷限量版手機(jī)</h1>')buffer.append('<h3>商品介紹</h3>');buffer.append('品牌: 華為(HUAWEI)');buffer.append('<br />');buffer.append('商品編號: 10023728787945');buffer.append(' ');buffer.append('商品毛重: 300.00g');buffer.append(' ');buffer.append('運(yùn)行內(nèi)存: 12GB');buffer.append(' ');buffer.append('機(jī)身存儲: 512GB');buffer.append('<br />');buffer.append('存儲卡: NM存儲卡');buffer.append(' ');buffer.append('攝像頭數(shù)量: 后置五攝');buffer.append(' ');buffer.append('后攝主攝像素: 5000萬像素');buffer.append(' ');buffer.append('前攝主攝像素:1300萬像素');buffer.append('<br />');buffer.append('主屏幕尺寸(英寸): 6.67英寸');buffer.append(' ');buffer.append('分辨率: 全高清FHD+');buffer.append(' ');buffer.append('充電器: 11V/6A;10V/4A');buffer.append(' ');buffer.append('熱點(diǎn): 5G');buffer.append('<br />');buffer.append('操作系統(tǒng): Android(安卓)');buffer.append(' ');buffer.append('價(jià)格: ¥ 19288.00');var result = buffer.toString();document.write(result);</script> </body> </html>5.7.2. 效果圖
總結(jié)
以上是生活随笔為你收集整理的044_定义类或对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。