JS好難:Prototype是個(gè)啥

說(shuō)到j(luò)s的prototype豺总,我平常都這樣用:

// 給Date添加Format方法
Date.prototype.Format = function (fmt) {}

不過(guò)這兩天在看騰訊地圖的js api署恍,發(fā)現(xiàn)了一個(gè)很特別的用法:

var Label = function(opts) {
  qq.maps.Overlay.call(this, opts);
}
Label.prototype = new qq.maps.Overlay();

wtf? 原型等于一個(gè)實(shí)例兴溜?call又是個(gè)啥项鬼?黑人問(wèn)號(hào)...

哲學(xué)起源

想要理解prototype是怎么回事零截,先從他為什么存在說(shuō)起吧。

眾所周知 JS 是一個(gè)面向?qū)ο蟮恼Z(yǔ)言秃臣,C語(yǔ)言什么的都有Class涧衙,JS木有哪工,咋整?

官方文檔是這么說(shuō)的(節(jié)選):

Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties.

blabla...

我知道你不想看這些弧哎。簡(jiǎn)單的說(shuō):JS的繼承主要是依賴于原型繼承機(jī)制雁比,如果調(diào)用這個(gè)方法或者屬性,先找對(duì)象自己撤嫩,找不到就往prototype找偎捎,prototype找不到繼續(xù)找prototype的prototype,重復(fù)下去序攘,徹底找不到再返回undefined茴她。

Prototype都有什么特點(diǎn)?

看看下面的這段:

var a = 'haha';
a.prototype // undefined
a.__proto__ // String {length: 0, [[PrimitiveValue]]: ""}

是的程奠,對(duì)象實(shí)例沒(méi)有prototype丈牢。

不過(guò)__proto__又是個(gè)啥?先別著急瞄沙。

var fk = function() {}
fk.prototype // Object {}
fk.__proto__ // function () {}

函數(shù)有己沛。恩,因吹斯聽距境。

PS.不是所有的函數(shù)都有prototype申尼,比如瀏覽器的alert()就沒(méi)有。

但是這和繼承有什么關(guān)系垫桂?往下看:

var Person = function() {
  this.name = 'Jobs'
  this.hello = function() {
    console.log('hello!');
  }
};
var p1 = new Person();
p1.__proto__ === Person.prototype // true
p1.__proto__ === p1.constructor.prototype // true
p1.hello() // hello!

可以看到a.__proto__在大多數(shù)時(shí)候都等價(jià)于a.constructor.prototype师幕,個(gè)別時(shí)候是什么呢?比如使用Object.create()诬滩,比如使用原型繼承的時(shí)候霹粥。請(qǐng)期待一下我后面的文章吧~

那么我們就可以這樣玩了:

function CatMama() {
  this.miao = function() {
    console.log('miao~');
  }
}
var catMama = new CatMama(); // 貓媽媽會(huì)miao
catMama.miao(); // miao~
catMama.__proto__ === CatMama.prototype; // true
catMama.__proto__ === catMama.constructor.prototype; // true

function LittleCat() {this.ha = function() {console.log('~')}} // 小貓什么都不會(huì)
LittleCat.prototype = catMama;

var c = new LittleCat(); // 神說(shuō)要有LittleCat
c.ha(); // ~
c.miao(); // miao~ 基因決定了一切
// 根據(jù)前面說(shuō)到的的原型繼承,因?yàn)閏沒(méi)有miao這個(gè)方法碱呼,于是往自己的prototype找蒙挑,找到了宗侦,調(diào)用愚臀。一切都那么順理成章,你看
c.miao === LittleCat.prototype.miao // true
c.__proto__ === LittleCat.prototype; // true
c.__proto__ === c.constructor.prototype; // false

// 可以看到__proto__和constructor.prototype的關(guān)系不是那么的穩(wěn)定
// 具體我也沒(méi)搞明白矾利,后面再看看

這樣就有了一個(gè)基于原型的繼承關(guān)系了姑裂。

總結(jié)起來(lái),Prototype屬性的主要特點(diǎn)其實(shí)就是:

  • 只有大部分function有
  • 內(nèi)容指向具體對(duì)象

番外 1

又有人問(wèn)了Object.getPrototypeOf() 又是個(gè)啥男旗?

