不知道你有沒有想過這樣一個(gè)問題
- 為什么我定義一個(gè)數(shù)組,它就有push晚凿、join亭罪、pop、shift等方法歼秽,我明明什么也沒寫坝σ邸?
- 為什么我定義一個(gè)函數(shù)哲银,它就有call扛吞、apply、length等屬性/方法荆责,我也什么都沒有做呀滥比?!
- 為什么我定義一個(gè)對(duì)象做院,它就有toString盲泛、valueOf等方法,我更是什么都沒有做呀键耕?寺滚!
1、我們先來說說對(duì)象屈雄。
當(dāng)我們定義一個(gè)對(duì)象時(shí)
var obj = {}
我們發(fā)現(xiàn)obj下面有一個(gè)屬性名叫
__proto__
村视,(它是個(gè)對(duì)象)obj.__proto__
里面又有很多屬性,包括 valueOf酒奶、toString蚁孔、constructor 等。當(dāng)我們需要找obj.valueOf
這個(gè)屬性時(shí)惋嚎,發(fā)現(xiàn)obj本身沒有沒有杠氢,那么它就會(huì)去查找obj.__proto__
是否有這個(gè)屬性,如果還沒有另伍,它去找obj.__proto__.__proto__
鼻百,直到找到這個(gè)屬性或null為止,在這個(gè)讀取屬性的過程中,是沿著__proto__
組成的鏈子來搜索的温艇,這個(gè)鏈子我們稱為原型鏈因悲。如果obj自身定義了一個(gè)valueOf屬性,那么它找到自身的valueOf之后就不再沿著
__proto__
來找中贝,因?yàn)橐呀?jīng)找到了囤捻,沒有必要繼續(xù)找了,也就是說
- 新增的屬性不會(huì)沿著
__proto__
查找 - 讀取屬性會(huì)沿著
__proto__
邻寿,直到找到這個(gè)屬性蝎土,或者是null為止。
2绣否、那么obj.__proto__
到底是什么呢誊涯?
__proto__
是一個(gè)簡(jiǎn)單的訪問器屬性,它總是指向它的構(gòu)造函數(shù)的prototype蒜撮。即原型對(duì)象暴构。
所有的對(duì)象都繼承了Object.prototype的屬性和方法,
它們可以被覆蓋(除了以null為原型的對(duì)象段磨,如 Object.create(null))取逾。
例如,新的構(gòu)造函數(shù)的原型覆蓋原來的構(gòu)造函數(shù)的原型苹支,提供它們自己的 toString() 方法.砾隅。對(duì)象的原型的改變會(huì)傳播到所有對(duì)象上,除非這些屬性和方法被其他對(duì)原型鏈更里層的改動(dòng)所覆蓋债蜜。
所有的對(duì)象會(huì)動(dòng)態(tài)生成一個(gè)__proto__
指向它構(gòu)造函數(shù)的原型(prototype )
當(dāng)我們?nèi)ゲ檎?code>obj.valueOf這個(gè)屬性時(shí)晴埂,他會(huì)沿著原型鏈去查找obj.__proto__.valueOf
,而obj.__proto__
指向obj.constructor.prototype
寻定。即
obj.__proto__ === obj.constructor.prototype // true
obj.__proto__.toString=== obj.constructor.prototype.toString // true
我們知道obj的構(gòu)造函數(shù)就是Object儒洛,那么我們也可以這么寫
obj.__proto__ === Object.prototype // true
obj.__proto__.toString === Object.prototype.toString // true
3、對(duì)于數(shù)組
以push方法為例狼速,我們知道當(dāng)我們定義一個(gè)空數(shù)組時(shí)琅锻,我們可以直接調(diào)用push方法,根據(jù)上面的解釋向胡,他會(huì)沿著這個(gè)數(shù)組的__proto__
去查找這個(gè)方法浅浮。
var array = []
array.push === array.__proto__.push // true
array.__proto__
指向它構(gòu)造函數(shù)的prototype,那么
array.__proto__.push === array.constructor.prototype.push // true
array.__proto__.push === Array.prototype.push // true
終于找到push方法了捷枯。
問題來了,我們知道array也是對(duì)象专执,那么JS是怎么知道array也是對(duì)象的呢淮捆?
答:通過__proto__
我們先看Array.__proto__
指向誰(shuí)
Array.__proto__ === Function.prototype // true
你也許會(huì)納悶,怎么Array.__proto__
指向的怎么是函數(shù)的原型對(duì)象呢?因?yàn)锳rray的構(gòu)造函數(shù)就是函數(shù)攀痊,不信你console.log(Array.constructor)試試桐腌?而函數(shù)的原型對(duì)象的__proto__
最終指向Object
Function.prototype.__proto__ === Object.prototype // true
Array.__proto__.__proto__ === Object.prototype // true
或者我們也可以這樣寫
Array.prototype.__proto__ === Object.prototype // true
最終歸宿都是Object。
至此我們看下一個(gè)小小的array都經(jīng)歷了什么
array.__proto__ ----> Array.prototype ----> Array.prototype.__proto__---->
Object.prototype
這樣也就不難理解為什么數(shù)組(函數(shù))也是個(gè)對(duì)象了苟径。
4案站、一切皆對(duì)象?
也許你會(huì)迷惑棘街,既然Array.__proto__.__proto__ = Object.prototype
蟆盐,那么
Number.__proto__.__proto__ === Object.prototype // true
Boolean.__proto__ .__proto__ === Object.prototype // true
String.__proto__.__proto__ === Object.prototype // true
為什么我們不可以說數(shù)字/布爾/字符串也是對(duì)象呢?
這要看這個(gè)數(shù)字/布爾/字符串是怎么創(chuàng)建的了遭殉。
以數(shù)字為例
var a = 1
var b = new Number(1)
我們知道基本類型是沒有屬性的石挂,即便可以訪問到這個(gè)屬性,也是訪問的臨時(shí)對(duì)象的屬性险污,訪問完就銷毀了痹愚,即使你發(fā)現(xiàn)a.__proto__.__proto__
指向是Object,也是new Number指向的蛔糯,跟a沒有半毛錢關(guān)系拯腮,因?yàn)閍就是個(gè)number。
b就不一樣了蚁飒,b是構(gòu)造函數(shù)Number構(gòu)造出來的一個(gè)對(duì)象动壤,只不過他的值是1,它可是有__proto__
屬性的飒箭,那么b就可以愉快的指來指去了狼电。
b.__proto__.__proto__ === Object.prototype // true
所以a是一個(gè)number,而b是一個(gè)object弦蹂。
同理字符串和布爾也是如此肩碟。
來跟我一起大聲念JS的七種數(shù)據(jù)類型:
number , string , boolean , undefined , null , object , symbol
說JS一切皆對(duì)象的,你們當(dāng)其他類型是吃干飯的么凸椿?