下面是2008年Github建立以來,各類編程語言的排名狀況javascript
好了,說了這麼多,我並非想說JS爲世界上最好的語言(顯然PHP纔是,對吧?←_←),也不是以爲JS會替代誰,我只是以爲,JavaScript將會是一個你們(不止web端)都應該瞭解和學習的語言工具。java
面對對象是你們都很熟悉的程序設計思想,是對真實世界的抽象,目前主要OOP語言用來實現面對對象的基礎是類,經過類的封裝,繼承來映射真實世界。包括Java,C#,甚至是python等都經過類的設計來實現面對對象。可是細想起來也會以爲有問題,由於真實世界其實沒有類這種概念,只有一個個不一樣的對象,真實世界中,繼承關係發生在對象和對象之間,而不是類。就好比孩子是對象,父母也是對象,孩子(對象)繼承自父母(對象)node
JS也是面對對象的編程語言,只不過它實現面對對象的思路是基於原型(prototype),而不是類。這種思路也叫對象關聯(Object Link Other Object),即在對象上直接映射那種真實世界的關係(如繼承)。python
相關的概念其實我研究了好幾天,除開原型概念自己,與之聯繫的對象的產生,構造函數,proto,prototype的區別,爲何對象沒有prototype這個指向原型的屬性,而是使用proto來指向原型?git
好,咱們先來談談原型這個概念。JS中一切皆對象,而每一個對象都有一個原型(Object除外),這個原型,大概就像Java中的父類,因此,基本上你能夠認爲原型就是這個對象的父對象,即每個對象(Object除外)內部都保存了它本身的父對象,這個父對象就是原型。通常建立的對象若是沒有特別指定原型,那麼它的原型就是Object(這就很相似Java中全部的類默認繼承自Object類)。程序員
在JS中,對象建立的方法有不少種,最多見的以下:github
//第一種,手動建立
var a={'name':'lala'};
//第二種,構造函數
function A(){
this.name='lala';
}
var a=new A();
//第三種,class (ES6標準寫法)
class A{
constructor(){
//super();此處沒有使用extends顯式繼承,不可以使用super()
this.name='lala';
}
}
var a=new A()
//其實後面兩種方法本質上是一種寫法複製代碼
這三種寫法建立的對象的原型(父對象)都是Object,須要提到的是,ES6經過引入class ,extends等關鍵字,以一種語法糖的形式把構造函數包裝成類的概念,更便於你們理解。是但願開發者再也不花精力去關注原型以及原型鏈,也充分說明原型的設計意圖和類是同樣的。web
當對象被建立以後,查看它們的原型的方法不止一種,之前通常使用對象的proto屬性,ES6推出後,推薦用Object.getPrototypeOf()方法來獲取對象的原型 編程
function A(){
this.name='lala';
}
var a=new A();
console.log(a.__proto__)
//輸出:Object {}
//推薦使用這種方式獲取對象的原型
console.log(Object.getPrototypeOf(a))
//輸出:Object {}複製代碼
不管對象是如何建立的,默認原型都是Object,在這裏須要說起的比較特殊的一點就是,經過構造函數來建立對象,函數A自己也是一個對象,而A有兩個指向表示原型的屬性,分別是proto和prototype,並且兩個屬性並不相同瀏覽器
function A(){
this.name='lala';
}
var a=new A();
console.log(A.prototype)
//輸出:Object {}
console.log(A.__proto__)
//輸出:function () {}
console.log(Object.getPrototypeOf(A))
//輸出:function () {}複製代碼
函數的的prototype屬性只有在看成構造函數建立的時候,把自身的prototype屬性值賦給對象的原型。而實際上,做爲函數自己,它的原型應該是function對象,而後function對象的原型纔是Object。
總之,建議使用ES6推薦的查看原型和設置原型的方法。
其實原型和類的繼承的用法是一致的:當你想用某個對象的屬性時,將當前對象的原型指向該對象,你就擁有了該對象的使用權了。
function A(){
this.name='world ';
}
function B(){
this.bb="hello"
}
var a=new A();
var b=new B();
Object.setPrototypeOf(a,b);
//將b設置爲a的原型,此處有一個問題,即a的constructor也指向了B構造函數,可能須要糾正
a.constructor=A;
console.log(a.bb)
//輸出 hello複製代碼
(增補)
若是使用ES6來作的話則簡單許多,甚至不涉及到prototype這個屬性
class B{
constructor(){
this.bb='hello'
}
}
class A extends B{
constructor(){
super()
this.name='world'
}
}
var a=new A();
console.log(a.bb+" "+a.name);
//輸出hello world
console.log(typeof(A))
//輸出 "function"複製代碼
怎麼樣?是否是已經徹底看不到原型的影子了?活脫脫就是類繼承,可是你也看獲得實際上類A 的類型是function,因此說,本質上class在JS中是一種語法糖,JS繼承的本質依然是原型,不過,ES6引入class,extends 來掩蓋原型的概念也是一個很友好的舉動,對於長期學習那些類繼承爲基礎的面對對象編程語言的程序員而言。
個人建議是,儘量理解原型,儘量用class這種語法糖。
這個概念其實也變得比較簡單,能夠類比類的繼承鏈條,即每一個對象的原型往上追溯,一直到Object爲止,這組成了一個鏈條,將其中的對象串聯起來,當查找當前對象的屬性時,若是沒找到,就會沿着這個鏈條去查找,一直到Object,若是還沒發現,就會報undefined。那麼也就意味着你的原型鏈不能太長,不然會出現效率問題。
還有人以爲個人分析很抽象,因此,我再總結一下,若是要一句話理解JS中的原型是什麼,那就是,對象的原型就指的對象的父對象。每一個對象都有父對象,父對象自己也有父對象(爺對象?)。而原型鏈呢,很像過去家譜的概念,能夠從你往上追溯你父親,到爺爺,到太爺爺一直到頭,這就造成了一個鏈條,若是其中每一個人都比做一個對象,那麼這個鏈條就是原型鏈。