從初學JS就有比較片面的學法薯蝎,比較定死的一些概念啊导而,結論之類的,只是為了學習每個東東的各自的基本作用的管挟,不需要糾結它是怎么來的轿曙,只需要知道它能干嗎就可以的,小學里最小的數(shù)是0僻孝,對的导帝。初中就是錯的。為什么穿铆,不是因為絕對的錯了您单,是因為應用環(huán)境錯的,平臺換了哦荞雏。說這些虐秦,意思就是看山不是山的時候到了。
JS的數(shù)據(jù)類型凤优,數(shù)字悦陋,字符串,布爾筑辨,對象俺驶,null,undifined。然后,我們用typeof去檢驗,嗯有些歷史遺留的問題帆卓,函數(shù)怎么是對象,為啥檢測是function?null為啥是對象送矩?需要跟它們深入地談談人生啦。
對象
什么哪替?你個攻城獅栋荸,竟然還么有對象?丟人不,new一個出來晌块,來來來爱沟,,匆背,
對象是什么玩意兒呼伸?
我是非科班出身,連c都沒學過钝尸,我只寫自己的理解和被洗腦后的感悟——對象括享,就是我要針對的目標,這個目標在JS里啊珍促,是數(shù)據(jù)和數(shù)據(jù)屬性铃辖,數(shù)據(jù)方法這些東東臃腫地一個封裝集合。別看我平時用的時候猪叙,一個標簽可以表現(xiàn)它的存在感娇斩,它只是那個標簽嗎?為啥我可以設置它的屬性穴翩,它的內容犬第,它的事件,大小藏否,它的一些操作方法function瓶殃。包括數(shù)組充包,內置的方法副签,遍歷等,說到函數(shù)基矮,函數(shù)為什么聲明前置淆储,為什么會有作用域,這不是屬性規(guī)則嗎家浇?包括連字符串本砰,數(shù)字不是都有屬性方法嗎?
對钢悲,有句話說点额,JS里萬物基于對象!因為我們把各個研究對象內置了屬性和方法莺琳,便于操作还棱,否則,單單只是變量惭等,要操作的話珍手,每次都要寫個函數(shù),聲明個變量作為它的屬性,再操作琳要,寡具,,煩不煩啊稚补,郭德綱說過童叠,你死不死啊,课幕,拯钻,
對,我們學JS時撰豺,對象淺顯地理解為key-value的值的合體粪般。不需要研究太深,還是那句話污桦,越實在暴露出來的亩歹,越是最基本的,這里的對象是面向對象凡橱,就是目標數(shù)據(jù)和這目標的規(guī)定的變量包括內置的參數(shù)我們叫屬性小作,長寬高等等吧,以及對這數(shù)據(jù)的操作方法了稼钩,這是改變你世界觀的一個意思顾稀,不要太膚淺,要知道一件事的源頭坝撑,這樣更有利于干什么静秆?有利于我們學習JS的設計思想和攻城獅的本身品質。雖然我沒學到前端工程化巡李,但是我就覺得完成對對象的封裝就有工程化的感覺了抚笔,而且確實會有很多福利,比如有些內置的方法只在某些對象身上侨拦,我想調用這個方法殊橙,否則我還要自己寫,但是數(shù)據(jù)不是符合的狱从,怎么辦膨蛮?那必須要可以啊,否則這語言也太low了季研,效率太低了敞葛。下面會講到的。
思路就是训貌,把功能集成一個整體制肮,不去考慮內部功能怎么實現(xiàn)的冒窍,會調用就行了,更簡潔豺鼻,可控综液。
構造對象
字面量的模式就是不能復用代碼。復用代碼怎么辦儒飒?
function createobj(ni,age){
var obj = {
ni:ni,
age:age,
printName:function(){
console.log(this.ni)
}
}
return obj
}
var obj1 = createobj('haha',26)
obj1.printName() //'haha'
我的理想很豐滿谬莹,如果給我一個房子的戶型圖,我能做出一百間這樣類的房子桩了,現(xiàn)實附帽,嗯也是可以的。
構造函數(shù):
function people(name,age){
this.name = name
this.age = age
console.log(this)
}
people('haha',26) //haha
全局變量相當于window的屬性井誉,這個函數(shù)聲明是全局變量蕉扮,
this就是指window。不是我想要的結果啊颗圣。
如何調用喳钟?
var obj1 = new people('li',20)
var obj2 = new people('wu',20)
都是people類的對象了。
new 的過程
new運算符接受一個函數(shù)F和其參數(shù):new F(arguments...)在岂。三步:
1奔则、創(chuàng)建類的實例,把一個空的對象的proto屬性設為F.prototype蔽午。
2易茬、 初始化實例,F(xiàn)被傳入?yún)?shù)并調用及老,關鍵字this被設為該實例抽莱。
3、返回實例写半。
obj1是空對象岸蜗,啥都沒有尉咕,它的proto指向這個函數(shù)聲明時的prototype屬性指向的那個對象叠蝇。它再執(zhí)行這個people函數(shù),執(zhí)行時年缎,this指向obj1這個空對象悔捶。而函數(shù)執(zhí)行時,給this賦值单芜,就是給obj1增加屬性蜕该,增加好了,就返回這個已經(jīng)不是空的對象給obj1洲鸠,所以obj1就是那個對象有三個屬性堂淡。
function people(name,age){
//var this ={}
this.name = name
this.age = age
//return this
}
如果是要阻礙一下流程:
function people(name,age){
//var this ={}
this.name = name
this.age = age
//return 2 //改了后馋缅,沒有影響,沒阻礙绢淀,基本類型萤悴,瀏覽器默認無效
}
如果是,皆的,覆履,
function people(name,age){
//var this ={}
this.name = name
this.age = age
//return {a:1} //這就有問題了。因為都是對象费薄,就覆蓋了
}
不需要return的
instanceof
操作符硝全,判斷對象是否為某個類型的實例。
obj1 instanceof people
//true
obj2 instanceof people
//true
現(xiàn)在先說一句楞抡,任何的一個對象怎么來的伟众,它的屬性方法怎么來的,都是由一個函數(shù)創(chuàng)造出來的召廷,對象有個屬性叫constructor赂鲤。
現(xiàn)在構造函數(shù),可以解決我們創(chuàng)建復用的難題了柱恤,但是里面的那個prototype是什么鬼数初?梗顺?
任何函數(shù)只要聲明了泡孩,它就有一個prototype屬性,這個屬性的值對應到了一個對象寺谤,這個對象里有一個constructor,它指向這個聲明的函數(shù)仑鸥,還有一個proto。
有什么用呢变屁?既然是對象眼俊,我們就可以寫入方法屬性,改成自己需要的圖紙粟关,再new出來一堆房子疮胖。這些房子都有一個屬性proto,它也指向了圖紙的prototype指向的那個對象,公用的闷板,一般函數(shù)公用最好了澎灸。
例子:
function people(name,age){
this.name = name
this.age = age
console.log(this.name)
}
people('haha',26)
people.prototype.sayage = function(){
console.log('hahaha')
}
people.prototype.sayage()
var obj1 = new people('li',20)
var obj2 = new people('wu',20)
obj1.__proto__.sayage()
如圖:
一般,不會這樣寫上proto的遮晚,因為直接寫成obj1.sayage就可以的性昭,因為它會先從自身屬性找,找不到就從proto找县遣。
經(jīng)驗之談:
所有的實例都可以訪問那個prototype,prototype是公用的糜颠,可以把公用的方法屬性放到它里面汹族。
prototype是函數(shù)的屬性,只要聲明就有其兴。
__proto__是函數(shù)創(chuàng)建對象的時候鞠抑,對象上有它。
對于基本類型是對象的說法
基本類型也是對象忌警,因為有自己的屬性方法的搁拙,但是我們作為應用層次,要嚴謹法绵,要認為不是對象才去應用箕速,因為不能隨便給它增加刪除屬性方法,加了有時候也起不了作用的朋譬,起不了作用盐茎,也就不是對象了,看問題是相對的徙赢。
下面原型就來了字柠,,狡赐,
對象與原型
刷完對象的世界觀窑业,我們來想想那些開發(fā)JS的攻城獅們的套路啊,他們是設計了一個個的對象封裝了枕屉,比如數(shù)組有個數(shù)組的封裝組件常柄,集成了所有數(shù)組的規(guī)則和應用基本方法,他們是開發(fā)人員搀擂,我們是應用層次啊西潘,他們想讓我們效率更高更好,比如哨颂,我們var了一個[],就能操作它喷市,用push,shift等等方法,還可以查它的length,用下標找到對應的值威恼,品姓,,多方便沃测!我沒有自己去寫push的函數(shù)吧缭黔,我不用關心怎么輸入一個[].length就可以知道它的長度了。我會用就行了〉倨疲現(xiàn)在我們要做什么?
為啥嘞别渔?這里的邏輯是什么附迷?
我們只是聲明了某個類型的變量惧互,然后這個變量就有這個類型的屬性和方法了,并且根據(jù)JS的設計尿性喇伯,屬性可以設置喊儡,那就可以創(chuàng)造,包括方法稻据。
在操作DOM時用的太多了艾猜,事實是必須的,之前寫項目時捻悯,用封裝匆赃,寫成對象那樣,里面的創(chuàng)造的很多變量就是對象的屬性嘛今缚。那我們可以創(chuàng)造對象的方法么算柳?告訴你沒毛病姓言!
這個中間肯定是有些規(guī)則瞬项,有些設定好的套路的,好比何荚,規(guī)定[]就是數(shù)組囱淋。
我們先驗證一下,
找到arr的constructor是f Array(),然后打開這個函數(shù)的prototype跟數(shù)組的proto比較一樣嗎餐塘。
所以這個arr的方法源于它祖宗的prototype指向的對象绎橘。
首先,開發(fā)人員們肯定根據(jù)數(shù)據(jù)類型集成對象唠倦,不會為了一個數(shù)組開發(fā)成兩個三個多個面向數(shù)組的對象的称鳞,到時候用哪個,并且集成了稠鼻,所有聲明的數(shù)組都可以用冈止,為什么非要分情況?而且都是只要是數(shù)組候齿,你就可以擁有一堆同樣的方法屬性熙暴。這里我把這個源頭的封裝的對象叫它們祖宗。那這祖宗跟它后代如何查到互相關聯(lián)慌盯?這里就要引入原型了周霉!
原型
原型的思想就好比是模具,后面生產的產品跟它差不多亚皂,至少模具有的俱箱,產品都有,至于產品要不要深加工再出現(xiàn)模具沒有的或者改變些神馬灭必,跟模具沒關系了狞谱,關鍵是現(xiàn)在產品已經(jīng)有了模具的特性了乃摹,不用我再手把手從頭做起了。
有大神跟衅,在這里引入了class的概念孵睬,類概念!類就是對象的抽象描述伶跷,對象是類的實例掰读,反正那個祖宗,我們是看不到的叭莫,只能看到同類的后代怎樣怎樣蹈集。正好可以引出繼承這一概念了,其實一說繼承食寡,就有父子關系了雾狈,可以說是子類繼承父類的。在代碼層次抵皱,就是新生產出的變量的方法和屬性的集合的那個棧的指針指向哪里善榛,指針從哪里賦值得到的。
arr.valueOf()在Array.prototype里沒有啊呻畸,可是沒報錯啊移盆,,伤为,
再看看Array函數(shù)的proto里看看
規(guī)定父對象也可以成為子對象的原型叙甸。每個對象都有一個原型,每個原型也是對象位衩,也有自己的原型裆蒸,從而形成原型鏈。
因為訪問對象的屬性或者方法時糖驴,在自己身上沒找到僚祷,就去自己的(父親)原型prototype上找,再找不到贮缕,再去(父親)原型的proto上找辙谜,再找不到,再去父親的原型(爺爺)的prototype找感昼,再找不到就到爺爺?shù)?strong>proto装哆,,,順著原型鏈攀爬烂琴,直到找到或者到它們祖宗也沒有爹殊。
規(guī)定:
1 js是基于原型的語言中的prototype
2 每個對象都有自己的原型中的prototype
3 每個函數(shù)都有一個原型屬性中的prototype
prototype是什么蜕乡?上面說過了奸绷,現(xiàn)在再直白點,創(chuàng)造者有prototype,指向一個對象层玲。
生成者的proto的指針復制了創(chuàng)造者的prototype的內容号醉。
因為身份不一樣,就算是同樣的內容叫法也不一樣辛块,為了一個邏輯性畔派。
第一句不管,太底層了润绵,沒興趣线椰。回想當初學的時候尘盼,自己的追本溯源憨愉,有種成就感,我當時問老師卿捎,所有的源頭配紫,包括對象的設定,都是因為語言環(huán)境提供運行支撐的吧午阵?躺孝?
第三句,看圖底桂,函數(shù)也有一堆內置屬性的植袍。函數(shù)名字叫什么?對吧籽懦!把函數(shù)歸為面向對象研究嘛于个,就當成對象得了。因為函數(shù)聲明你也知道猫十,那函數(shù)是怎么造出來的览濒?直說結果,我也不知道過程拖云,就是對象造出來的贷笛。
這里就要有個方法了,如何找到自己的原型宙项?
首先乏苦,我們根據(jù)邏輯其實知道了,arr是Array創(chuàng)造的,所以汇荐,規(guī)定了
arr.proto === Array.prototype洞就。
所以這是判斷原型的一個依據(jù)的。再說一個事實掀淘,是誰創(chuàng)造了Array函數(shù)呢旬蟋?是Object這個函數(shù)。
那如何判斷Object是不是arr的原型革娄?思路是先看arr的proto是不是等于Object的prototype倾贰,不等于就看父輩的proto,還不等拦惋,就繼續(xù)看爺爺輩的proto匆浙,,厕妖,只要有一級相等首尼,那就是,一般找到Object.prototype.proto ===null,這是終點了言秸,Object.prototype指向的對象是個特殊對象软能,里面沒有proto。這就是instanceof的判斷邏輯的井仰。
神附加題:
function people(){}
var p = new people()
p.__proto__ === people.prototype //true,不解釋
people.__proto__ === Function.prototype // true ,因為函數(shù)的父親是Function
(people.prototype).__proto__ === Object.prototype //true ,因為Object創(chuàng)造了所有基本對象埋嵌,
//左邊的意思就是people函數(shù)的prototype指向的那個對象的父親的prototype。
Function.prototype === Function._proto_ //true 俱恶,F(xiàn)unction的prototype指向了它生成的對象的 _proto_ 雹嗦,
//那這里,意思是Function是不是自己創(chuàng)建的合是,是的了罪,如下圖,聪全,泊藕,
Function.prototype === Object._proto_ //ture ,Object的父親是不是Function?Object也是個函數(shù)难礼,娃圆,,
Function.prototype._proto_ === Object.prototype //true ,Function.prototype指向的對象的父親的prototype蛾茉。
Function instanceof Object //true ,
就是判斷Function.__proto__ === Object.prototype,因為Function._proto_ ===Function.prototype讼呢,
而Function.prototype._proto_ === Object._proto_
Function._proto_ ._proto_=== Object._proto_ ,Function._proto_ 指向了對象。
經(jīng)驗之談:
當new一個函數(shù)時谦炬,會創(chuàng)建一個對象悦屏,函數(shù)的prototype === 這個對象的proto节沦。
一切函數(shù)由Function創(chuàng)建,包括它自己础爬。
一切函數(shù)的原型對象都是由Object這個函數(shù)創(chuàng)建的甫贯。fn.prototype.proto ===Object.prototype
創(chuàng)造者是作為函數(shù)看待,有prototype,生成者被當成對象看待看蚜,有proto
其實這里憑感覺都可以知道的叫搁,只不過有些別扭的是自己生產自己的,這個其實很容易理解失乾,一個變量自己還能自增常熙,自增了自己還是自己纬乍。