使用new和字面量的的方法是兩種主流創建對象的方法,兩種最終都能達到同樣的實例化的對象,本章主要圍繞new關鍵字來實例化一個對象並且講一個不使用new但是完全與new實例化對象相同的例子。
在使用new后面跟一個構造函數的時候,將會返回一個新對象,
這個對象中的屬性便就是new后面跟的構造函數中的this的屬性,
這個對象的__proto__屬性指向就是new后面的構造函數的prototype屬性。
prototype屬性的值是一個對象,是Javascript為每一個函數內置的一個屬性,也就是說,所有的函數都能成為構造函數,所以不到萬不得已不要覆蓋原來的值,更加不要使用基本類型覆蓋prototype屬性。
new可以總結為下面幾個步驟:
- 創建一個新對象
- 把這個新對象的__proto__指向構造函數的prototype屬性
- 把構造函數中的this上的屬性全部添加到新對象
- 返回這個新對象
通過這四個步驟,返回了一個新實例化對象,牢記這四個步驟,就能輕松的模擬出new的效果,或者通過new模擬字面量的表示法。
假設現在有一個構造函數
//構造函數首字母大寫並不是強烈要求,只是一種書寫的規范 function Fun(){ this.a = 1; this.b = "字符串"; this.arr = [1,2] } //每個函數中都內置了一個prototype屬性 //可以通過console.log(Fun.prototype)打印出來看看 Fun.prototype.c = "prototype"
//不要直接覆蓋prototype,就像下面這個例子 // Fun.prototype = "obj" //但是如果非要覆蓋的話,你可以這樣做 // Fun.prototype = { // constructor:Fun // 把constructor屬性指回構造函數 // 可以達到一樣的效果 // }
執行上面的代碼,接下來只需要 new Fun() 就可以返回一個新對象,並且這個對象里面擁有a,b,arr的屬性,原型鏈__proto__上擁有c這個屬性,它的結構是這樣的
1 //一些只是表示結構。不能執行 2 Fun { 3 4 a:1, 5 b:"字符串", 6 arr:[1,2] 7 __proto__:{ 8 c : "prototype", 9 constructor:function Fun() 10 __proto__: ...... 11 } 12 }
但是,這並不是目的,目的是希望不通過new來得到一個新對象並且跟new出來一樣的結果。
首先執行第一步(返回一個新對象):
1 //通過字面量創建一個新對象 2 var obj = {}
得到這個對象之后把這個對象的__proto__指向prototype
//這里__proto__與prototype的值都是對象 //最終對象的原型鏈都將指向一個帶get,set方法的超級對象 //__proto__是對象的原型鏈屬性 //prototype是構造函數的一個屬性 obj.__proto__ = Fun.prototype
然后需要把構造函數中this的屬性全部拷貝到對象的中,這時可以使用call或在apply方法。
1 //把obj作為第一個參數傳入到call中 2 //這是Fun中的this就變成了obj 3 //Fun通過call執行之后,this中的屬性就被拷貝到了obj中 4 Fun.call(obj) 5 //或在Fun.apply(obj) 6 //兩個方法僅有的區別就是所傳的參數不同。
最后打印一下obj,發現它與new出來的對象幾乎沒有差異
以下是完整代碼
function Fun(){ this.a = 1; this.b = "字符串"; this.arr = [1,2] } Fun.prototype.c = "prototype"; var obj = {}; obj.__proto__ = Fun.prototype; Fun.call(obj) var newObj = new Fun(); console.log(obj); console.log(newObj);