JS面向對象篇2、什麼是原型?原型對象與實例對象、構造函數的關係及相關方法

本文內容:
一、構造函數、原型對象與實例對象之間的關係;
二、isPrototypeOf()和Object.getPrototypeOf();
三、實例對象上與原型對象上同名的屬性處理; 四、hasOwnProperty()方法和in操做符判斷屬性來自實例對象自己仍是它的原型對象;
五、for-in、Object.keys()和Object.getOwnPropertyNames()方法獲取實例對象或者原型對象上的屬性;
六、需注意的特殊問題javascript

構造函數、原型對象與實例對象

function Person () {}
Person.prototype.name = 'zhangsan'
Person.prototype.age = 23
Person.prototype.sayName = function () {
	console.log(thi.name)
}
var p1 = new Person()
p1.sayName() // 'zhangsan'
var p2 = new Person()
p2.sayName() // 'zhangsan'
複製代碼

首先每個函數都有一個prototype屬性,這個屬性指向一個對象,這個對象就是原型對象
默認狀況下,全部原型對象都會自動得到一個constructor屬性,這個屬性指向prototype屬性所在的函數,也就是構造函數
當調用構造函數建立一個實例對象後,該實例對象有一個內部屬性:[[prototype]](在部分瀏覽器中可經過__proto__訪問到),這個屬性指向構造函數的原型對象
如在上面的例子中:構造函數Person有一個prototype屬性,它指向原型對象,而原型對象默認有且只有一個constructor屬性,該屬性指向構造函數Person,即Person.prototype.constructor指向Person,實例對象p1和p2也都包含一個內部屬性([[prototype]]),該屬性指向Person.prototype。 它們之間的關係如圖: java

在這裏插入圖片描述
另外還須要注意的一個問題: 雖然p1和p2兩個實例中並無sayName(),可是在例子中咱們也能訪問到,成功打印出‘zhangsan’,這是經過 查找對象屬性來實現的,其過程以下: 每當代碼讀取到某個屬性時,都會執行一次搜索,首先從實例對象自己開始,若是實例對象上找到了該屬性,則返回該屬性值,若沒有找到,則繼續到實例的[[prototype]]指向的原型對象上去找,也就是去建立此實例的構造函數的prototype指向的原型對象上找,找到了就返回該屬性值。 調用p1.sayName(),首先p1上沒有定義該屬性,那麼要到Person.prototype中去找,Person.prototype中有sayName()的定義,則取該函數。

isPrototypeOf()和Object.getPrototypeOf()

由於[[prototype]]爲內部屬性,咱們沒法直接訪問到它,可是咱們能夠經過isPrptotypeOf()方法來肯定對象與它的構造函數的原型對象之間的關聯;數組

console.log(Person.prototype.isPrototypeOf(p1)) // true
console.log(Person.prototype.isPrototypeOf(p2)) // true
複製代碼

ECMAScript5中新增了Object.getPrototypeOf()方法用來獲取一個對象的原型:瀏覽器

console.log(Object.getPrototypeOf(p1) == Person.prototype) // true
console.log(Object.getPrototypeOf(p1).name) // 'zhangsan'
複製代碼

實例對象上定義的與原型對象同名的屬性

在實例對象上定義了與原型對象上同名的屬性後,會屏蔽掉原型對象的該屬性,訪問的是實例對象上的屬性。函數

hasOwnProperty()

使用hasOwnProperty()方法可以判斷某個屬性是不是實例對象自己的屬性。優化

function Person () {}
Person.prototype.name = 'zhangsan'
var p1 = new Person()
console.log(p1.hasOwnProperty('name')) // false
p1.job = 'coder'
console.log(p1.hasOwnProperty('job')) // true
複製代碼

in操做符

in操做符,能夠判斷屬性是否能夠經過對象訪問到,不管是原型上的屬性仍是實例上的。ui

console.log('name' in p1) // true
console.log('job' in p1) // true
複製代碼

結合in操做符和hasOwnProperty()方法就可以準確的判斷出,一個屬性是實例上對象自己的,仍是原型對象上的。this

function isPrototypeProperty (obj, name) {
	return !obj.hasOwnProperty(name) && name in obj
}
複製代碼

遍歷對象屬性

for-in

for-in循環返回的事全部可以經過對象訪問的、可枚舉的(enumerable)屬性,既包含存在於實例中的屬性,又包含原型中的屬性。spa

Object.keys()

ECMAScript5中新增的方法,返回的是全部可枚舉的實例屬性的字符串數組prototype

function Person () {}
Person.prototype.name = 'zhangsan'
Person.prototype.sayName = function () {
	console.log(this.name)
}
console.log(Object.keys(Person.prototype)) // ['name', 'sayName']
var p1 = new Person()
p1.name = 'lisi'
p1.age = 23
console.log(Object.keys(p1)) // ["name", "age"]
複製代碼

Object.getOwnPropertyNames()

返回全部的實例屬性,不管它是否可枚舉。

function Person () {}
Person.prototype.name = 'zhangsan'
Person.prototype.sayName = function () {
	console.log(this.name)
}
console.log(Object.keys(Person.prototype)) // ['constructor','name', 'sayName']
複製代碼

更簡單的原型語法

以前的代碼中,每添加一個屬性就要寫一遍Person.prototype,爲了簡化代碼,能夠作優化以下:

function Person() {}
Person.prototype = {
	name : 'zhangsan',
	age : 23,
	sayName : function () {
		console.log(this.name)
	}
}
var p1 = new Person()
複製代碼

要注意這樣作會對對原型對象的constructor有影響,以前提到,每個函數都有一個prototype屬性,這個屬性自動獲取了一個constructor屬性,它指向構造函數,可是上面這種寫法至關於給Person的prototype屬性從新賦予了一個新的對象,而這個對象不存在constructor屬性了。

另一個問題:重寫構造函數的原型和調用構造函數建立實例的順序很重要! 當咱們像下面這樣作時會致使報錯:

function Person () {}
var p1 = new Person()
Person.prototype = {
	name : 'zhangsan',
	sayName : function () {
		console.log(this.name)
	}
}
console.log(p1.name) // 報錯
複製代碼

如上代碼,因爲先建立了實例對象,這時p1的[[prototype]]屬性指向了Person.prototype,即構造函數Person的原型對象,接着,重寫了Person.prototype,再訪問p1.name的時候,因爲p1實例上找不到name屬性,因而要到它指向的原型對象上面找,而它的原型對象依然是最初構造函數默認指向的那個原型對象,並非後面賦值的字面兩對象,因此是找不到的,所以報錯。

相關文章
相關標籤/搜索