學習Javascript人,大多據說一句話叫js裏面一切都是對象。我剛開始接觸javascript面向對象編程時候,挺亂的,我當時習慣性的把PHP的面像對象思想套用在js上面,其實js的面向對象與傳統的面向對象仍是有不少區別的。這裏就再也不去講解基礎的面向對象是什麼了,看這篇文章就默認你們都知道面向對象的概念。javascript
首先,在目前的js版本中,依然沒有引入class這個關鍵詞,js裏面沒有類的概念,其餘語言在實例化一個對象的時候,都是使用new 類名來獲得實例,而js因爲沒有class,所以它的面向對象也能夠理解爲是一種模擬的方式。首先咱們來講一下js裏面的函數,js裏面都函數有一個特色,就是全部的函數都有返回值,若是咱們沒有手動編寫 return。那麼函數會返回一個underfind,若是寫了,則返回你寫的值。這種返回值是在正常的函數調用狀況下出現的。好比:java
function a(){編程
return 123;瀏覽器
}app
a();函數
此時返回123。學習
function a(){this
}prototype
a();對象
此時返回underfind。
那麼,咱們在調用js裏面的函數時候,其實不止這一種方式,經常使用的還用使用call或者apply方法也可使函數執行,這個之後再說。除此以外,咱們還能夠經過 new的方式來調用函數,new在js裏面實際上是一種運算符,但凡函數是經過它來調用的,返回值就會發生變化。若是咱們在函數中沒有寫返回值或者返回值寫的不是對象類型的數據,那麼這個函數都會返回一個空對象,若是咱們寫的返回值是一個對象,那麼則返回咱們寫的對象。因此只要是經過new來調用函數,返回值就變成了對象。
js也充分利用了這一點,來模擬傳統的面向對象,咱們來看一個例子:
function Obj(){
this.name='小紅';
this.age='24';
}
var poeple = new Obj();
根據咱們上面說的,當咱們使用new去調用函數時候,返回對象,所以咱們能夠得知people其實就是一個對象了。咱們看到上面的代碼中this.name='小紅',this.age='24',這個this實際上是什麼呢?其實這個this就是咱們返回的對象,也就是說this就是people,若是不能理解,只需記住,這是js的特色。咱們接着看:
function Obj(){
this.name='小紅';
this.age='24';
}
var poeple = new Obj();
alert(people.name);
alert(people.age);
運行上述代碼之後,分別彈出了小紅和24,這更加確切的說明this就是people.
可是這個this也不必定只是people,確切的說這個this是誰具體要看是誰在調用這個函數,看例子:
function Obj(){
this.name='小紅';
this.age='24';
}
Obj();
當我這樣去調用的時候,這個this指向的是誰呢?其實js是運行在瀏覽器中的,瀏覽器中有一個頂層對象叫作window,全部的變量和函數其實都是掛在他下面的,看例子:
Obj();
window.Obj();
這兩種寫法的效果是同樣的,此時沒有經過new來調用,其實就是一個普通的函數,而這個函數的調用者是window,所以這個this指向window.
因此說this的指向不是固定的,要看具體的調用方式以及是誰在調用。
咱們通常把這種用來返回的對象的函數稱做構造函數,他的做用就是用來建立對象的,通常爲了區分構造函數與普通函數的區別,構造函數的首字母會大寫。我早期學習的時候,就把這裏的構造函數與其餘語言裏的class弄混淆了,其實他們是不一樣的東西。
第二個就是js的面向對象一樣有繼承的功能,只是它的繼承與傳統的繼承方式也不同,我我的認爲也是一種模擬。首先說明一件事情,在js裏面全部的對象都有一個叫作__proto__的屬性,全部的函數都有__proto_的屬性和prototype的屬性,prototype屬性對應的值是一個對象,這個對象能夠保存不少東西的,中文名稱叫作原型對象,看例子:
function Obj(){
this.name='小紅';
this.age='24';
}
Obj.prototype.job='老師';
var poeple = new Obj();
alert(people.job);// 彈出 老師
剛纔已經說過,此時的people就是一個對象,而Obj是一個函數,people是Obj的一個實例對象,他們之間確定是有關係的,既然people是一個對象,那麼他確定有_proto_屬性,這個屬性其實指向了Obj的prototype,再詳細說一下,people的__proto__保存的是一個地址,這個地址指向的是people的構造函數Obj的prototype屬性。因此程序運行到people.job的時候,people這個對象在本身的屬性裏面開始尋找job這個東西,可是木有找到,因而接下來他就找到了本身的__proto__,順着裏面的地址找到了Obj.prototype,在這裏發現了job,而後呢,他就把job的值給彈了出來。這一連串的動做專業術語叫作原型鏈查找,那爲何要說這個呢,主要是由於js的繼承有一種方式就叫作原型繼承。那上面已經把對象和構造函數的在原型方面的聯繫說清楚了。那麼接下來咱們就說一下js的原型繼承,看例子:
function a(){
this.name='小紅';
this.age='24';
}
function b(){
this.job='小紅';
this.sex='男';
}
b.prototype=new a();
var c = new b();
alert(c.name);//小紅
以上代碼,作了一個功能,把b的原型對象修改爲了a的實例對象,這時b的實例對象去訪問name屬性時候,首先在自身查找,沒有發現,因而去他的構造函數b的原型裏面去查找,而此時b的原型就是一個a的實例對象,a的實例對象固然有name和age屬性了,所以就訪問到了。這種方式成爲原型繼承,有一個小問題,就是這樣直接把對象賦值給prototype,修改了prototype裏面的constructer屬性,constructer屬性後面再說。
除了原型繼承之外,還有一些方式也能夠實現繼承,最經常使用的就是call和apply方法,解釋這個問題還須要瞭解另一個特性,咱們上面已經看到了,call方法能夠用來調用函數,其實他的功能不止於此,最核心的功能就是改變this指向,咱們來看例子:
此時,對象c擁有 name age self三個屬性,前面兩個就是繼承下來的了。
其實,真實的編寫過程,每每把上述兩種繼承方式同時使用,固然還有一些其餘的繼承方式,你們能夠本身去網上查查。
這裏說一下,上面那種原型繼承法有一點點問題,直接把對象賦值給原型會致使原型裏面的constructor出現錯誤,這個constructor實際上用來保存對象的構造函數的,也就是說一個對象是由那個構造函數實例化而成的,那麼這個constructor就表明那個構造函數。舉例:
function a(){
this.name='小紅';
this.age='24';
}
var b = new a();
alert(a.constructor); // 打印出來的就是整個函數
若是直接把對象賦值給原型,這個constructor值就會被修改,因此,爲了保險起見,不少人都會手動加上一句 a.prototype.constructor = a;
這裏沒有介紹更多的面向對象的東西,寫的過程當中也有遇到疑惑的,若是有錯誤的話,請你們多多指教!