function Fn() { this.foo = ‘haha’; } // Fn爲構造函數
var f1 = new Fn(); // f1是Fn構造函數建立的對象
__proto__屬性:瀏覽器
在建立對象的時候,都會有一個屬性__proto__,它指向構造函數的原型對象prototype。函數
console.log(f1.__proto__ === Fn.prototype); // true
原型對象:this
每一個函數下都有一個子對象prototype,它稱爲原型對象。它就是指代該構造函數的原型。只有函數纔有prototype。當經過new建立一個實例對象的時候,prototype對象的成員都會成爲實例化對象的成員。spa
console.log(Fn.prototype.constructor === f1.constructor); // true
基礎知識明白以後,繼續擴展:prototype
在prototype中,也一樣有__proto__屬性
console.log(Fn.prototype.__proto__ === Object.prototype);//true
console.log(Object.prototype.__proto__);//null
console.log(Fn.prototype.__proto__.__proto__);//null
console.log(Function.prototype === Function.__proto__);//true
console.log(Object.__proto__ === Function.__proto__);//true
console.log(Function.prototype.__proto__ === Object.prototype); // true
Object.constructor === Function; // true
原型對象的做用:code
主要做用用於繼承。咱們可經過對prototype設置一個函數對象的屬性,使得後續經過該函數建立的對象,得到這個屬性。例如對象
var person = function(name) { this.name = name; } person.prototype.getName = function() { return this.name; } var zhangsan = new person(‘Zhang san’); zhangsan.getName(); // Zhang san
定義:原型對象也可能擁有原型,並從中繼承方法和屬性,一層一層、以此類推。這種關係常被稱爲原型鏈。blog
原型鏈如何容許對象之間繼承特性、prototype 屬性?如何經過它來向構造器添加方法?繼承
function Fn() { } console.log(Fn.prototype);
運行上述程序,能夠看到,prototype原型對象中有constructor和__proto__。
{
constructor: ƒ Fn(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
添加屬性到prototype中:原型鏈
function Fn() { } Fn.prototype.foo = 'bar'; console.log(Fn.prototype)
結果:
{ foo: "bar", constructor: ƒ Fn(), __proto__: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
經過Fn函數建立一個對象,並添加新的屬性:
function Fn() { } Fn.prototype.foo = 'bar'; var f = new Fn(); f.type = 'rangle'; console.log(f);
結果:
{ type: "rangle", __proto__: { foo: "bar", constructor: ƒ Fn(), __proto__: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
__proto__指向的是Fn構造函數,在建立f對象以前,經過構造函數的prototype原型賦給了foo屬性,那麼若是執行f.foo,可否得到對應的值呢?
function Fn() { } Fn.prototype.foo = 'bar'; var f = new Fn(); f.type = 'rangle'; console.log(f.foo); console.log(f.type)
結果:
bar
rangle
當訪問一個對象的屬性時,瀏覽器首先會查找該對象是否有這個屬性,若是沒有在該對象中找到,則會在它的__proto__中去找,若是這個__proto__也沒有,則又在__proto__的__proto__去找,以此類推,最終的Object.__proto__是null,原型鏈上面的全部的__proto__都被找完了, 瀏覽器全部已經聲明瞭的__proto__上都不存在這個屬性,而後就得出結論,這個屬性是 undefined。
示例:
function Fn() { } Fn.prototype.foo = 'bar'; var f = new Fn(); f.type = 'rangle'; console.log(f.foo); console.log(f.type); var f2 = new Fn(); console.log(f2.foo); console.log(f2.type);
結果:
bar
rangle
bar
undefined
2. 如何經過它來向構造器添加方法?
每一個實例對象都從原型中繼承了一個constructor屬性,該屬性指向了用於構造此實例對象的構造函數。
事實上,一種極其常見的對象定義模式是,在構造器(函數體)中定義屬性、在 prototype 屬性上定義方法。如此,構造器只包含屬性定義,而方法則分裝在不一樣的代碼塊,代碼更具可讀性。咱們能夠經過prototype,向構造器添加方法, 例如:
// 構造器及其屬性定義
function Test(a,b,c,d) { // 屬性定義
}; // 定義第一個方法
Test.prototype.x = function () { ... } // 定義第二個方法
Test.prototype.y = function () { ... }