Web開發——JavaScript基礎(繼承)
當前參考學習《JavaScript語言精粹》
在基於類的語言中,對象是類的實例,並且類可以從另一個類繼承。JavaScript是一門基於原型的語言,這意味著對象直接從其它對象繼承。
1、偽類
當一個函數對象被創建時,Function構造器產生的函數對象會運行類似這樣的一些代碼:
1 this.prototype = {constructor: this};
新函數對象被賦予一個prototype屬性,其值是包含一個constructor屬性且屬性值為該新函數對象。該prototype對象是存放繼承特征的地方。因為JavaScript語言沒有提供一種方法確定哪個函數式打算用來作構造器的,所以每個函數都會得到一個prototype對象。constructor屬性沒什麽用。重要的是prototype對象。
我們可以定義一個構造器並擴充它的原型:
1 var Mammal = function (name) { 2 this.name = name; 3 }; 4 Mammal.prototype.get_name = function () { 5 return this.name; 6 }; 7 Mammal.prototype.says = function () { 8 return this.saying || ‘‘; 9 }; 10 11 // 現在可以構造一個實例 12 var myMammal = new Mammal("Herb the Mammal");13 var name = myMammal.get_name(); 14 console.log(name);
輸出結果:Herb the Mammal
我們可以構造另一個偽類來繼承Mammel,這是通過定義它的constructor函數並替換它的prototype為一個Mammal的實例來實現的:
1 var Mammal = function (name) { 2 this.name = name; 3 }; 4 Mammal.prototype.get_name = function () { 5 return this.name; 6 };7 Mammal.prototype.says = function () { 8 return this.saying || ‘‘; 9 }; 10 11 // // 現在可以構造一個實例 12 // var myMammal = new Mammal("Herb the Mammal"); 13 // var name = myMammal.get_name(); 14 // console.log(name); 15 16 var Cat = function (name) { 17 this.name = name; 18 this.saying = ‘meow‘; 19 }; 20 // 替換Cat.prototype為一個新的Mammal實例 21 Cat.prototype = new Mammal(); 22 // 擴充新原型對象,增加purr和get_name方法 23 Cat.prototype.purr = function (n) { 24 var i, s = ‘‘; 25 for (i = 0; i < n; i++) { 26 if (s) { 27 s += ‘-‘; 28 } 29 s += ‘r‘; 30 } 31 return s; 32 }; 33 Cat.prototype.get_name = function () { 34 return this.says() + ‘ ‘ + this.name + ‘ ‘ + this.says(); 35 }; 36 37 var myCat = new Cat(‘Henrietta‘); 38 var says = myCat.says(); 39 console.log(says); // ‘meow‘ 40 var purr = myCat.purr(5); 41 console.log(purr); // r-r-r-r-r 42 var name = myCat.get_name(); 43 console.log(name); // ‘meow Henrietta meow‘
輸出結果:
1 r-r-r-r-r 2 meow Henrietta meow
偽類模式本意是想向面向對象靠攏,但它看起來格格不入。我們可以隱藏一些醜陋的細節,這是通過使用method方法定義一個inherits方法來實現的。
2、對象說明符(屬性特性)
有時候,構造器要接受一大串的參數。這可能是令人煩惱的,因為要記住參數的順序可能非常困難。在這種情況下,如果我們在編寫構造器時使其接受一個簡單的對象說明符可能會更加友好。那個對象包含了將要構建的對象規格說明。所以,語氣這樣寫:
1 var myObject = maker(f, l, m, c, s);
不如這麽寫:
1 var myObject = maker({ 2 first: f, 3 last: l, 4 state: s, 5 city: c 6 });
現在多個參數可以任意順序排列,如果構造器會聰明地使用默認值,一些參數可以忽略掉,並且代碼也更容易閱讀。
舉例說明(參考https://www.cnblogs.com/qiutianlaile/p/7829496.html):
先創建一個對象:
1 var person = { 2 name: "Nicholas", 3 _job: "Software Engineer", 4 sayName: function () { 5 console.log(this.name); 6 }, 7 get job() { 8 return this._job; 9 }, 10 set job(newJob) { 11 this._job = newJob; 12 } 13 };
在這個對象中,我們定義了一個name屬性和一個_job屬性;至於set和get開頭的兩處代碼,它們共同定義了一個屬性job。明顯屬性job和_job、name的屬性是不同的。是的,JavaScript中的對象由兩種不同類型的屬性:數據屬性和訪問器屬性。name和_job是數據屬性,job是訪問器屬性數據屬性和訪問器屬性最大的不同在於:當訪問一個訪問器屬性時,得到get後面函數的返回值;給訪問器屬性賦值時,執行的是set後面的函數,這個函數以賦的值為參數:
1 console.log(person.name); // 輸出Nicholas 2 console.log(person._job); // "Software Engineer" 3 console.log(person.job); // "Software Engineer" 4 person.job = ‘Coder‘; 5 console.log(person.job); // Coder 6 console.log(person._job); // Coder
上述代碼中,在set函數中我們通過this改變了_job的值(this指向person這個對象)。
我們在創建person對象時沒有為它的屬性們直接指定特征值,JavaScript自動為它們創建了屬性特性。在ES3中屬性特性不可訪問,但是ES5中屬性的特性可以通過Object.getOwnPropertyDescriptors或Object.getOwnPropertyDescriptor得到:
1 var descriptors= Object.getOwnPropertyDescriptors(person); 2 console.log(descriptors);
輸出結果如下(descriptors的屬性):
1 { name: 2 { value: ‘Nicholas‘, 3 writable: true, 4 enumerable: true, 5 configurable: true }, 6 _job: 7 { value: ‘Coder‘, 8 writable: true, 9 enumerable: true, 10 configurable: true }, 11 sayName: 12 { value: [Function: sayName], 13 writable: true, 14 enumerable: true, 15 configurable: true }, 16 job: 17 { get: [Function: get job], 18 set: [Function: set job], 19 enumerable: true, 20 configurable: true } }
數據屬性
數據屬性的描述符的有四個屬性分別是:
- value:包含這個屬性的數據值。讀取屬性的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。默認為undefined。
- writable:表示能否修改屬性的值。是一個bool值,默認為true。
- enumerable:屬性是否可枚舉,即能否通過for-in循環返回屬性。是一個bool值,默認為true。
- configrable:屬性是否可配置。即屬性能否通過delete刪除,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。是一個bool值,默認為true。
我們最初開始創建的person對象的屬性name,它的value為“Nicholas”,其他描述符為true。
訪問器屬性
訪問器屬性的描述符的有四個屬性分別是:
- get:在讀取屬性時調用的函數。默認值為 undefined。
- set:在寫入屬性時調用的函數。默認值為 undefined。
- enumerable:屬性是否可枚舉,即能否通過for-in循環返回屬性。是一個bool值,默認為true。
- configrable:屬性是否可配置。即屬性能否通過delete刪除,能否修改屬性的特性,或者能否把屬性修改為數據屬性。是一個bool值,默認為true。
我們最初開始創建的person對象的屬性job,它的get和set值分別是我們指定的函數,其他描述符為true。
要修改屬性默認的特性,必須使用 ECMAScript 5 的 Object.defineProperty或 Object.defineProperties。
有了定義屬性特性的方法,那我們通過代碼來探索下這些屬性特性的作用:
1 var person ={}; 2 // 除了configrable之外,其他三個屬性相互之間不會影響,讀者可以自己測試 3 4 console.log(‘---------------------1 writable start---------------------‘); 5 Object.defineProperty(person, "name", { 6 writable: false, 7 enumerable: true, 8 configurable: true, 9 value: "Nicholas" 10 }); 11 12 console.log("1.1 " + person.name); // "Nicholas" 13 person.name = "Greg"; 14 // writable為false,屬性不可修改 15 console.log("1.2 " + person.name); // "Nicholas" 16 17 // writable為false,但configrable為true,我們可以重新配置屬性描述符, 18 Object.defineProperty(person, "name", { 19 writable: false, 20 enumerable: true, 21 configurable: true, 22 value: "John" 23 }); 24 console.log("1.3 " + person.name) // John 25 26 delete person.name 27 //但configrable為true,屬性可以被刪除 28 console.log("1.4 " + person.name) // undefined 29 console.log(‘---------------------1 writable end---------------------\n‘); 30 31 console.log(‘---------------------2 enumerable start---------------------‘); 32 var person = {}; 33 Object.defineProperty(person, "name", { 34 writable: false, 35 enumerable: true, 36 configurable: true, 37 value: "Nicholas" 38 }); 39 // enumerable為true屬性可枚舉 40 for (var prop in person) { 41 console.log("2.1 " + prop); // name 42 } 43 44 Object.defineProperty(person, "name", { 45 writable: false, 46 enumerable: false, 47 configurable: true, 48 value: "Nicholas" 49 }); 50 // enumerable為false屬性不可枚舉,循環體不執行 51 for (var prop in person) { 52 console.log("2.2 " + prop) // 53 } 54 console.log(‘---------------------2 enumerable end---------------------\n‘); 55 56 console.log(‘---------------------3 configurable start---------------------‘); 57 var person = {}; 58 Object.defineProperty(person, "name", { 59 writable: true, 60 enumerable: true, 61 configurable: false, 62 value: "Nicholas" 63 }); 64 // configurable為false,writable為true,屬性仍然可修改 65 person.name = "John" 66 console.log("3.1 " + person.name); // John 67 68 // configurable為false,writable為true,仍然可以通過配置的方式改變屬性值 69 Object.defineProperty(person, "name", { 70 writable: true, 71 enumerable: true, 72 configurable: false, 73 value: "Nicholas" 74 }); 75 console.log("3.2 " + person.name) // Nicholas 76 77 // configurable為false,enumerable為ture,屬性可枚舉 78 for (var prop in person) { 79 console.log("3.3 " + prop) // name 80 } 81 82 // configurable為false,我們仍然可以把writable屬性由true改為false 83 Object.defineProperty(person, "name", { 84 writable: false, 85 enumerable: true, 86 configurable: false, 87 value: "Nicholas" 88 }); 89 console.log("3.4 " + Object.getOwnPropertyDescriptor(person, "name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} 90 91 // configurable為false,writable為false,不能通過配置改變value的值 92 try{ 93 Object.defineProperty(person, "name", { 94 writable: false, 95 enumerable: true, 96 configurable: false, 97 value: "John" 98 }); 99 } catch(error) { 100 console.log("3.5 " + "value change error"); 101 console.log(Object.getOwnPropertyDescriptor(person, "name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} 102 } 103 104 // configurable為false,但是不能把writable屬性由false改為true 105 try{ 106 Object.defineProperty(person, "name", { 107 writable: true, 108 enumerable: true, 109 configurable: false, 110 value: "Nicholas" 111 }); 112 } catch(error) { 113 console.log("3.6 " + "writable false to true error") 114 console.log(Object.getOwnPropertyDescriptor(person, "name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} 115 } 116 117 // configurable為false,不能改變enumerable的值 118 try{ 119 Object.defineProperty(person, "name", { 120 writable: false, 121 enumerable: false, 122 configurable: false, 123 value: "Nicholas" 124 }); 125 } catch(error) { 126 console.log("3.7 " + "enumerable change error"); 127 console.log(Object.getOwnPropertyDescriptor(person, "name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false} 128 } 129 130 var person = {}; 131 Object.defineProperty(person, "name", { 132 writable: true, 133 enumerable: true, 134 configurable: true, 135 value: "Nicholas" 136 }); 137 138 // configurable為true,可以把數據屬性修改為訪問器屬性 139 try{ 140 Object.defineProperty(person, "name", { 141 142 get: function(){return "Nicholas"}, 143 enumerable: true, 144 configurable: false 145 }); 146 } catch(error) { 147 console.log("3.8 " + "get error"); 148 } 149 console.log("3.9 " + Object.getOwnPropertyDescriptor(person, "name"))//{set: undefined, enumerable: true, configurable: false, get: ?} 150 151 var person = {}; 152 Object.defineProperty(person, "name", { 153 writable: true, 154 enumerable: true, 155 configurable: false, 156 value: "Nicholas" 157 }); 158 // configurable為false,不可以把數據屬性修改為訪問器屬性 159 try{ 160 Object.defineProperty(person, "name", { 161 get: function(){return "Nicholas"}, 162 enumerable: true, 163 configurable: false 164 }); 165 } catch(error) { 166 console.log("3.10 " + "get error"); 167 } 168 console.log("3.11 " + Object.getOwnPropertyDescriptor(person, "name"))//{value: "Nicholas", writable: true, enumerable: true, configurable: false} 169 console.log(‘---------------------3 configurable end---------------------\n‘);
輸出結果:
1 1.3 John 2 1.4 undefined 3 ---------------------1 writable end--------------------- 4 ---------------------2 enumerable start--------------------- 5 2.1 name 6 ---------------------2 enumerable end--------------------- 7 ---------------------3 configurable start--------------------- 8 3.1 John 9 3.2 Nicholas 10 3.3 name 11 { value: ‘Nicholas‘, 12 writable: false, 13 enumerable: true, 14 configurable: false } 15 3.5 value change error 16 { value: ‘Nicholas‘, 17 writable: false, 18 enumerable: true, 19 configurable: false } 20 3.6 writable false to true error 21 { value: ‘Nicholas‘, 22 writable: false, 23 enumerable: true, 24 configurable: false } 25 3.7 enumerable change error 26 { value: ‘Nicholas‘, 27 writable: false, 28 enumerable: true, 29 configurable: false } 30 3.9 [object Object] 31 3.10 get error 32 3.11 [object Object] 33 ---------------------3 configurable end---------------------
Web開發——JavaScript基礎(繼承)