深入理解javascript中的原型

原型prototype是javascript中極其重要的概念之一肺素,但也是比較容易引起混淆的地方走趋。我們需要花費(fèi)一些時(shí)間和精力好好理解原型的概念,這對于我們學(xué)習(xí)javascript是必須的汁雷。


原型的概念

真正理解什么是原型是學(xué)習(xí)原型理論的關(guān)鍵介时。很多人在此產(chǎn)生了混淆,沒有真正理解蒜埋,自然后續(xù)疑惑更多淫痰。

首先,我們明確原型是一個(gè)對象整份,其次待错,最重要的是,
** Every function has a prototype property and it contains an object **
這句話就是說烈评,每個(gè)函數(shù)都有一個(gè)屬性叫做原型火俄,這個(gè)屬性指向一個(gè)對象。
也就是說讲冠,原型是函數(shù)對象的屬性瓜客,不是所有對象的屬性,對象經(jīng)過構(gòu)造函數(shù)new出來竿开,那么這個(gè)new出來的對象的構(gòu)造函數(shù)有一個(gè)屬性叫原型谱仪。明確這一點(diǎn)很重要。

** The prototype property is a property that is available to you as soon as you define the function. Its initial value is an "empty" object.
**
每次你定義一個(gè)函數(shù)的時(shí)候否彩,這個(gè)函數(shù)的原型屬性也就被定義出來了疯攒,也就可以使用了,如果不對它進(jìn)行顯示賦值的話列荔,那么它的初始值就是一個(gè)空的對象Object卸例。
所以称杨,綜上我們知道我們討論原型的時(shí)候,都是基于函數(shù)的筷转,有了一個(gè)函數(shù)對象,就有了原型悬而。切記這一點(diǎn)呜舒,討論原型,不能脫離了函數(shù)笨奠,它是原型真正歸屬的地方袭蝗,** 原型只是函數(shù)的一個(gè)屬性 **!

function foo(a,b) {
    return a+b;
}
foo.prototype
foo.constructor

chrome控制臺測試結(jié)果

Paste_Image.png

我們可以看到函數(shù)foo的原型是空對象Object般婆,所有函數(shù)的構(gòu)造函數(shù)都是Function到腥。


使用原型給對象添加方法和屬性

不使用原型,使用構(gòu)造函數(shù)給對象添加屬性和方法的是通過this蔚袍,像下面這樣乡范。

function Gadget(name, color) {
    this.name = name;
    this.color = color;
    this.whatAreYou = function() {
        return 'I am ' + this.color + '  ' + this.name; 
    }
} 

Gadget是一個(gè)構(gòu)造函數(shù),作為一個(gè)函數(shù)啤咽,它有一個(gè)屬性晋辆,這個(gè)屬性是原型,它指向一個(gè)對象宇整,目前我們沒有設(shè)置這個(gè)屬性瓶佳,所以它是一個(gè)空的對象。
** Adding methods and properties to the prototype property of the constructor
function is another way to add functionality to the objects this constructor produces **
當(dāng)我們有了原型之后鳞青,我們可以給構(gòu)造函數(shù)的原型對象添加屬性和方法來霸饲。
像下面這樣

Gadget.prototype.price = 100;
Gadget.prototype.rating = 3;
Gadget.prototype.getInfo = function() {
    return 'Rating: ' + this.rating +', price: ' + this.price;
}

給原型添加了屬性和方法后,原型所指的對象也會更新

Paste_Image.png

使用原型對象的屬性和方法

我們使用原型的對象和方法不會在直接在構(gòu)造函數(shù)上使用臂拓,而是通過構(gòu)造函數(shù)new出一個(gè)對象厚脉,那么new出來的對象就會有構(gòu)造函數(shù)原型里的屬性和方法。

Paste_Image.png

這里很容易造成誤解埃儿,我們需要強(qiáng)調(diào)newtoy這個(gè)new出來的對象是沒有原型的器仗,原型只是函數(shù)對象的一個(gè)屬性,newtoy是通過構(gòu)造函數(shù)new出來的對象童番,所以他不是函數(shù)對象精钮,也沒有prototype屬性,我們在chrome的控制臺里自然也無法訪問他的prototype屬性剃斧。
但我們可以通過構(gòu)造函數(shù)訪問轨香。
我們知道每個(gè)對象都有constructor屬性,newtoy的constructor屬性就指向Gadget幼东,那么我們通過constructor可以訪問到prototype臂容。

