以前的文章地址:
2018年3月面試心得《跨域問題》
2018年3月面試心得《上下文,做用域》前端
想了半天,我仍是對原型下手了。
近期面試的小夥伴們確定也是面臨着很多原型的問題。es6
「請你簡單描述一下原型。」
——原型就是爸爸,原型鏈就是爸爸的基因。
「如何實現繼承?」 ——es6的extends。 「es5呢?」
——………(臉上笑嘻嘻,內心fuuuuuuuuuuuck!!!)
我想應該有很多轉專業到前端來的小夥伴跟我同樣,基礎弱的一筆,抓了幾個面試題刷了刷,完事英勇的衝了出去,昂首挺胸的被問幾句,答上來了自信心爆表,可是面試官再問細一點,立馬就蔫巴了~
不得不說,大廠都會抓着你基礎死勁問,由於他們認爲基礎決定一切。(他們是否是這麼認爲的我也不知道,可是給我感受就是這樣……)
接下來,咱們要好好的來鞏固一下原型的基礎啦。面試
// 我先個你一個兒子的設計圖 function 兒子(name, age, isGay) { this.name = name; this.age = age; this.isGay = true; } // 如今我要開始造兒子了!!! var myson = new 兒子('小明', 50, false) // 如今打印一下咱們的myson //{name: "小明", age: 50, isGay: true} // 臥槽怎麼回事,我兒子怎麼是個gay!不行! myson.isGay = false // 好了這下舒坦了 嘿嘿嘿嘿
上面的這個就是構造函數,咱們也能夠叫他爲兒子生成器,或者圖紙。
咱們拿着圖紙去生成不一樣的兒子,兒子你又能夠給他新的屬性等等~你能夠diy~
是否是頗有意思。
好了咱們進入正題。segmentfault
JavaScript中萬物皆對象,但對象之間也是有區別的。分爲普通對象和函數對象。跨域
咱們大前端是不怕單身的,沒對象能夠new一個。
在看代碼的時候,常常會看到這些東西。函數
var a = new Object; var b = new funxtion() {……}
這就是被咱們new出來的對象。
很明顯,上面的是普通對象,下面的是函數對象。性能
普通對象若是加上了prototype的屬性,那麼就變成了原型對象。
每一個函數對象建立的時候,都會自帶一個prototype的屬性,這個屬性至關於一個指針,指向他自己的原型對象,這個原型對象裏包含着自定義的方法屬性。學習
好了,讓咱們來實踐一下,首先咱們要作一隻貓咪和一隻狗狗。this
var miao; miao.prototype = { name = "大蛋蛋", age = 1 } function dog(name, age){ this.name = name, this.age = age } // 如今咱們把貓狗的圖紙畫好了,上面是原型對象的方式,下面是函數對象,接下來咱們就能夠來建立一些貓貓狗狗了。
昨晚在我寫到這裏的時候,去看了一本書,上面寫着其實js的繼承,其實並不該該叫繼承。
在其餘語言中,繼承是屬於複製。es5
我創造了一個爸爸,那麼繼承就是我複製了不少爸爸出來,每一個爸爸都同樣的。
固然你也能夠給你創造的每一個爸爸一些單獨的屬性,這個爸爸會作飯,那個爸爸會釣魚。
在you don`t know js裏面的第五章寫着:
原本設計js語言的時候並無想將它作的太過複雜。 經過new的函數調用並無創造關聯,這個關聯只是一個**意外的反作用**。
這個觀點我目前還不是太清楚對錯,可是咱們能夠暫時先這麼理解一下。
若是說其餘語言的繼承,就是複製的話,你創造一個爸爸圖紙,能夠做出不一樣的爸爸。
那麼js的理解應該就是,你創造一個爸爸,而且生出一堆兒子,每一個兒子都 能夠擁有爸爸給孩子的東西,例如釣魚、作飯(爸爸的方法)。爲何會擁有他的方法呢?
由於在兒子尚未學習這個方法的時候,咱們班主任讓兒子去釣魚,那麼兒子不會,就會向上查找去叫爸爸,若是爸爸有這個技能,咱們就能夠成功完成任務了。
如今咱們來用作簡單的語言來闡述一下原型和原型鏈。
什麼是原型?那就是爸爸。
什麼是原型鏈?那就是爸爸和兒子的關係。
不知道這麼解釋會不會有點牽強,不過這確實能夠有助於咱們去實踐下一步。
咱們仍是簡單去作一個爸爸吧。
function 爸爸(name) { // 如今我給爸爸丟上一些方法 // 給孩子命名 this.name = name; // 爸爸願意交給兒子能夠釣上來你想要的魚 this.goFishing = function(fis) { return fis } } // 如今咱們來創造兒子 var 兒子小明 = new 爸爸('小明') 兒子小明.name //"小明" 兒子小明.goFishing('章魚') //"章魚" // 爸爸沒有直接較 爸爸.prototype.cook = function () { ['米飯', '爆炒魷魚', '番茄蛋湯'].forEach(i => console.log(i)) } 兒子小明.cook() // 米飯 爆炒魷魚 番茄蛋湯
如今咱們打印一下小明兒子。
兒子裏面有:{name: "小明", goFishing: ƒ}
這是爸爸直接給兒子的東西。
那麼爸爸還沒給兒子,兒子卻可使用的到哪裏去了呢?
咱們往下再看,有一個__proto__。
在這裏面,就能發現爸爸的cook。
因此爸爸的獨門絕技丟在了prototype裏面,這裏面是兒子不須要學習卻能夠直接使用的獨門絕技~
這個又是什麼呢?
其實在剛剛咱們打印兒子小明的時候,在cook下面,有一個constructor的東西,就是這玩意。
仍是剛剛的代碼,如今跟着我一塊兒打印一下兒子小明.constructor。
如今直接把爸爸打印出來了。
這個怎麼理解呢……
我找到了一句話。
在默認狀況下,全部的原型對象都會自動得到一個 constructor(構造函數)屬性,這個屬性(是一個指針)指向 prototype 屬性所在的函數(Person)
好了,就按照這句話這麼理解的話,咱們能夠理解爲:
全部的兒子都會有一個constructor的屬性,這個屬性指着能夠獲取爸爸獨門絕技(prototype)的那個爸爸。
如今我叫他找爸爸屬性……
如今咱們再建立一個女兒
var 女兒小花 = new 爸爸('小花') 兒子小明.constructor === 女兒小花.constructor // ture
那咱們用了找爸爸屬性,因此小明和小花的爸爸是同一個爸爸~
如今咱們再仔細看一下剛剛打印出來的小明。
goFishing:ƒ (fis) name:"小明" __proto__:{ cook:ƒ () constructor:ƒ 爸爸(name) __proto__:Object }
仔細看,constructor在哪裏~
大聲告訴我在哪裏~
在__proto__裏面對吧。
因此這個找爸爸的屬性是從爸爸那裏帶過來的,是爸爸告訴你怎麼找爸爸,而且沒有實際教會你怎麼找爸爸,是直接調用爸爸的獨門絕技找的爸爸~
如今咱們再來往下科普一下其餘的幾個數據類型,再研究其原型。
它是 JavaScript 語言的第七種數據類型,前六種是:undefined、null、布爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)。
還有ES6 引入了一種新的原始數據類型Symbol,表示獨一無二的值。
咱們如今先來玩一下數字。
var 數字 = 12; // 他是否有爸爸呢? console.log(數字.__proto__) // 跟着我敲敲看。 var 數字1 = new Number(12) 數字 == 數字1 // true 數字 === 數字1 // false
在打印數字的proto的時候,瞧咱們看到了什麼!
Number!
這就是他爸爸!
es6:
Class 能夠經過 extends關鍵字實現繼承,這比 ES5 的經過修改原型鏈實現繼承,要清晰和方便不少。
子類必須在constructor方法中調用 super方法,不然新建實例時會 報錯。這是由於子類 沒有本身的this對象,而是 繼承父類的this對象,而後對其進行加工。若是不調用super方法,子類就 得不到this對象。
es5:
方法一:
子.prototype = new 爸爸()新建一個爸爸做爲孩子的獨門絕技~
可是要想爲兒子新增屬性和方法,必需要在new 爸爸()這樣的語句以後執行,不能放到構造器中……
來自原型對象的引用屬性是全部實例共享的,建立子類實例時,沒法向父類構造函數傳參方法二:
function 兒子(name){ 父親.call(this); this.name = name || 'Tom'; }可是這樣的話,每一個兒子都要綁定爸爸的實例函數的副本,影響性能
方法三:
兒子.prototype = Object.create(爸爸.prototype) 創造一個新爸爸的獨門絕技來當兒子的獨門絕技~其實方法有不少,我就不一一舉例了。
若是對文章有什麼間接,歡迎悄悄告訴我~ =333333=
麼麼噠~上面我還寫了數字和數字1,猜猜看我下一章寫的是啥~~~