先看一下下面的代碼,若是你很簡單就能理解,那麼能夠跳過這一篇文章了。html
var x = new y();
// 一、全部的實例的原型都是由其構造函數的原型對象:
x.__proto__ === y.prototype
// 二、全部的構造函數都是 Function 的實例,有1可知:
y.__proto__ === Function.prototype
// 三、由於構造函數的原型對象也是對象,因此由1可知:
y.prototype.__proto__ === Object.prototype
// 四、Object 是頂級對象,因此它不符合第3條規則:
Object.prototype.__proto__ // is null
複製代碼
沒看明白的咱們繼續。。。函數
先了解一下原型模式吧:原型模式post
在原型模式中咱們能夠利用過一個原型對象來指明咱們所要建立對象的類型,而後經過複製這個對象的方法來得到與該對象如出一轍的對象實例,因此:ui
再其次,咱們都知道,JS中的聲明,實際是 new 了一個構造函數,好比:this
var a = {};
// 實際是
var a = new Object();
複製代碼
可知,a 是一個對象實例,因此 new Object 構造函數作了什麼?spa
構造函數.prototype ---> 以 String 爲例就是 String.prototypeprototype
由上述能夠,實例的原型就是構造函數上的原型對象,而JS中,爲了實現繼承,實例的 __proto__ 字段所指向它的原型,,即「實例.__proto__」。code
實例.__proto__ ---> 以 var a = {} 爲例,就是 a.__proto__htm
構造函數.prototype 指向它要拷貝的原型對象,而 實例.__proto__ 指向它的原型,都指向同一個對象,故:對象
var x = new y();
x.__proto__ === y.prototype
複製代碼
如今應該知道原型是什麼了吧。
其實構造函數是一個函數實例,因此能夠知道:
Object.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype // true
String.__proto__ === Function.prototype // true
// 特殊的 Function
Function.__proto__ === Function.prototype // true
複製代碼
再研究一下,由於函數的 prototype 一個對象實例,因此構造函數的原型對象的原型屬於誰呢? 答案是:Object ,萬物皆對象。
Array.prototype.__proto__ === Object.prototype // true
String.prototype.__proto__ === Object.prototype // true
Function.prototype.__proto__ === Object.prototype // true
// 物皆對象,因此 Object 的原型對象已是頂級了
Object.prototype.__proto__ // null
複製代碼
因此,這裏已經解釋了最開始的四行代碼。
以上就是原型和原型對象的概念,順便提一個原型對象上的屬性:constructor ,它指向創造這個原型對象的函數,即:
// 函數的原型對象的構造函數就是它本身
Array.prototype.constructor === Array // true
String.prototype.constructor === String // true
Function.prototype.constructor === Function // true
Object.prototype.constructor === Object // true
// 實例的原型的構造函數就是聲明的這個實例
var a = [], b = {}, c = '';
a.__proto__.constructor === Array // true
b.__proto__.constructor === Object // true
c.__proto__.constructor === String // true
複製代碼
如今來理解一下原型鏈。
其實上面有代碼已經初現端倪了:
Array.prototype.__proto__ === Object.prototype // true
複製代碼
構造函數的原型對象的原型,是否是有些拗口,這其實就是原型鏈,再來一個看幾個代碼:
var a = [];
a.__proto__ // = Array.prototype
a.__proto__.constructor // = Array
a.__proto__.constructor.prototype // = Array.prototype
複製代碼
有一萬中寫法來顯示一些東西,其實原型鏈的大體概念是:
因此上面的例子能夠這樣簡寫:
var a = [], b = {}, c = '';
a.constructor === Array // true
b.constructor === Object // true
c.constructor === String // true
複製代碼
由於 a 沒有 constructor ,因此會去找原型上的 constructor ,so...
再看一個例子,由於原型鏈的存在,因此能夠在構造函數的原型對象上添加方法,這樣全部由此函數建立的實例均可以調用此方法:
function a(){}
a.prototype.say = function(x){ console.log(x) }
var b = new a();
var c = new a();
b.say(‘hello’); // hello
c.say(‘你好’); // 你好
複製代碼
上面的寫法怎麼看會有點奇怪,ES6 新增了 class 的聲明,能夠這樣寫一個類:
// 定義類
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`你好,我是${this.name},我今年${this.age}歲`);
}
}
// 實例
var my = new User('jack', 22);
my.sayHello(); // 你好,我是jack,我今年22歲
複製代碼
對比一下ES5代碼:
// 定義類
function User(name, age){
this.name = name;
this.age = age;
}
User.prototype.sayHello = function(){
console.log(`你好,我是${this.name},我今年${this.age}歲`);
}
// 實例
var my = new User('jack', 22);
my.sayHello(); // 你好,我是jack,我今年22歲
複製代碼
Class 定義其自己是個語法糖,能夠用ES5來實現,具體參考此處
OK,說完了,原型鏈就這一點點。