1. 程式人生 > >面向物件(建立物件)--原型模式03(上)

面向物件(建立物件)--原型模式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"