1. 程式人生 > >Web開發——JavaScript基礎(繼承)

Web開發——JavaScript基礎(繼承)

ola mas lse for 實現 func doc 閱讀 基礎

  當前參考學習《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 } }

數據屬性

  數據屬性的描述符的有四個屬性分別是:

  1. value:包含這個屬性的數據值。讀取屬性的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。默認為undefined。
  2. writable:表示能否修改屬性的值。是一個bool值,默認為true。
  3. enumerable:屬性是否可枚舉,即能否通過for-in循環返回屬性。是一個bool值,默認為true。
  4. configrable:屬性是否可配置。即屬性能否通過delete刪除,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。是一個bool值,默認為true。

  我們最初開始創建的person對象的屬性name,它的value為“Nicholas”,其他描述符為true。

訪問器屬性 

  訪問器屬性的描述符的有四個屬性分別是:

  1. get:在讀取屬性時調用的函數。默認值為 undefined。
  2. set:在寫入屬性時調用的函數。默認值為 undefined。
  3. enumerable:屬性是否可枚舉,即能否通過for-in循環返回屬性。是一個bool值,默認為true。
  4. 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基礎(繼承)