簡單地說,prototype就是原型對象的一個開放接口,讓咱們能夠爲對象的實例擴展屬性和方法。
先看一下對象的靜態方法和實例方法。瀏覽器
function Person () { }; Person.sayHello = function () { //定義一個靜態方法 console.log("Hello!"); }; var p = new Person(); Person.sayHello(); // Hello! p.sayHello(); // p.sayHello is not a function
由此能夠看出,對象的靜態方法不能被對象實例調用。例如String對象的fromCharCode()方法,正確的調用方法應該是 String.fromCharCode(),而使用 myString.fromCharCode()會報錯。函數
若是咱們想給對象的實例添加方法,能夠在構造函數中使用"this"來定義:this
var Person = function () { this.sayHello = function () { console.log("Hello!"); } }; var p = new Person(); p.sayHello(); // Hello!
構造函數模式雖然好用,但有個很大的缺點,那就是每一個方法都會在每一個實例上從新建立一遍。例如,咱們建立兩個Person實例,p1和p2都有一個名爲sayHello()的方法,但這兩個方法不是同一個Function的實例。prototype
var Person = function () { this.sayHello = function () { console.log("Hello!"); } }; var p1 = new Person(); var p2 = new Person(); console.log(p1.sayHello == p2.sayHello); // false
由於JavaScript中函數也是對象,所以每定義一個函數就會實例化一個Function對象,形成了沒必要要的內存開銷。另外,使用this建立實例方法也並不老是可行的。例如咱們想給Date對象實例擴展一個format()方法,咱們總不能直接修改Date的源碼吧,而用prototype就很簡單了:指針
Date.prototype.format = function () { //do something... } var time = new Date(); time.format();
下面說一下__proto__。當調用構造函數建立一個新實例後,該實例內部會包含一個指針,指向構造函數的原型對象。這個指針在ECMA-262第5版上叫[[Prototype]],雖然沒有標準方式訪問[[Prototype]],但在Firefox、Safari和Chrome等瀏覽器上都實現了一個__proto__屬性來訪問它。當解析器查找實例上的某個屬性時,若是沒有查找到,就會在__proto__上查找,而__proto__指向構造函數的原型對象,這就是多個對象實例共享原型的屬性和方法的基本原理。code
簡單的說就是,p.__proto__ === p.constructor.prototypeorm
但也有例外,那就是使用Object.creat建立對象的時候。對象
function Person () { }; var p1 = new Person(); var p2 = Object.create(Person) console.log(p1.__proto__ === Person.prototype) // true console.log(p2.__proto__ === Person.prototype) // false console.log(p2.__proto__ === Person.prototype.constructor) // true
通常狀況下,對象的__proto__等於其構造函數的prototype ,而使用Object.create()建立的對象,其__proto__等於其原型對象的構造函數。接口