該文為閱讀高級程式設計(第三本)p144-p164的理解與總結! 接受指導與批評。 對於我,我一直是使用字面量的方式創建對象,然而體繫上的創建對象的方法卻並不局限於此。 創建對象的方法 1 工廠模式: 定義工廠函數創建並返回包含特定屬性的對象, 2 構造函數模式: 先貼出代碼 2.1 new Per ...
該文為閱讀高級程式設計(第三本)p144-p164的理解與總結! 接受指導與批評。
對於我,我一直是使用字面量的方式創建對象,然而體繫上的創建對象的方法卻並不局限於此。
創建對象的方法
- 工廠模式
- 構造函數模式
- 原型模式
- 組合使用構造模式和原型模式
- 動態原型模式
- 寄生構造函數模式
- 穩妥的構造函數模式
1 工廠模式:
定義工廠函數創建並返回包含特定屬性的對象,
function createPerson(name, age, job) { var o = new Object(); // var o = {}; //一樣 o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); } return o; } var person0 = createPerson('jody', 22, 'wife');
2 構造函數模式:
先貼出代碼
function Person(name) { this.name = name; this.sayName = function() { console.log(this); } } var person1 = new Person('xxx'); var person2 = new Person('bbb'); console.log(person1)
2.1 new Person() 的發生了什麼?步驟分解:這是重點(new的工作原理)
- 新建一個對象: instance = new Object();
- 設置原型鏈: instance.prototype = Person.prototype
- 任意函數在創建的時候都會創建prototype對象,並自動為它添加constructor屬性
- 讓 Person 中的 this 指向 instance, 然後執行函數:
- 相當於在instance中定義屬性(name,sayName)
- 判斷 Person 函數的返回 :
- Person 中沒有寫return,相當於return undefined(值類型),因此返回instance
- 如果返回值類型,則丟棄該值類型, 然後返回 instance。
- 如果返回引用類型,則丟棄instance, 返回該引用類型。
2.2 構造函數也是函數,
它是可以直接執行的,然後給執行作用域綁定屬性。
任何函數,只要使用new 操作符,這個函數函數就變成了構造函數
2.3 構造函數的優缺點
1.優點:
person1 instanceof Person person2 instanceof Object //都為ture,用於判斷類型(工廠模式不行)
2.缺點:公共屬性(如sayName)在每一個Person實例中都創建了,不能做到復用
3 原型模式
function Person() { }; Person.prototype.sayName = function() { console.log(this.name); } Person.prototype.name = 'miaowwwwww'; var person1 = new Person(); var person2 = new Person(); person1.sayName(); console.log(person1)
3.1 什麼是原型對象??
當創建一個函數的時候,js根據一組特定的規則自動地為函數創建 prototype 屬性 和 一個原型對象。
prototype 屬性是一個指針,指向原型對象。
原型對象自動獲得一個 constructor(構造函數) 指針屬性,該屬性指向 被創建的函數。
3.2 實際應用重點:
使用構造函數創建的每一個實例,都自動包含一個[[prototype]] 指針屬性。(在瀏覽器prototype是隱藏的,一些瀏覽器提供__proto__)。
並且這個屬性指向,構造函數的 prototype (即原型對象, 所以每一個實例共用一個原型對象)
3.3 構造函數-原型-實例的關係如圖:
3.4 註意:
- 原型對象上的可遍歷屬屬性將會出現在 實例的 for-in 遍歷中
- 構造函數 Person.prototype 是一個指針
- 若使用 Person.prototype.sayName = function() {...} 表示在原 原型中添加屬性
- 若使用如下方式:表示 改變了 Person.prototype 的指針, 讓它指向了一個新的對象,該對象的屬性被實例共用,當缺失了constructor屬性
Person.prototype = { constructor: Person, // 可手動添加,補全constructor屬性 name: 'miaowwwww', sayName: function() { console.log(this.name); } }
3.5 原型模式的優缺點
1.優點:
- 完善構造函數模式的缺點,所有實例可以共用原型的方法,而不需要是個實例創建副本,提高復用性
- 類型判斷
person1 instanceOf Person Person.isPrototypeOf(person1) Object.getPrototypeOf(person1) === Person // 都可以正確判斷結果
2.缺點:若原型中使用引用類型,則實例可以修改原型中的
function Person() {}; Person.prototype = { constructor: Person, name: 'miaowwwww', friends: ['jody', 'robin'], sayName: function() {} } var person1 = new Person(); var person2 = new Person(); person1.friends.push('mike'); // person2.friends : ['jody', 'robin', 'mike'];
4. 組合構造函數模式與原型模式
function Bird(name) { this.name = name; this.friends = ['mike', 'jody']; } Bird.prototype = { sayName: function() { console.log(this.name); } }
4.1 優點: 結合構造函數模式與原型模式的優點,把引用類型的屬性,放到構造函數中,為每一個實例創建副本,同時復用原型上的方法
5.動態原型模式
function Cat(name) { this.name = name; this.friends = ['a', 'b'], if(typeof this.sayName !== 'function') { Cat.prototype.sayName = function() { console.log(this.name); } } }
5.1 解讀:就是把第四種模式,變異一下,在判斷實例沒有 sayName 方法的時候才添加 該方法到 原型上
每次新建對象的時候都會執行判斷代碼, 然後只有第一次執行, if 才為true
6.寄生構造函數模式
function Book(name) { var o = new Array(); o.name = name; o.sayName = function() { console.log(this.name); } return o; } var book1 = new Book('javascript');
6.1 請回顧上面說過的 2.1 new 的工作原理
6.2 理解:
- 這種方法除了 return 以及 new Book() 之外,跟工廠函數基本沒有區別。
- 可以理解為對已有對象的增強(如這裡的Array,因為不能直接修改Array對象)
6.3 缺點:
- book1 的原型中不包含 Book, 而是Array, 所以叫寄生
- 與構造函數一樣,每一個實例都有 sayName.. 的副本,復用性降低
7 穩妥的構造函數模式
function Fish(_name){ var o = new Object(); // new Person(); 或其他對象 // 私有變數和函數 var name = _name; function sayHi() { console.log('hi') }; // ... // 添加方法 o.sayName = function() { console.log(name); sayHi(); return name; } o.setName = function(value) { name = value; } return o; } var aa = Fish('miaowwwww'); var bb = Fish('jody'); aa.sayName(); bb.sayName();
7.1 這個竟然是平時一直在用的閉包。
7.2 優點:變數私有化,並且只能通過特定介面訪問
缺點: 又是每一個實例一個函數副本,復用性降低了;