Paste_Image.png

到這里科雳,我們對為什么要通過constructor.protptype訪問屬性應(yīng)該清楚了。(筆者第一次接觸原型就沒看懂這個(gè))脓杉,切記糟秘,原型是函數(shù)對象的屬性,只有函數(shù)對象才有原型就容易理解了球散。

原型的實(shí)時(shí)性

這里特別需要提出尿赚,原型是實(shí)時(shí)的,意思就是原型對象的屬性和方法會實(shí)時(shí)更新蕉堰。其實(shí)很好理解凌净,javascript中對象是通過引用傳遞的,原型對象只有一份屋讶,不是new出一個(gè)對象就復(fù)制一份冰寻,所以我們對原型的操作和更新,會影響到所有的對象皿渗。這就是原型對象的實(shí)時(shí)性斩芭。

Paste_Image.png

自身屬性與原型屬性

這里涉及到j(luò)avascript是如何搜索屬性和方法的,javascript會先在對象的自身屬性里尋找羹奉,如果找到了就輸出秒旋,如果在自身屬性里沒有找到,那么接著到構(gòu)造函數(shù)的原型屬性里去找诀拭,如果找到了就輸出迁筛,如果沒找到,就null耕挨。
所以细卧,如果碰到了自身屬性和原型屬性里有同名屬性,那么根據(jù)javascript尋找屬性的過程筒占,顯然贪庙,如果我們直接訪問的話,會得到自身屬性里面的值翰苫。

Paste_Image.png

我們加下來做一個(gè)小實(shí)驗(yàn)止邮,尋找toString方法是誰的屬性,一步步尋找

Paste_Image.png

通過實(shí)驗(yàn)我們可以發(fā)現(xiàn)奏窑,原來toString方法是object的原型對象的方法导披。

isPrototypeOf()

Object的原型里還有這樣一個(gè)方法isPrototypeOf(),這個(gè)方法可以返回一個(gè)特定的對象是不是另一個(gè)對象的原型,實(shí)際這里不準(zhǔn)確埃唯,因?yàn)槲覀冎乐挥泻瘮?shù)對象有原型屬性撩匕,普通對象通過構(gòu)造函數(shù)new出來,自動繼承了構(gòu)造的函數(shù)原型的屬性方法墨叛。但這個(gè)方法是可以直接判斷止毕,而不需要先取出constructor對象再訪問prototype模蜡。看下面的例子:

function Human(name) {
    this.name = name;
}

var monkey = {
    hair:true,
    feeds:'banana',
}

Human.prototype = monkey;

var chi = new Human('chi');
Paste_Image.png

我們知道chi這個(gè)對象是沒有原型屬性的扁凛,它有的是他的構(gòu)造函數(shù)的原型屬性monkey忍疾。但isPrototypeOf直接判斷,實(shí)際上是省略了獲取構(gòu)造函數(shù)的過程令漂,搞清楚這里面的區(qū)別膝昆。
object還有一個(gè)getPrototypeOf方法,基本用法和isPrototype一樣叠必,參考下面的代碼:

Paste_Image.png

神秘的proto鏈接

我們之前訪問對象的原型,都要先取得構(gòu)造函數(shù)然后訪問prototype

chi.constructor.prototype;
newtoy.constructor.prototype;

這樣是不是特別別扭妹窖,所以各個(gè)瀏覽器一般都會給出一個(gè)proto屬性纬朝,前后分別有雙下劃線,對象的這個(gè)屬性可以直接訪問到構(gòu)造函數(shù)的原型骄呼。這就很方便了共苛。所以proto與prototype是有很大區(qū)別的。區(qū)別就在此蜓萄。proto是實(shí)例對象用來直接訪問構(gòu)造函數(shù)的屬性隅茎,prototype是函數(shù)對象的原型屬性。

Paste_Image.png
chi.constructor.prototype == chi.__proto__
Paste_Image.png

顯然現(xiàn)在已經(jīng)很容易弄清楚了proto和prototype的區(qū)別了嫉沽。

原型的陷阱

