Object-Oriented(二)原型對象

自用備忘筆記函數

 

1. 理解原型對象this

只要建立函數,函數上就會建立一個 prototype 屬性指向函數的原型對象。spa

function Person() {}
Person.prototype  //指向該函數的原型對象

全部原型對象會自動得到一個 constructor 屬性指向 prototype 屬性所在的函數。prototype

Person.prototype.constructor === Person;  //true

每一個經過構造函數生成的實例對象,都包含一個 [[Prototype]] 屬性指向其構造函數的原型對象,在 Firefox、Safari 和 Chrome 中能夠經過 __proto__ 來訪問。code

var Daryl = new Person();
Daryl.__proto__ === Person.prototype;  //true

雖然沒法經過標準的方法訪問 [[Prototype]] 方法,但能夠經過 isPrototypeOf() 方法來確實關係。對象

Person.prototype.isPrototypeOf(Daryl);  //true

ES5新增了一個方法:Object.getPrototypeOf(),該方法能夠返回 [[Prototype]] 的值。blog

Object.getPrototypeOf(Daryl) === Person.prototype;  //true

雖然經過實例對象能夠訪問原型中的屬性,但沒法經過實例對象重寫原型中的屬性。字符串

function Person() {}
Person.prototype.name = 'Daryl';

var person1 = new Person(),
    person2 = new Person();

person1.name = 'Nicholas';
person1.name;  //Nicholas
person2.name;  //Daryl

對對象屬性的訪問,是先檢索實例上有無該屬性,再檢索原型對象上有無該屬性。若刪除實例上的屬性,則會恢復對原型對象屬性的訪問。get

person1.name = 'Nicholas';
delete person1.name;
person1.name;  //Daryl

經過 hasOwnProperty 方法能夠檢測某個屬性究竟來自實例仍是原型。原型

person1.name = 'Nicholas';
person1.hasOwnProperty('name');  //true
person2.hasOwnProperty('name');  //false

 

2. 原型與 in 操做符

in 操做符用來檢測某個對象中是否有某個屬性,能夠單獨使用,也能夠搭配 for 使用。

person1.name = 'Nicholas';
'name' in person1;  //true
'name' in person2;  //true 

使用 for-in 遍歷對象的屬性時會將原型上的屬性一塊兒遍歷,若是須要濾掉這部分,能夠搭配 hasOwnProperty 方法一塊兒使用。

function Person() {}
Person.prototype.name = 'Nicholas';
var person = new Person();
person.age = 29;
person.gender = 'male';

for (var o in person) {
    if (person.hasOwnProperty(o)) {
        console.log(o);  //age, gender
    }
}

ES5新增了 Object.keys 方法,用來返回一個全部可枚舉的實例屬性的集合。

Object.keys(person);  //['age', 'gender']

若是想要獲得全部的屬性,不管是否可枚舉,能夠使用 Object.getOwnPropertyNames

Object.getOwnPropertyNames(Person.prototype);  //['constructor', 'name'] 

以上兩種方法均可以用來代替 for-in 循環。

 

3. 更簡單的原型語法

爲了不重複書寫,能夠經過對象字面量的方式重寫原型對象。 

function Person() {}
Person.prototype = {
    name: 'Nicholas',
    age: 29,
    sayName: function() {
        alert(this.name);
    }
};

這種方式會致使原型對象上的 constructor 屬性丟失,能夠手動指定 constructor 屬性。

function Person() {}
Person.prototype = {
    constructor: Person,
    name: 'Nicholas',
    age: 29,
    sayName: function() {
        alert(this.name);
    }
};

但 constructor 自己是不可枚舉的,能夠經過 ES5 新增的 Object.defineProperty() 方法設置。

Object.defineProperty(Person.prototype, 'constructor', {
    enumerable: false,
    value: Person
});

 

4. 原型的動態性

因爲在原型中查找值的過程是一次搜索,所以在任什麼時候候對原型對象的修改都會當即反映在實例上。

var Daryl = new Person();
Person.prototype.sayHi = function() {
    alert('Hi');
};
Daryl.sayHi();  //Hi

可是重寫原型對象會致使構造函數與原型之間的聯繫被切斷。

var Daryl = new Person();
Person.prototype = {
    sayHi: function() {
        alert('Hi');
    }
};
Daryl.sayHi();  //出錯

 

5. 原生對象的原型

原型模式不只應用在自定義類型方面,就連全部原生的引用類型,都是採用這種模式建立的。它們都在其構造函數的原型上定義了方法。

Array.prototype.sort;  //function
String.prototype.substring;  //function

還能夠在原生對象上定義新方法,以下,定義一個反轉字符串的方法。

String.prototype.reverseString = function() {
    return this.split('').reverse().join('');
};

'Hello World!'.reverseString();  //!dlroW olleH

 

6. 原型的問題

因爲原型上全部屬性均是共享的,若某個實例對原型中的引用類型屬性進行修改,則這一個修改會反映在全部實例上。

function Person() {}
Person.prototype.friends = ['Shelby', 'Court'];

var person1 = new Person(),
    person2 = new Person();

person1.friends.push('Van');
person2.friends;  //['Shelby', 'Court', 'Van'];
相關文章
相關標籤/搜索