JS中原型與原型鏈

一. 普通對象與函數對象

JavaScript 中,萬物皆對象!但對象也是有區別的。分爲普通對象和函數對象,Object 、Function等 是 JS 自帶的函數對象。下面舉例說明。javascript

var o1 = {}; 
var o2 =new Object();
var o3 = new f1();

function f1(){}; 
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

console.log(typeof Object); //function 
console.log(typeof Function); //function  

console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   

console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object
在上面的例子中 o1 o2 o3 爲普通對象,f1 f2 f3 爲函數對象。怎麼區分,其實很簡單, 凡是經過 new Function() 建立的對象都是函數對象(Function),其餘的都是普通對象(Object)。f1,f2,歸根結底都是經過 new Function()的方式進行建立的。Function Object也都是經過 New Function()建立的

二. 原型對象

function Person(){}
Person.prototype.name = 'test';
Person.prototype.age  = 28;
Person.prototype.job  = 'Software Engineer';
Person.prototype.sayName = function() {
  alert(this.name);  
}
var person = new Person();

在 JavaScript 中,每當定義一個對象(函數也是對象)時候,對象中都會包含一些預約義的屬性。其中每一個函數對象都有一個prototype屬性,這個屬性指向函數的原型對象。而原型對象他自己就是一個普通對象(沒有經過 new Function() 建立的對象都是普通對象),即原型對象就是Person.prototype,若是你仍是懼怕它,那就把它想一想成一個字母 A:var A = Person.prototype。這裏要強調一點,只有函數對象纔會擁有prototype屬性,可是每一個對象都擁有__proto__屬性(null除外)java

在上面咱們給A添加了四個屬性:name、age、job、sayName。其實它還有一個默認的屬性:constructor。在默認狀況下,全部的原型對象都會自動得到一個 constructor(構造函數)屬性,這個屬性(是一個指針)指向 prototype屬性所在的函數(Person)。即:Person.prototype.constructor == Person。當咱們建立對象var person = new Person()時,person能夠繼承原型對象Person.prototype的constructor屬性,所以person.constructor == Person,注意person這個實例自己是沒有constructor,實例的constructor是經過原型鏈(__proto__)獲取原型對象上邊的constructor。函數

person.constructor == Person
Person.prototype.constructor == Person

從這一角度,咱們能夠將Person.prototype理解爲Person的一個實例。(但其實原型對象(Person.prototype)並非構造函數(Person)的實例,而是構造函數的屬性,並且是預約義添加的)。this

var A = new Person();
Person.prototype = A;

可是有一個很是特別的原型對象:Function.prototype,它並非普通對象,而是函數對象,而這個函數對象卻沒有prototype屬性(前面所說的「每一個函數對象都有一個prototype屬性,這個屬性指向函數的原型對象」,對Function.prototype並不適用)。spa

function Person(){};
console.log(typeof Person.prototype) //Object
console.log(typeof Function.prototype) // Function,這個特殊
console.log(typeof Object.prototype) // Object
console.log(typeof Function.prototype.prototype) //undefined

Function.prototype爲何是函數對象呢?prototype

 var A = new Function();
 Function.prototype = A;

上文提到過凡是經過凡是經過 new Function() 建立的對象都是函數對象,其餘的都是普通對象。由於 A 是函數對象,因此Function.prototype是函數對象。指針

. __proto__

JS 在建立對象(不管是普通對象仍是函數對象)的時候,都有一個叫作__proto__的內置屬性,用於指向建立它的構造函數的原型對象。對象 person有一個__proto__屬性,建立它的構造函數是Person,構造函數的原型對象是Person.prototype ,因此:person.__proto__ == Person.prototypecode

Person.prototype.constructor == Person;
person.__proto__ == Person.prototype;
person.constructor == Person;

相似的,Person.__proto__ == Function.prototype;Person.prototype.__proto__ == Object.prototype;Object.__proto__ == Function.prototype; Object.prototype.__proto__ == null,按照上述理解 Object.prototype是普通對象,而普通對象的構造函數是Object,那麼Object.prototype.__proto__ == Object.prototype,從而在原型鏈上造成死循環沒法終止,所以定義Object.prototype.__proto__ == null,null是原型鏈的頂端。對象

不過,要明確的真正重要的一點就是,這個鏈接存在於實例(person)與構造函數(Person)的原型對象(Person.prototype)之間,而不是存在於實例(person)與構造函數(Person)之間blog

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.prototype = animal;
var tidy = new dog();
console.log(dog.price) //undefined
console.log(tidy.price) // 2000

實例(tidy)和 原型對象(dog.prototype)存在一個鏈接。這個鏈接存在於實例(tidy)與構造函數的原型對象(dog.prototype)之間,而不是存在於實例(tidy)與構造函數(dog)之間。

四. 函數對象

全部函數對象的__proto__都是指向Function.prototype,它是一個空函數。

Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true

Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true

String.__proto__ === Function.prototype  // true
String.constructor == Function //true

// 全部的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true

// 全部的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true

Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true

RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true

Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true

Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

全部的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身。全部構造器都繼承了Function.prototype的屬性及方法。Function.__proto__ == Function.prototype,而前面說過,Function.prototype它不是普通對象,而是函數對象,那麼Function.prototype.__proto__ == ?,按照上述,Function.prototype.__proto__ == Function.prototype,但又出現了原型鏈上的死循環,JS一直強調萬物皆對象,函數對象也是對象,給他認個祖宗,指向 Object.prototype,Object.prototype.__proto__ == null,保證原型鏈可以正常結束。

Function.prototype.__proto__ ===  Object.prototype // true

特別的,Math,JSON是以普通對象形式存在的。


Math.__proto__ === Object.prototype // true Math.construrctor == Object // true JSON.__proto__ === Object.prototype // true JSON.construrctor == Object //true

四. 總結

原型和原型鏈是JS實現繼承的一種模型。

原型鏈的造成是真正是靠__proto__而非prototype。

參考資料:https://www.jianshu.com/p/dee9f8b14771

相關文章
相關標籤/搜索