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
誰呢 ?
實際上 java
、 c
#在執行 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 待續
封裝
自執行函數