原型和原型鏈也是一個老生常談的問題,很多初學者對于__proto__和prototype搞不清楚。其實我剛開始學習的時候也搞不清楚,因此寫這篇文章對于這方面的知識進行一個鞏固。
作者:一木
鏈接:http://www.lofter.com/lpost/1f0227be_1106b61c
來源:LOFTER
在說原型和原型鏈之前,我們先來看看如下知識點:
一、私有變量和私有函數(shù)
在函數(shù)內(nèi)部定義的變量和函數(shù),如果不對外提供接口,那么外部是無法訪問到的。這個被定義的變量和函數(shù)就叫做該函數(shù)的私有變量和私有函數(shù)。
function Foo()?{
var name =?"yiMu";??//私有變量
var fn = function()?{???//私有函數(shù)
console.log("hello word");
};
}
var bar = new Foo();
console.log(bar.name);??//undefined
console.log(bar.fn);????//undefined
二、靜態(tài)變量和靜態(tài)函數(shù)
當定義一個函數(shù)后通過"."的方式為其添加屬性和函數(shù),通過對象本身可以訪問到,但是其實例卻無法訪問到,這樣的變量和函數(shù)叫做靜態(tài)變量和靜態(tài)函數(shù)。
function Foo(){}
Foo.num = 10;??//靜態(tài)變量
Foo.fn = function()?{???//靜態(tài)函數(shù)
console.log("hello word");
};
console.log(Foo.num);??//10
console.log(typeof Foo.fn);?//function
var bar = new Foo();
console.log(bar.num);??//undefined
console.log(typeof bar.fn);?//undefined
三、實例屬性和實例方法
在面向?qū)ο缶幊讨谐艘恍旌瘮?shù),我們還是希望在定義一個對象的時候同時定義一些屬性和方法并在實例化后能夠訪問,這些添加的屬性和方法就叫做實例屬性和實例方法。
function Foo()?{
this.num =?[];??//實例屬性
this.fn = function()?{??//實例方法
console.log("hello word");
};
}
console.log(Foo.num);???//undefined
console.log(typeof Foo.fn);?//undefined
var bar = new Foo();
console.log(bar.num);???//[]
console.log(typeof bar.fn);?//function
我們也可以為實例屬性和實例方法添加屬性和方法:
function Foo()?{
this.num =?[];??//實例屬性
this.fn = function()?{??//實例方法
console.log("hello word");
}
}
var oneBar = new Foo();
oneBar.num.push(1);
oneBar.fn =?{};
console.log(oneBar.num);????//[1]
console.log(typeof oneBar.fn);??//Object
var twoBar = new Foo();
console.log(twoBar.num);???//[]
console.log(typeof twoBar.fn);??//function
從上面的代碼可以看到,當我們在oneBar中修改了num的值和fn的類型,但是在twoBar中卻沒有發(fā)生改變,這是由于數(shù)組和函數(shù)都是對象,屬于引用類型。oneBar和twoBar中的屬性和方法名稱雖然相同但是卻不是同一個引用,它們只是對Foo對象定義的屬性和方法的一個復制。
如果一個構(gòu)造函數(shù)對象有上千的實例方法,那么它的每個實例都要對這個構(gòu)造函數(shù)的上千個實例方法進行復制,這顯然是不科學的,那么這種情況下我們就必須使用prototype了。
基本概念
我們創(chuàng)建的每一個函數(shù)都有一個prototype屬性,這個屬性是一個指針,指向一個對象,這個對象的用途是可以由特定類型的所有實例共享的屬性和方法,那么prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型對象。
使用原型的好處就是可以讓對象實例共享它所包含的屬性和方法。
在JS中,一共有兩種類型的值——原始值和對象值。每個對象都有一個內(nèi)部屬性prototype,我們稱之為原型。原型的值可以是一個對象,也可以是null。如果它的值是一個對象,那么這個對象一定也有自己的原型,這樣就形成了一條線性的鏈,我們稱之為原型鏈。
prototype和__proto__的區(qū)別
函數(shù)可以作為構(gòu)造函數(shù)來使用,只有函數(shù)的prototype屬性才可以訪問到,但是對象實例不具有該屬性,只有一個內(nèi)部的不可訪問的__proto__屬性,如下圖:

圖片來自于水乙博客
var a =?{};
console.log(a.prototype);???//undefined
console.log(a.__proto__);???//Object{}
var b = function(){};
console.log(b.prototype);???//Object{constructor: function, __proto__: Object}
console.log(b.__proto__);???//function(){}

圖片來自于水乙博客
//字面量方式
var a =?{};
console.log(a.__proto__);???//Object{}
console.log(a.__proto__?=== a.constructor.prototype);???//true
//構(gòu)造器方式
var C = function(){};
var d = new C();
console.log(d.__proto__);???//C{}
console.log(d.__proto__ === C.prototype);???//true
console.log(d.__proto__ === d.constructor.prototype);???//true
//Object.create方式
var e =?{name:?"qtb"};
var f = Object.create(e);
console.log(f.__proto__);???//Object{name:?"qtb"}
console.log(f.__proto__ === f.constructor.prototype);???//false(此處即為圖1中例外情況)

圖片來自于水乙博客
var A = function(){};
var a = new A();
console.log(a.__proto__);???//A{}(即構(gòu)造器function A 的原型對象)
console.log(a.__proto__.__proto__);?//Object{}(即構(gòu)造器function Object 的原型對象)
console.log(a.__proto__.__proto__.__proto__);???//null
構(gòu)造函數(shù)、實例和原型對象的區(qū)別
實例就是通過構(gòu)造函數(shù)創(chuàng)建的。實例一創(chuàng)建出來就具有constructor屬性(指向構(gòu)造函數(shù))和__proto__屬性(指向原型對象)。構(gòu)造函數(shù)中有一個prototype屬性,這個屬性是一個指針,指向它的原型對象。
原型對象內(nèi)部也有一個指針(constructor屬性),指向構(gòu)造函數(shù)Person.prototype.constructor = Person。
實例可以訪問原型對象上定義的屬性和方法。
如果你在本文中發(fā)現(xiàn)錯誤或者有異議的地方,可以在評論區(qū)留言,謝謝!