一文搞定js的原型對象

js是一門面向對象語言,但是它並不是一門真正面向對象的語言。與完備面嚮對象語言java、c#的區別是沒有類的概念(ES6之後新出現的類是一種語法糖),而是利用原型對象來實現面向對象繼承的功能,那麼原型對象到底是什麼東西呢?沒有類js是怎麼實現的繼承呢?下文就一步步的來搞定這個問題。

js對象

在java中,新建對象需要先新建類,然後通過 new 類 來得到對象,繼承利用類來來實現,而js則是完全通過對象來實現的。

由於js在設計之初考慮其應用場景並沒有設計太過複雜,所以沒有原生 類 的概念。但是在使用中面向對象思想利於開發, 因此js之父利用其他方式實現了面對對象,也就是我們提到的原型對象

js中定義一個對象有不同的方式,最簡單的就是字面量定義,如下:

	var person = {
		name = "alex",
		age = 3,
	}
	console.log(person)

除了這種方式,js還支持利用 new 關鍵字 來定義一個對象,等等,上邊不是說 js 沒有類的概念嗎,那 去new 誰呢 ?

實際上 javac#在執行 new class也就是new 類來新建一個對象時其實執行的是類的構造函數,所以js之父設計了一種利用構造函數新建對象的方法,如下:

function person (name){
	this.name = name
}
var p1 = new person("alex");
console.log(p.name)

在這裏插入圖片描述
new的目標就是 構造函數構造函數和普通函數幾乎完全相同,唯一區別就是多了 this, this指向了將要新建的實例,利用剛纔的person 構造函數 其中this 指向了後來新建的 p1 實例。

上述 通過 new 構造函數 person 得到的對象 p ,如果我們想要新建一個 p2 那麼可以執行:

p2 = new person("tom");
console.log(p2.name);

現在可以得到兩個對象 p1 p2 ,我們都知道 對象是由屬性和方法組成的,現在構造函數中只定義 name 屬性,沒有方法,如果我們想定義一個 sayName方法,我們可以修改 構造函數:

function person (name){
	this.name = name;
	this.sayName = function(){
		console.log("my name is ",this.name)
	}
}
var p1 = new person("alex");
var p2 = new person("alex");
p1.sayName();
p1.sayName();

在構造函數上新增方法能夠實現對象的公共方法,但是卻會造成內存浪費,因爲生成的每個對象的 sayName方法是相同的,每個對象都擁有了一份相同代碼。

能不能把相同公共的方法從每個對象中提取出來呢?答案是可以。

原型對象

在每個js對象中都有一個 prototype 屬性 (在谷歌瀏覽器中表現爲_proto_),該屬性指向一個對象,也就是我們常說的原型對象
在這裏插入圖片描述
上圖中的 Object 就是 原型對像,每個實例對象都指向同一個原型對象。
在這裏插入圖片描述
那麼我們就可以將一些公共方法放在原型對象上了,只要在實例對象上定義一次,其他實例對象也可以使用該方法,如下

p1.__proto__.sayHello = function(){
	console.log("hello");
}
p1.sayHello();
p2.sayHello();

在這裏插入圖片描述
說明 實例對象 p1 和 p2 只存放了 對 sayHello的 指針,並沒有複製 sayHello方法,解決上述內存佔用過多的問題。(其實 存放指針這一說法並不準確,應該是向上查找,這裏暫時這樣理解)
在這裏插入圖片描述
在上圖我們也可以直接看到 sayHello方法確實加到了實例對像 p1 的原型對象上。

從上圖我們還可以看出,實例對象p1的原型對象中處理自定義的 sayHello方法還有一個 constructor屬性,而且可以看到 它指向了我們之前定義的person構造函數。你可能會疑惑,這繞來繞去的是爲了什麼呢?其實就是爲了下面將要提到的繼承。

爲了更好的理解,這裏用一張圖表示 實例對象原型對象構造函數之間的關係。
在這裏插入圖片描述
通過這張圖,能很清晰看出三者關係:構造函數 new 出來實例對象,實例對象的prototype屬性指向一個原型對象,原型對象的constructor屬性又指向構造函數,三者形成一個閉環

面嚮對象語言的三個特點是:封裝、繼承和多態 ,那麼js如何實現呢?

繼承

js的繼承並非像 java 語法上利用 extends 關鍵字就能簡單的實現。
其實在上文中我們增加 實例對象p1 的原型對象的 sayHello 方法後 實例對象p2 就能調用該方法,這就變相實現了一種繼承。

//todo 待續

封裝

自執行函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章