通常來說,javascript中的對象就是一個指向prototype的指針和一個自身的屬性列表。javascript創建對象時採用了寫時複製的理念。 只有構造器才具有prototype屬性,原型鏈繼承就是創建一個新的指針,指向構造器的prototype屬性。 prototype屬性之所以特別,是因為 ...
通常來說,javascript中的對象就是一個指向prototype的指針和一個自身的屬性列表。javascript創建對象時採用了寫時複製的理念。 只有構造器才具有prototype屬性,原型鏈繼承就是創建一個新的指針,指向構造器的prototype屬性。 prototype屬性之所以特別,是因為javascript時讀取屬性時的遍歷機制決定的。本質上它就是一個普通的指針。 構造器包括:
1.Object 2.Function 3.Array 4.Date 5.String下麵我們來舉一些例子吧
- //每個function都有一個預設的屬性prototype,而這個prototype的constructor預設指向這個函數
- //註意Person.constructor 不等於 Person.prototype.constructor. Function實例自帶constructor屬性
- functionPerson(name){
- this.name = name;
- };
- Person.prototype.getName =function(){
- returnthis.name;
- };
- var p =newPerson("ZhangSan");
- console.log(Person.prototype.constructor===Person);// true
- console.log(p.constructor===Person);// true ,這是因為p本身不包含constructor屬性,所以這裡其實調用的是Person.prototype.constructor
我們的目的是要表示
1.表明Person繼承自Animal 2. 表明p2是Person的實例我們修改一下prototype屬性的指向,讓Person能獲取Animal中的prototype屬性中的方法。也就是Person繼承自Animal(人是野獸)
- functionPerson(name){
- this.name = name;
- };
- Person.prototype.getName =function(){
- returnthis.name;
- };
- var p1 =newPerson("ZhangSan");
- console.log(p.constructor===Person);// true
- console.log(Person.prototype.constructor===Person);// true
- functionAnimal(){}
- Person.prototype =newAnimal();//之所以不採用Person.prototype = Animal.prototype,是因為new 還有其他功能,最後總結。
- var p2 =newPerson("ZhangSan");
- //(p2 -> Person.prototype -> Animal.prototype, 所以p2.constructor其實就是Animal.prototype.constructor)
- console.log(p2.constructor===Person);// 輸出為false ,但我們的本意是要這裡為true的,表明p2是Person的實例。此時目的1達到了,目的2沒達到。
但如果我們這麼修正
Person.prototype = new Animal(); Person.prototype.constructor = Person;這時p2.consturctor是對了,指向的是Person,表示p2是Person類的實例,但是新問題出現了。此時目的2達到了,目的1沒達到。 目的1和目的2此時互相矛盾,是因為此時prototype表達了矛盾的兩個意思,
1表示父類是誰 2作為自己實例的原型來複制因此我們不能直接使用prototype屬性來表示父類是誰,而是用getPrototypeOf()方法來知道父類是誰。
- Person.prototype =newAnimal();
- Person.prototype.constructor=Person;
- var p2 =newPerson("ZhangSan");
- p2.constructor//顯示 function Person() {}
- Object.getPrototypeOf(Person.prototype).constructor//顯示 function Animal() {}
就把這兩個概念給分開了 ,其實通過使用 hasOwnProperty()方法,什麼時候訪問的是實例屬性,什麼時候訪問的是原型屬性就一清二楚了
new做了哪些事情?
當代碼var p = new Person()執行時,new 做瞭如下幾件事情:
創建一個空白對象
創建一個指向Person.prototype的指針
將這個對象通過this關鍵字傳遞到構造函數中並執行構造函數。
具體點來說,在下麵這段代碼中,
- Person.prototype.getName =function(){}
- var person =newPerson();
- 其實類似於
- var person =newObject();
- person.getName =Person.prototype.getName;
因此通過person.getName()調用方法時,this指向的是這個新創建的對象,而不是prototype對象。
這其實在給現有函數加上新功能的情況下會用到,我們可以這麼擴展現有的方法:
- //function myFunc 的寫法基本上等於 var myFunc = new Function();
- function myFunc (){
- }
- myFunc =function(func){
- //可以在這裡做點其他事情
- returnfunction(){
- //可以在這裡做點其他事情
- return func.apply(this,arguments);
- }
- }(myFunc)
也可以在Function.prototype方法里直接通過this來訪問上面代碼的myFunc所指向的對象
- function myFunc (){
- }
- if(!Function.prototype.extend){
- Function.prototype.extend =function(){
- var func =this;
- returnfunction(){
- func.apply(this,arguments);
- }
- }
- };
- var myFunc = myFunc.extend();
總結一下
如果採用Person.prototype = Animal.prototype來表示Person繼承自Animal, instanceof方法也同樣會顯示p也是Animal的實例,返回為true
.
之所以不採用此方法,是因為下麵兩個原因:
1.new 創建了一個新對象,這樣就避免了設置Person.prototype.constructor = Person 的時候也會導致Animal.prototype.constructor的值變為Person,而是動態給這個新創建的對象一個constructor實例屬性。這樣實例上的屬性constructor就覆蓋了Animal.prototype.constructor,這樣Person.prototype.constructor和Animal.prototype.contructor就分開了。
2.Animal自身的this對象的屬性沒辦法傳遞給Person
但是像下麵這樣直接調用構造函數又可能失敗,或者產生其他影響。
- Person.prototype =newAnimal();
為了避免這種情況,所以我們引入了一個中間函數。所以正確的做法應該是
- Person.prototype =(funtion(){
- function F(){};
- F.prototype =Animal.prototype
- returnnew F();
- })()