您好,登錄后才能下訂單哦!
我們創建的每個函數都有一個prototype屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法,通俗的講,prototype就是通過構造函數創建的實例對象的原型對象,使用原型對象的好處是可以讓所有的實例對象共享它的屬性和方法
1 function Person(){ 2 Person.prototype.name = "Nicholas"; 3 Person.prototype.age = 29; 4 Person.prototype.job = "Software Engineer"; 5 Person.prototype.sayName = function(){ 6 console.log(this.name); 7 }; 8 }; 9 10 var person1 = new Person();11 person1.sayName(); //Nicholas12 13 var person2 = new Person();14 person2.sayName(); //Nicholas15 16 console.log(person1.sayName == person2.sayName); //true
1、理解原型對象
無論什么時候,只要創建了一個新的函數,就會根據一組特定的規則為該函數創建一個prototype屬性,這個屬性指向函數的原型對象。在默認情況下,原型對象有一個constructor屬性,指向prototype屬性所在的函數指針
isPrototypeOf()方法可以確定對象之間是否存在這種關系
1 console.log(Person.prototype.isPrototypeOf(person1)); //true
ECMAScript5新增加了一個方法Object.getPrototypeOf()
1 console.log(Object.getPrototypeOf(person1));//Object {name: "Nicholas", age: 29, job: "Software Engineer"}
每當代碼讀取某個對象的某個屬性時,都會執行一次搜索。首先搜索從對象實例本身開始。如果在實例對象本身找到了具有給定名字的屬性,則返回該屬性的值,如果沒有找到,在繼續搜索指針指向的原型對象
雖然可以通過對象實例訪問保存在原型中的值,但卻不能通過對象實例改寫原型中的值,使用delete可以完全刪除實例屬性,從而讓我們能夠重新訪問原型中的屬性
1 function Person(){ 2 Person.prototype.name = "Nicholas"; 3 Person.prototype.age = 29; 4 Person.prototype.job = "Software Engineer"; 5 Person.prototype.sayName = function(){ 6 console.log(this.name); 7 }; 8 }; 9 10 var person1 = new Person();11 12 var person2 = new Person();13 14 person1.name = "Greg";15 console.log(person1.name); //Greg16 console.log(person2.name); //Nicholas17 18 delete person1.name;19 console.log(person1.name); //Nicholas
hasOwnProperty()方法可以檢測一個屬性是否在實例中,還是在原型中,在實例中返回true
1 function Person(){ 2 Person.prototype.name = "Nicholas"; 3 Person.prototype.age = 29; 4 Person.prototype.job = "Software Engineer"; 5 Person.prototype.sayName = function(){ 6 console.log(this.name); 7 }; 8 }; 9 10 var person1 = new Person();11 var person2 = new Person();12 console.log(person1.hasOwnProperty("name")); //false13 person2.name = "Greg";14 console.log(person2.hasOwnProperty("name")); //true
2、原型與in操作符
有兩種方式使用in操作符,單獨使用和在for-in中使用,單獨使用時,in操作符在通過對象能夠訪問到指定屬性時返回true,無論在實例中還是在原型中
1 function Person(){ 2 Person.prototype.name = "Nicholas"; 3 Person.prototype.age = 29; 4 Person.prototype.job = "Software Engineer"; 5 Person.prototype.sayName = function(){ 6 console.log(this.name); 7 }; 8 }; 9 10 var person1 = new Person();11 var person2 = new Person();12 console.log(person1.hasOwnProperty("name")); //false13 console.log("name" in person1); //true14 person1.name = "Greg";15 console.log(person1.hasOwnProperty("name")); //true16 console.log("name" in person1); //true
同時使用hasOwnProperty()和in操作符,就可以確定該屬性到底是存在于實例中還是存在于原型中
1 function hasPrototypeProperty(object,name){ 2 return (!object.hasOwnProperty(name) && (name in object)); 3 } 4 5 //hasPrototypeProperty()返回true則表示該屬性在原型中 6 7 function Person(){ 8 Person.prototype.name = "Nicholas"; 9 Person.prototype.age = 29;10 Person.prototype.job = "Software Engineer";11 Person.prototype.sayName = function(){12 console.log(this.name);13 };14 };15 16 var person3 = new Person();17 console.log(hasPrototypeProperty(person3,"job")); //true18 person3.job = "Teacher";19 console.log(hasPrototypeProperty(person3,"job")); //false
在使用for-in循環時,返回的是所有能夠通過對象訪問的,可枚舉屬性,其中包括在實例中的屬性、原型中的屬性、屏蔽了原型中不可枚舉屬性的實例屬性。
要取得對象上所有可枚舉的屬性,可以使用ECMAScript5的Object.keys()方法,這個方法接受一個對象作為參數,返回一個包含所有可枚舉屬性的字符串數組。
1 function Person(){ 2 Person.prototype.name = "Nicholas"; 3 Person.prototype.age = 29; 4 Person.prototype.job = "Software Engineer"; 5 Person.prototype.sayName = function(){ 6 console.log(this.name); 7 }; 8 }; 9 10 // Object.defineProperties(Person.prototype,{11 // name:{12 // enumerable: true,13 // },14 // age:{15 // enumerable: true,16 // },17 // });18 19 var keys = Object.keys(Person.prototype);20 console.log(keys);21 var person1 = new Person();22 person1.name = "Greg";23 person1.age = 22;24 var person1Keys = Object.keys(person1);25 console.log(person1Keys);
如果想要得到所有的實例屬性,無論它是否可枚舉,都可以使用Object.getOwnPropertyNames()方法
1 var keys = Object.getOwnPropertyNames(Person.prototype);2 console.log(keys); //["constructor", "name", "age", "job", "sayName"]
3、更簡單的原型語法
是用一個包含所有屬性和方法的對象字面量來重寫整個原型對象
1 function Person(){ 2 3 } 4 5 Person.prototype = { 6 name : "Nicholas", 7 age : 29, 8 job : "SoftWare Engineer", 9 sayName : function(){10 console.log(this.name);11 },12 };
這里使用的語法,本質上完全重寫了默認的prototype對象,因此constructor屬性也就變成了新對象的constructor屬性(指向Object構造函數),此時盡管instanceof操作符還能返回正確的結果,但通過constructor已經無法確定對象的類型了
1 var friend = new Person();2 3 console.log(friend instanceof Object); //true4 console.log(friend instanceof Person); //true5 console.log(friend.constructor == Person); //false6 console.log(friend.constructor == Object); //true
可見此時的constructor屬性等于Object,如果constructor的值很重要,可以將它設置為適當的值
1 function Person(){ 2 3 } 4 5 Person.prototype = { 6 constructor : Person, 7 name : "Nicholas", 8 age : 29, 9 job : "SoftWare Engineer",10 sayName : function(){11 console.log(this.name);12 },13 };
注意:以這種方式重設constructor屬性會導致他的`Enumerable`特性被設置為true。默認情況下,原生的
constructor屬性是不可枚舉的
4、原型的動態性
1 var friend = new Person();2 Person.prototype.sayHi = function(){3 alert("hi");4 };5 friend.sayHi(); //hi
雖然friend實例是在添加新方法之前創建的,但是它可以訪問這個方法,原因在于每當代碼讀取某個對象的某個屬性時,都會執行一次搜索。首先搜索從對象實例本身開始。如果在實例對象本身找到了具有給定名字的屬性,則返回該屬性的值,如果沒有找到,在繼續搜索指針指向的原型對象
盡管可以隨時為原型添加屬性和方法,并且修改能夠立即在所有對象實例中反映出來,如果是重新寫整個原型對象,那么情況就不一樣了
1 function Person(){ 2 } 3 4 var friend = new Person(); 5 6 Person.prototype = { 7 constructor : Person, 8 name : "Nicholas", 9 age : 29,10 job : "SoftWare Engineer",11 sayName : function(){12 console.log(this.name);13 },14 };15 16 friend.sayName(); //error
5、原生對象模型
通過對原生對象的原型,不僅可以取得所有默認方法的引用,而且也可以定義新方法
1 String.prototype.ll = function(text){2 return text.length;3 };4 var msg = "Hello";5 console.log(msg.ll("SoftWare")); //8
6、原型對象的問題
首先,它省略了為構造函數傳遞參數這一環節,結果所有的實例在默認情況下都將取得相同的屬性值。原型模式最大的問題是由共享的本性所導致的,對于包含基本值的屬性可以再實例中添加一個同名屬性而對于包含引用類型的屬性來說,問題就比較突出了
1 function Person(){ 2 } 3 4 Person.prototype = { 5 constructor : Person, 6 name : "Nicholas", 7 age : 29, 8 job : "SoftWare Engineer", 9 friend : ["Shelby","Court"],10 sayName : function(){11 console.log(this.name);12 },13 };14 15 var person1 = new Person();16 var person2 = new Person();17 18 person1.friend.push("Van");19 console.log(person1.friend); //["Shelby", "Court", "Van"]20 console.log(person2.friend); //["Shelby", "Court", "Van"]21 console.log(person1.friend ==person2.friend); //true
而這個問題正是我們看到很少有人單獨使用原型模式的原因所在
組合使用構造函數模式和原型模式
1 function Person(name,age,job){ 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 this.friend = ["Shelby","Court"]; 6 } 7 8 Person.prototype = { 9 constructor : Person,10 sayName : function(){11 console.log(this.name);12 },13 };14 15 var person1 = new Person();16 var person2 = new Person();17 18 person1.friend.push("Van");19 console.log(person1.friend); //["Shelby", "Court", "Van"]20 console.log(person2.friend); //["Shelby", "Court"]21 console.log(person1.sayName === person2.sayName); //true
這是目前在ECMAScript中使用最廣泛的一種創建自定義類型的方法,可以說,這是用來定義引用類型的一種默認模式
動態原型模式
把所有信息都封裝在構造函數中,而通過在構造函數中初始化原型,又保持了同時使用構造函數和原型的優點
1 function Person(name,age,job){ 2 this.name = name; 3 this.age = age; 4 this.job = job; 5 if(typeof this.sayName != "function"){ //只有在初次調用構造函數時才會執行 6 Person.prototype.sayName = function(){ 7 console.log(this.name); 8 }; 9 };10 };11 12 var friend = new Person("Nicholas",29,"Software Engineer");13 friend.sayName(); //Nicholas
寄生構造函數模式
1 function Person(name,age,job){ 2 var o = new Object(); 3 o.name = name; 4 o.age = age; 5 o.job = job; 6 o.sayName = function(){ 7 console.log(this.name); 8 }; 9 return o;10 }11 12 var friend = new Person("Nicholas",29,"Software Engineer");13 friend.sayName(); //Nicholas
這個模式在特殊情況下來為對象創建構造函數,假設我們想創建一個具有額外方法的特殊數組
1 function SpecialArray(){ 2 var values = new Array(); 3 values.push.apply(values,arguments); 4 values.toPipedString = function(){ 5 return this.join("|"); 6 }; 7 return values; 8 }; 9 10 var colors = new SpecialArray("red","blue","green");11 console.log(colors.toPipedString()); //red|blue|green
穩妥構造函數模式
所謂穩妥對象,值得是沒有公共屬性,而且其方法也不引用this對象
1 function Person(name,age,job){ 2 3 //創建要返回的對象 4 var o = new Object(); 5 6 //可以再這里定義私有變量和函數 7 8 //添加方法 9 o.sayName = function(){10 console.log(name);11 };12 13 //返回對象14 return o;15 16 };17 18 var friend = Person("Nicholas",29,"Software Engineer");19 friend.sayName(); //Nicholas
這樣,變量friend中保存著一個穩妥對象,而除了調用sayName()方法外,沒有別的方式可以訪問其數據成員。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。