function Person() {
  this.haha = function() {
    console.log('haha')
  }
}
var p = new Person();
Object.getPrototypeOf(p) === Person.prototype // true
Object.getPrototypeOf(p) === p.__proto__ // true

這么看起來(lái)似乎沒(méi)啥區(qū)別呢舶斧。MDN是這么說(shuō)的:

Warning: While Object.prototype.__proto__ is supported today in most browsers, its existence and exact behavior has only been standardized in the ECMAScript 2015 specification as a legacy feature to ensure compatibility for web browsers. For better support, it is recommended that only Object.getPrototypeOf() be used instead.

意思就是__proto__是不規(guī)范的,為了更好的兼容性察皇,建議大家用Object.getPrototypeOf()


番外 2:

如果修改了prototype茴厉,那么之前已經(jīng)創(chuàng)建好的實(shí)例會(huì)不會(huì)跟著變呢泽台?

提示一下,按照上面的描述矾缓,prototype其實(shí)是個(gè)引用怀酷。

好問(wèn)題,我們一起看一下:

// 先來(lái)一個(gè)反例
function Cat() {
  this.miao = function() {
    console.log('m~')
  }
}
var c1 = new Cat();
c1.miao(); // m~

Cat.prototype.miao = function() {
    console.log('mmmmmm');
}
c1.miao(); // m~
// 知道為什么沒(méi)有修改成功么嗜闻?
// 因?yàn)樵屠^承鏈總是先查找對(duì)象本身有沒(méi)有該方法蜕依,miao方法在Cat與prototype中都存在,所以直接調(diào)用了Cat本身的miao琉雳,而不會(huì)去prototype中查找了样眠。

正確的例子在這里:

function Cat() {}
Cat.prototype.miao = function() {
  console.log('m~')
}
var c1 = new Cat();
c1.miao(); // m~

// 修改1
Cat.prototype.miao = function() {
    console.log('mmmmmm');
}
c1.miao(); // mmmmmm
// 恩,符合我們的猜想翠肘。因?yàn)閜rototype本身就是引用檐束,所以修改這個(gè)引用的內(nèi)容,直接調(diào)用就會(huì)用最新的值

// 修改2
Cat.prototype = {
  miao: function() {
    console.log('haha');
  }
}
c1.miao(); // mmmmm
// 為什么沒(méi)有修改成功锯茄?
// 因?yàn)閜rototype對(duì)象直接變了厢塘,所以沒(méi)有修改成功
// 而上面的方式是直接修改引用本身,所以可以成功

想知道修改2為什么沒(méi)有成功的話肌幽,這就要從new 到底做了些什么說(shuō)起了晚碾。請(qǐng)期待一下吧~

P.S.以上代碼運(yùn)行環(huán)境均為Chrome 55

參考文章:

ECMA-262

詳解prototype與__proto__區(qū)別

Object.getPrototypeOf 函數(shù) (JavaScript)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市喂急,隨后出現(xiàn)的幾起案子格嘁,更是在濱河造成了極大的恐慌,老刑警劉巖廊移,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糕簿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡狡孔,警方通過(guò)查閱死者的電腦和手機(jī)懂诗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)苗膝,“玉大人殃恒,你說(shuō)我怎么就攤上這事∪杞遥” “怎么了离唐?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)问窃。 經(jīng)常有香客問(wèn)我亥鬓,道長(zhǎng),這世上最難降的妖魔是什么域庇? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任嵌戈,我火速辦了婚禮覆积,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘熟呛。我一直安慰自己技健,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布惰拱。 她就那樣靜靜地躺著雌贱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪偿短。 梳的紋絲不亂的頭發(fā)上欣孤,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音昔逗,去河邊找鬼降传。 笑死,一個(gè)胖子當(dāng)著我的面吹牛勾怒,可吹牛的內(nèi)容都是我干的婆排。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼笔链,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼段只!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鉴扫,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赞枕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后坪创,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炕婶,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年莱预,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柠掂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡依沮,死狀恐怖涯贞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悉抵,我是刑警寧澤肩狂,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布摘完,位于F島的核電站姥饰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏孝治。R本人自食惡果不足惜列粪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一审磁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岂座,春花似錦态蒂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鸳址,卻和暖如春瘩蚪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稿黍。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工疹瘦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巡球。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓言沐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親酣栈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子险胰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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