原型在使用的時(shí)候有一個(gè)陷阱:
** 在我們完全替換掉原型對象的時(shí)候辟犀,原型會失去實(shí)時(shí)性,同時(shí)原型的構(gòu)造函數(shù)屬性不可靠绸硕,不是理論上應(yīng)該的值堂竟。**
這個(gè)陷進(jìn)說的是什么呢?好像不太明白
舉個(gè)例子我們就懂了

function Dog() {
    this.tail = true;
}

var benji = new Dog();
var rusty = new Dog();

Dog.prototype.say = function () {
    return 'Woof!';
};

我們進(jìn)行測試:

Paste_Image.png

直到這里一切都是正常的
接下來我們將原型對象整個(gè)替換掉

Dog.prototype = {
    paws: 4,
    hair: true
};
Paste_Image.png

通過測試我們發(fā)現(xiàn)玻佩,我們沒法訪問剛剛更新的原型對象出嘹,卻能訪問之前的原型對象,這說明沒有實(shí)現(xiàn)實(shí)時(shí)性咬崔。

我們繼續(xù)測試

Paste_Image.png

我們發(fā)現(xiàn)這時(shí)新建的對象可以訪問更新后的原型税稼,但是構(gòu)造方法又不對了,本來constructor屬性應(yīng)該指向dog垮斯,結(jié)果卻指向了Object郎仆。這就是javascript中的原型陷阱。

我們很容易解決這個(gè)問題甚脉,只要在更新原型對象后面丸升,重新指定構(gòu)造函數(shù)即可。

Dog.prototype.constructor = Dog;
Paste_Image.png

這樣所有就按正常的運(yùn)行了

** 所以我們切記在替換掉原型對象之后牺氨,切記重新設(shè)置constructor.prototype **

小結(jié)

我們大概介紹了原型中容易混淆的問題狡耻,主要有以下幾方面:

  • 所有函數(shù)都有一個(gè)屬性prototype墩剖,這就是我們指的原型,他的初始值是一個(gè)空的對象
  • 你可以原型對象添加屬性和方法夷狰,甚至直接用另一個(gè)對象替換他
  • 當(dāng)你用構(gòu)造函數(shù)new出一個(gè)對象之后岭皂,這個(gè)對象可以訪問構(gòu)造函數(shù)的原型對象的屬性和方法
  • 對象的自身屬性搜索的優(yōu)先級比原型的屬性要高
  • proto屬性的神秘連接及其同prototype的區(qū)別
  • prototype使用中的陷阱
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沼头,隨后出現(xiàn)的幾起案子爷绘,更是在濱河造成了極大的恐慌,老刑警劉巖进倍,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件土至,死亡現(xiàn)場離奇詭異,居然都是意外死亡猾昆,警方通過查閱死者的電腦和手機(jī)陶因,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垂蜗,“玉大人楷扬,你說我怎么就攤上這事√” “怎么了烘苹?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長片部。 經(jīng)常有香客問我镣衡,道長,這世上最難降的妖魔是什么吞琐? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任捆探,我火速辦了婚禮,結(jié)果婚禮上站粟,老公的妹妹穿的比我還像新娘黍图。我一直安慰自己,他們只是感情好奴烙,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布助被。 她就那樣靜靜地躺著,像睡著了一般切诀。 火紅的嫁衣襯著肌膚如雪揩环。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天幅虑,我揣著相機(jī)與錄音丰滑,去河邊找鬼。 笑死倒庵,一個(gè)胖子當(dāng)著我的面吹牛褒墨,可吹牛的內(nèi)容都是我干的炫刷。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼郁妈,長吁一口氣:“原來是場噩夢啊……” “哼浑玛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起噩咪,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤顾彰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胃碾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涨享,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年仆百,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灰伟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡儒旬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帖族,到底是詐尸還是另有隱情栈源,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布竖般,位于F島的核電站甚垦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏涣雕。R本人自食惡果不足惜艰亮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挣郭。 院中可真熱鬧迄埃,春花似錦、人聲如沸兑障。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流译。三九已至逞怨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間福澡,已是汗流浹背叠赦。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留革砸,地道東北人除秀。 一個(gè)月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓糯累,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鳞仙。 傳聞我的和親對象是個(gè)殘疾皇子寇蚊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容