JS 學習筆記--13---原型

來源:互聯網
上載者:User

練習中使用的是IE10,如果有錯誤之處,還請各位朋友多多指教。本文關於原型難以描述,故多用代碼展示   原型是JS中一個很重要的概念,也是JS中一個痛點,語言上難以描述,原型對象的屬性和方法叫做原型屬性和原型方法,建構函式中的屬性和方法叫做執行個體屬性和執行個體方法,它們的區別就是:對於一個對象的多個執行個體之間,它們的執行個體屬性和執行個體方法是各不一樣的,前面的物件導向中已經證明,而他們的原型屬性和原型方法是一模一樣的,完全相等的 建構函式方式聲明原型對象:                                                                       複製代碼 1 //建構函式定義的成員變數時執行個體成員 2     function Box(user,age){ 3         this.user=user;        //執行個體屬性 4         this.age=age;         5         this.run=function(){    //執行個體方法 6             return this.user+" "+this.age+" "+"運行中..."; 7         } 8     } 9 10 //原型聲明,建構函式中什麼也不寫,然後通過prototype對象來添加屬性和方法,調用都是一樣的11     function Box1(){};12     Box1.prototype.user='abc';    //通過prototype定義的叫做  原型屬性13     Box1.prototype.age=22;        //下面這個方法叫做原型方法14     Box1.prototype.run=function(){ return this.user+" "+this.age+" "+"運行中..."; };15 16     var box=new Box1();17     alert(box.user);    // abc18     alert(box.age);        // 2219     alert(box.run());    // abc 22 運行中...20 21 //聲明了兩個對象   從下面的結果可以知道,這兩個對象的引用不相等,但是他們中間的方法和屬性是完全相等的  包括引用22     var box1=new Box();23     var box2=new Box();24     alert(box1.user);    //abc25     alert(box2.user);    //abc26     alert(box1.age);    //2227     alert(box2.age);    //2228         29     //如果是執行個體方法,不同的執行個體化,他們的方法的引用地址是不一樣的,是唯一的30     //但是原型對象,不同的執行個體化,他們的方法的引用地址也是一樣的,共用的,大家都一樣31     alert(box1.run==box2.run);    // true32     alert(box1==box2);    //false複製代碼        1、  原型的作用主要作用   用來共用一些屬性和方法,我們每建立一個函數,都會自動的建立一個原型對象,原型對象是由函數下面的一個屬性[__proto__]來指向的,這個屬性是一個指標,它指向了原型對象的constructor屬性,我們可以通過這兩個屬性就可以訪問原型對象中的屬性和方法了。   constructor是一個構造屬性,是可以擷取建構函式的本身的,它的作用其實就是被原型指標[__proto__]定位,然後擷取到建構函式的本身 1    alert(Box.prototype);    //存取方法的屬性prototype2     alert(box1.prototype);    //undefined        這個屬性是一個對象,是訪問不到的    3     alert(box1.__proto__);    //object Object      低版本的IE可能列印不出來4    alert(box1.constructor);    //function Box(){}; //構造屬性5     alert(Box.constructor);        // function Function(){...};    Box 本身就是一個Function類型6     //alert(box1.prototype.constructor)    //error      2、isPrototypeOf() 方法   判斷一個執行個體對象是否是指向了該建構函式的原型對象,可以用 isPrototypeOf() 方法來判斷,如果指向了返回為true,沒有則返回為false,一切對象都是繼承自Object對象 1     alert(Box.prototype.isPrototypeOf(box1)); //true    只要是執行個體化的,都指向了原型對象2     alert(Box.prototype.isPrototypeOf(box2)); //true3     alert(Object.prototype.isPrototypeOf(box1));//true  因為一切對象都是 Object 類型的,故指向了4     var box=new Object();5     alert(Box.prototype.isPrototypeOf(box)); // false    box對象是Object類型的對象,Box其實類似於是繼承自Object類型  3、原型模型的執行流程:遵循JS中的就近原則   先尋找建構函式執行個體裡面的方法和屬性,即先尋找執行個體屬性,如果有,立即傳回值或者執行對應的方法   如果執行個體屬性或者執行個體方法中沒有,則去原型對象中尋找相應的屬性和方法,有就返回或執行方法,如果沒有就返回undefined或者報錯 複製代碼1     var box1=new Box();2     var box2=new Box();    3     box1.name='kkk';    //給對象box1添加一個執行個體屬性,4     alert(box1.name);    //訪問的是box1的執行個體屬性,box2是訪問不到的,因為原型屬性中也沒有5     alert(box2.name);6     box1.user='jjj';    //其實是執行個體屬性,並沒有重寫原型屬性的值7     alert(box1.__proto__.user);//abc    訪問原型屬性中的值8     alert(box1.user);//jjj    訪問的是執行個體屬性中的值9     alert(box2.user);//abc    box2 中不存在執行個體屬性 user  就返回的是原型屬性,它訪問不到box1中的執行個體屬性,因為他們之間共用的只是原型屬性和方法複製代碼  4、屬性的刪除和修改  可以通過 delete 關鍵字來刪除執行個體屬性和原型屬性,刪除和修改原型屬性可以通過兩種方式:(1) 通過執行個體對象的指標修改:box1.__proto__.age; (2) 定義原型屬性一樣用建構函式修改 prototype: Box.prototype.age;   建構函式中是改了就生效,不管何時聲明的對象,而在後面的字面量形式中只有在聲明執行個體對象之前該才有效(詳細的見後面字面量形式建立中) 複製代碼1     delete box1.user;    //刪除對象box1中的執行個體屬性2     alert(box1.user);    //abc    因為前面刪除了執行個體屬性中的user屬性,返回的就是原型屬性中的user屬性3     //delete box1.__proto__.age;    //通過這種方式可以刪除原型對象中的屬性,但是別這樣弄,牽一髮而動全身4     //delete Box.prototype.age;    //也是刪除了原型對象中的屬性 age5     alert(box2.age);    //undefined     因為前面刪除了原型屬性中的age屬性6     box1.__proto__.age=33;    //修改了原型屬性中的值7     alert(box2.age);    //338     Box.prototype.age=44;    //修改了原型屬性中的值9     alert(box2.age);    //44複製代碼 5、hasProperty() 方法和 in 操作符   判斷某個對象是否擁有某個執行個體屬性,可以通過 hasOwnProperty() 方法來測試,有就返回true,否則返回false   in 操作符可以判斷對象中是否包含某個屬性,不管這個屬性是原型屬性還是執行個體屬性,包含則返回true   可以通過 hasOwnProperty() 方法和 in 操作符 共同來判斷某個對象是否包含某個原型屬性  複製代碼    var box1 = new Box();    var box2 = new Box();  1    box1.name='jjj'; 2     alert(box1.hasOwnProperty('name'));    //true 3     alert(box2.hasOwnProperty('name'));    //false        box2 並沒有執行個體屬性name 4     alert(box1.hasOwnProperty('user'));    //false        user 屬性是原型屬性,不是執行個體屬性 5  6     alert('name' in box1);    //false 7     alert('user' in box1);    //true    原型屬性 8     box1.name='jjj'; 9     alert('name' in box1);    //true    執行個體屬性10 11 //通過 hasOwnProperty() 方法和 in 操作符 共同來判斷某個對象是否包含某個原型屬性12 function checkPropo(object,element){    //判斷的是在某個對象中的屬性,故要將對象和屬性傳遞過來13         if(!object.hasOwnProperty(element)){    //先判斷是否存在執行個體屬性,如果存在就返回false14             if(element in object){    //如果存在就返回true15                 return true;    16             }else{17                 return false;18             }19         }else{20             return false;21         }22         //上面的代碼可以用一個運算式來表示:return !object.hasOwnProperty(element)&& (element in object);23     }24     25     alert(checkPropo(box1,'name'));    //false26     box1.name='name';27     alert(checkPropo(box1,'name'));    //false        雖然有屬性 name,但是這是一個執行個體屬性28     alert(checkPropo(box1,'user'));    //true        //原型屬性 user 是存在的複製代碼 字面量形式建立原型對象:                                                         複製代碼 1     function Box(){}; 2     Box.prototype={        // 通過字面方法來建立原型屬性和方法,這樣有點封裝的感覺 3         user:'abc', 4         age:123, 5         run:function(){return this.user+" "+this.age+" 運行中...";} 6     } 7  8 //建立兩個對象 9     var box1=new Box();10     var box2=new Box();11     //運行結果是一樣的12     alert(box1.run());    //abc 123 運行中...13     alert(box2.run());    //abc 123 運行中...14     alert(box1.run==box2.run);    //true複製代碼 6、字面量方式建立原型對象注意的問題一:構造屬性的指向   字面量建立的方式 用constructor 屬性指向的是Object對象,而不是執行個體本身,但是建構函式方式建立的這相反;   如果想讓constructor指向執行個體[Box],可以採用強制指向的方式 複製代碼 1     var box1 = new Box(); 2  3     alert(box1.constructor);    //function Object() { [native code] } 4     alert(box1.constructor == Box);        //false 5     alert(box1.constructor == Object);    //true 6     alert(box1 instanceof Box);        //true 7     alert(box1 instanceof Object);    //true 8     alert(Box.constructor);    //function Function... 9     alert(Box.prototype);    //object Object    //使用建構函式名(對象名)訪問prototype10     alert(box1.__proto__);    //object Object    //使用對象執行個體訪問prototype的指標11 12 // 如果想讓constructor指向執行個體[Box],可以採用強制指向的方式13     function Box(){};14     Box.prototype={        15         constructor:Box,    //強制原型對象來指向Box16         user:'abc',17         age:123,18         run:function(){return this.user+" "+this.age+" 運行中...";}19     }20 21     //建立兩個對象22     var box1=new Box();23     var box2=new Box();24 25     //強制constructor指向Box的時候,返回結果如下26     alert(box1.constructor == Box);        //true27     alert(box1.constructor == Object);    //false複製代碼  之所以出現上面的原因是因為:通過Box.prototype={..};方式建立的時候,都建立一個新的對象,而每次建立一個函數,都會同時建立它自己的prototype,那麼這個新的對象也就會自動擷取它自己的constructor屬性,這樣新對象的constructor屬性重寫了Box執行個體的constructor屬性,因此會執行新的對象,而這個新的對象又沒有指定建構函式,故預設的就是Object 7、字面量形式建立原型對象的問題二:原型屬性的重寫   原型的聲明是有先後順序的,所以,重寫的原型會切斷之前的原型,故應該注意避免此種情況的發生 複製代碼 1 var box2=new Box();     2 Box.prototype={    //字面量方式不存在修改原型屬性,直接是覆蓋掉原來的,因為每次都是建立一個新的對象 3     user:444    //這裡不會保留之前原型的任何資訊 4                 //把原來的原型對象和建構函式對象之間的關係給切斷了 5 } 6 //字面量方式建立,只要聲明了,以後隨便怎麼重寫原型對象,已經建立的執行個體對象的值不會改變 7     var box1=new Box();     8  9     alert(box1.user);    // 444    重寫中賦值為44410     alert(box1.age);    //undefined        因為第二次重寫中沒有這個屬性11 12     alert(box2.age);//123    因為在對象的定義是發生在用字面量形式重寫原型屬性之前,故以後的原型屬性的修改和box2無關複製代碼 9、通過原型模式擴充類型的方法   原型對象不僅僅可以在自訂對象的情況下使用, 而 ECMAScript 內建的參考型別都可以使用這種方式,並且內建的參考型別本身也使用了原型。   通過prototype來添加方法,訪問的時候要用這種類型的變數點這個方法,和訪問系統內建的方法是一樣的,但是最好不要用這種方式來添加方法,因為可能會存在命名衝突的問題,特別是在代碼量大的時候,容易照成命名衝突問題。 複製代碼 1     String.prototype.addString=function(s){        //傳遞一個參數過來, 2         return '【' + s + '】'; 3     } 4  5     alert('abc'.addString('111'));// 【111】 6  7     String.prototype.addString=function(){    //可以不進行傳參,通過this來代表當前調用這個方法 的字串 8         return this+"被添加了!"; 9     }10 11     alert("abcd".addString());    // abcd被添加了!複製代碼 原型模式建立對象的缺點以及採用的方式:                                                                                  原型模式建立對象也有自己的缺點, 它省略了建構函式傳參初始化這一過程, 帶來的缺點就是初始化的值都是一致的。而原型最大的缺點就是它最大的優點,那就是共用。   原型中所有的屬性都能被很多執行個體共用,共用對於函數非常適合,對於包含基本值的屬性頁面還可以,但是如果屬性包含參考型別就一定存在問題,見代碼中 複製代碼 1 function Box(){}; 2     Box.prototype={ 3         constructor:Box, 4         user:'abc', 5         age:22, 6         family:['哥哥','姐姐','妹妹'], 7         run:function(){ 8             return this.user+" "+this.age+" 運行中..."; 9         }10     }11 12     //var box=new Box(123);    //缺點之一就是不能夠傳遞參數13 14     var box1=new Box();    //缺點二就是原型的共用性,這也是它最大的優點15     alert(box1.family);    //哥哥,姐姐,妹妹16     box1.family.push('弟弟');    //在第一個執行個體後修改了參考型別,保持了共用,但本質上不希望它共用17     alert(box1.family);    //哥哥,姐姐,妹妹,弟弟18 19     var box2=new Box();20     alert(box2.family);    //哥哥,姐姐,妹妹,弟弟    共用了box1參考型別添加後的原型複製代碼 11、組合建構函式 + 原型模式   這種方式能夠很好的解決傳參和引用共用的問題,是建立對象比較好的方法 複製代碼 1 function Box(user,age){    //建構函式,這裡面聲明一些會變的屬性和參考型別的屬性 2         this.user=user; 3         this.age=age; 4         this.family=['哥哥','姐姐','妹妹']; 5     } 6  7     Box.prototype={    //原型模式 8         constructor:Box, 9         run:function(){10             return this.user+" "+this.age+" 運行中...";11         }12     }13 14     //下面可以看出通過建構函式總寫一些自己會變的屬性等,即使值改變了也不會被共用出去15     var box1=new Box('abc',22);16     alert(box1.run());    // abc 22 運行中...17     alert(box1.family);    //哥哥,姐姐,妹妹    18     box1.family.push("弟弟");19     alert(box1.family);    //哥哥,姐姐,妹妹,弟弟20 21     var box2=new Box('jack',33);22     alert(box2.family);    //哥哥,姐姐,妹妹    並沒有共用對象box1中的參考型別23     alert(box2.run());    //jack 33 運行中...        和box1 中的輸出結果是不一樣的複製代碼 11、動態原型模型   將建構函式和原型模型封裝在一起,也能夠解決共用的問題,但是要注意兩個問題,一是資源的浪費,還有就是要注意,不可以再使用字面量的方式重寫原型,因為會切斷執行個體和新原型之間的聯絡   在下面的代碼中,當第一次調用建構函式時,run()方法發現不存在,然後初始化原型。當第二次調用, 就不會初始化, 並且第二次建立新對象, 原型也不會再初始化了。 這樣及得到了封裝, 又實現了原型方法共用,並且屬性都保持獨立 複製代碼function Box(user,age){        this.user=user;        this.age=age;        this.family=['哥哥','姐姐','妹妹'];         if(typeof this.run != 'function'){//判斷this.run是否存在,因為執行一次後類型傳回值就為為function            alert("原型初始化開始");//沒有判斷之前,每new一次Box都會執行一次,            Box.prototype.clss='person';            Box.prototype.run=function(){                return this.user+" "+this.age+" 運行中...";            };            alert("原型初始化結束");        }    }     var box1=new Box('abc',22);        var box2=new Box('jack',33);  //也可以判斷原型中任意一個屬性的類型傳回值,是否等於undefined,若等於原型就還沒有建立    //因為原型中的屬性一般都是一開始就有特定的值的,除非故意賦值為undefined[這樣沒意義了]    function Box(user,age){        this.user=user;        this.age=age;        this.family=["哥哥","姐姐","妹妹"];         if(typeof this.clss == "undefined"){            alert("原型初始化開始");//沒有判斷之前,每new一次Box都會執行一次,            Box.prototype.clss="person";            Box.prototype.run=function(){                return this.user+" "+this.age+" 運行中...";            };            alert("原型初始化結束");        }    }     var box1=new Box('abc',22);        var box2=new Box('jack',33);複製代碼 12、寄生建構函式   寄生建構函式,其實就是原廠模式+建構函式模式。這種模式比較通用,但不能確定對象關係,所以,在可以使用之前所說的模式時,不建議使用此模式;在什麼情況下使用寄生建構函式比較合適呢?假設要建立一個具有額外方法的參考型別。由於之前說明不建議直接 String.prototype.addstring,可以通過寄生構造的方式添加 複製代碼    function myString(string){        var str = new String(string);        str.addString = function(){            return this + ",被添加了!";//this 指的是對象str下的值string        }          return str;      }     var box = new myString("abcd");    alert(box.addString());複製代碼 13、穩妥建構函式   在一些安全的環境中, 比如禁止使用 this 和 new, 這裡的 this 是建構函式裡不使用 this,這裡的 new 是在外部執行個體化建構函式時不使用 new。這種建立方式叫做穩妥建構函式。 複製代碼 1     function Box(name, age) { 2         var obj = new Object(); 3         obj.name = name; 4         obj.age = age; 5         obj.run = function () { 6             return name + age + '運行中...'; 7         }; 8         return obj; 9     }10 11     var box1 = Box('Lee', 100);12     alert(box1.run());13 14     var box2 = Box('Jack', 200);15     alert(box2.run());複製代碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.