面向物件(建立物件)--原型模式03(上)
回顧一下用建構函式模式編寫的程式碼
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName; } function sayName() { alert(this.name); } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor");
現在我們可以用原型模式進行改寫
function Person() { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); person1.sayName(); //"Nicholas" var person2 = new Person(); person2.sayName(); //"Nicholas" alert(person1.sayName == person2.sayName); //true
通過呼叫建構函式來建立新物件,而且新物件還會具有相同的屬性和方法。但與建構函式模式不同的是,新物件的這些屬性和方法是由所有例項共享的。
1.理解原型物件
一、基本原理
a.只要建立了一個新函式(建構函式Person),就會根據一組特定的規則為該函式建立一個 prototype屬性,這個屬性指向函式的原型物件(Person Prototype)。
b.在預設情況下,所有原型物件都會自動獲得一個 constructor(建構函式)屬性,這個屬性包含一個指向 prototype 屬性所在函式的指標。(Person.prototype. constructor 指向 Person 。而通過這個建構函式,我們還可繼續為原型物件新增其他屬性和方法)。
c.建立了自定義的建構函式之後,其原型物件預設只會取得 constructor 屬性;至於其他方法,則都是從 Object 繼承而來的。
d.當呼叫建構函式建立一個新例項後,該例項的內部將包含一個指標(內部屬性),指向建構函式的原型物件。即 [[Prototype]] ,這個連線存在於例項與建構函式的原型物件之間,而不是存在於例項與建構函式之間。
二、兩個方法
isPrototypeOf() 判斷例項的內部的一個指標(內部屬性),是否指向建構函式的原型物件。
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
Object.getPrototypeOf() 返回的原型物件(Person Prototype),即 [[Prototype]] 的值。
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
三、屬性讀取
多個物件例項共享原型中的屬性和方法的原理是什麼?
a.第一次搜尋:從物件例項本身開始。如果在例項中找到了具有給定名字的屬性,則返回該屬性的值,搜尋終止;沒有找到則繼續搜尋。(首先,解析器會問:“例項 person1 有 sayName 屬性嗎?”答:“沒有。”然後,它繼續搜尋)
b.繼續搜尋:搜尋指標指向的原型物件,在原型物件中查詢具有給定名字的屬性。如果在原型物件中找到了這個屬性,則返回該屬性的值。(繼續搜尋,再問:“ person1 的原型有 sayName 屬性嗎?”答:“有。”於是,它就讀取那個儲存在原型物件中的函式。)
另外,還要注意以下幾點:
當為物件例項新增一個屬性時,這個屬性就會遮蔽原型物件中儲存的同名屬性。使用 delete 操作符則可以完全刪除例項屬性。
hasOwnProperty() 方法可以檢測一個屬性是存在於例項中(true),還是存在於原型(false)中。
2. 原型與 in 操作符
in 操作符使用方式:單獨使用和在 for-in 迴圈中使用。
在單獨使用時, in 操作符會在通過物件能夠訪問給定屬性時返回 true ,無論該屬性存在於例項中還是原型中
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
person1.name = "Greg";
alert(person1.name); //"Greg" ——來自例項
alert(person1.hasOwnProperty("name")); //true
alert("name" in person1); //true
alert(person2.name); //"Nicholas" ——來自原型
alert(person2.hasOwnProperty("name")); //false
alert("name" in person2); //true
delete person1.name;
alert(person1.name); //"Nicholas" ——來自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
同時使用 hasOwnProperty() 方法和 in 操作符,就可以確定該屬性到底是存在於物件中,還是存在於原型中,
取得例項和原型中所有可列舉的屬性(例項和原型)
在使用 for-in 迴圈時,返回的是所有能夠通過物件訪問的、可列舉的(enumerated)屬性,其中既包括存在於例項中的屬性,也包括存在於原型中的屬性。不可列舉的屬性有下面幾種:
hasOwnProperty() 、 propertyIsEnumerable() 、 toLocaleString() 、 toString() 和 valueOf() 。ECMAScript 5 也將 constructor 和 prototype 屬性的 [[Enumerable]] 特性設定為 false 。
取得物件上所有可列舉的例項屬性
可以使用 ECMAScript 5 的 Object.keys() 方法。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //"name,age"
得到所有例項屬性,無論它是否可列舉
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys); //"constructor,name,age,job,sayName"