摘要:的過程新生成了一個對象鏈接到原型綁定返回新對象在調(diào)用的過程中會發(fā)生以上四件事情,我們也可以試著來自己實(shí)現(xiàn)一個創(chuàng)建一個空的對象獲得構(gòu)造函數(shù)鏈接到原型綁定,執(zhí)行構(gòu)造函數(shù)確保出來的是個對象對于實(shí)例對象來說,都是通過產(chǎn)生的,無論是還是。
prototype
首先來介紹下 prototype 屬性。這是一個顯式原型屬性,只有函數(shù)才擁有該屬性?;旧纤泻瘮?shù)都有這個屬性,但是也有一個例外
let fun = Function.prototype.bind()
如果你以上述方法創(chuàng)建一個函數(shù),那么可以發(fā)現(xiàn)這個函數(shù)是不具有 prototype 屬性的。
prototype 如何產(chǎn)生的
當(dāng)我們聲明一個函數(shù)時,這個屬性就被自動創(chuàng)建了
function Foo() {}
并且這個屬性的值是一個對象(也就是原型),只有一個屬性 constructor
constructor 對應(yīng)著構(gòu)造函數(shù),也就是 Foo。
constructor
constructor 是一個公有且不可枚舉的屬性。一旦我們改變了函數(shù)的 prototype ,那么新對象就沒有這個屬性了(當(dāng)然可以通過原型鏈取到 constructor)。
那么你肯定也有一個疑問,這個屬性到底有什么用呢?其實(shí)這個屬性可以說是一個歷史遺留問題,在大部分情況下是沒用的,在我的理解里,我認(rèn)為他有兩個作用:
1.讓實(shí)例對象知道是什么函數(shù)構(gòu)造了它
2.如果想給某些類庫中的構(gòu)造函數(shù)增加一些自定義的方法,就可以通過 xx.constructor.method 來擴(kuò)展
這是每個對象都有的隱式原型屬性,指向了創(chuàng)建該對象的構(gòu)造函數(shù)的原型。其實(shí)這個屬性指向了 [[prototype]],但是 [[prototype]] 是內(nèi)部屬性,我們并不能訪問到,所以使用 __proto__ 來訪問。
因?yàn)樵?JS 中是沒有類的概念的,為了實(shí)現(xiàn)類似繼承的方式,通過 __proto__ 將對象和原型聯(lián)系起來組成原型鏈,得以讓對象可以訪問到不屬于自己的屬性。
實(shí)例對象的 __proto__ 如何產(chǎn)生的
從上圖可知,當(dāng)我們使用 new 操作符時,生成的實(shí)例對象擁有了 __proto__屬性。
function Foo() {} // 這個函數(shù)是 Function 的實(shí)例對象 // function 就是一個語法糖 // 內(nèi)部調(diào)用了 new Function(...)
所以可以說,在 new 的過程中,新對象被添加了 __proto__ 并且鏈接到構(gòu)造函數(shù)的原型上。
new 的過程
1.新生成了一個對象
2.鏈接到原型
3.綁定 this
4.返回新對象
在調(diào)用 new 的過程中會發(fā)生以上四件事情,我們也可以試著來自己實(shí)現(xiàn)一個 new
function create() { // 創(chuàng)建一個空的對象 let obj = new Object() // 獲得構(gòu)造函數(shù) let Con = [].shift.call(arguments) // 鏈接到原型 obj.__proto__ = Con.prototype // 綁定 this,執(zhí)行構(gòu)造函數(shù) let result = Con.apply(obj, arguments) // 確保 new 出來的是個對象 return typeof result === "object" ? result : obj }
對于實(shí)例對象來說,都是通過 new 產(chǎn)生的,無論是 function Foo() 還是 let a = { b : 1 } 。
對于創(chuàng)建一個對象來說,更推薦使用字面量的方式創(chuàng)建對象。因?yàn)槟闶褂?new Object() 的方式創(chuàng)建對象需要通過作用域鏈一層層找到 Object,但是你使用字面量的方式就沒這個問題。
function Foo() {} // function 就是個語法糖 // 內(nèi)部等同于 new Function() let a = { b: 1 } // 這個字面量內(nèi)部也是使用了 new Object()Function.__proto__ === Function.prototype
對于對象來說,xx.__proto__.contrcutor 是該對象的構(gòu)造函數(shù),但是在圖中我們可以發(fā)現(xiàn) Function.__proto__ === Function.prototype,難道這代表著 Function 自己產(chǎn)生了自己?
答案肯定是否認(rèn)的,要說明這個問題我們先從 Object 說起。
從圖中我們可以發(fā)現(xiàn),所有對象都可以通過原型鏈最終找到 Object.prototype ,雖然 Object.prototype 也是一個對象,但是這個對象卻不是 Object 創(chuàng)造的,而是引擎自己創(chuàng)建了 Object.prototype 。所以可以這樣說,所有實(shí)例都是對象,但是對象不一定都是實(shí)例。
接下來我們來看 Function.prototype 這個特殊的對象,如果你在瀏覽器將這個對象打印出來,會發(fā)現(xiàn)這個對象其實(shí)是一個函數(shù)。
我們知道函數(shù)都是通過 new Function() 生成的,難道 Function.prototype 也是通過 new Function() 產(chǎn)生的嗎?答案也是否定的,這個函數(shù)也是引擎自己創(chuàng)建的。首先引擎創(chuàng)建了 Object.prototype ,然后創(chuàng)建了 Function.prototype ,并且通過 __proto__ 將兩者聯(lián)系了起來。這里也很好的解釋了上面的一個問題,為什么 let fun = Function.prototype.bind() 沒有 prototype 屬性。因?yàn)?Function.prototype 是引擎創(chuàng)建出來的對象,引擎認(rèn)為不需要給這個對象添加 prototype 屬性。
所以我們又可以得出一個結(jié)論,不是所有函數(shù)都是 new Function() 產(chǎn)生的。
有了 Function.prototype 以后才有了 function Function() ,然后其他的構(gòu)造函數(shù)都是 function Function() 生成的。
現(xiàn)在可以來解釋 Function.__proto__ === Function.prototype 這個問題了。因?yàn)橄扔械?Function.prototype 以后才有的 function Function() ,所以也就不存在雞生蛋蛋生雞的悖論問題了。對于為什么 Function.__proto__ 會等于 Function.prototype ,個人的理解是:其他所有的構(gòu)造函數(shù)都可以通過原型鏈找到 Function.prototype ,并且 function Function() 本質(zhì)也是一個函數(shù),為了不產(chǎn)生混亂就將 function Function() 的 __proto__ 聯(lián)系到了 Function.prototype 上。
總結(jié)Object 是所有對象的爸爸,所有對象都可以通過 __proto__ 找到它
Function 是所有函數(shù)的爸爸,所有函數(shù)都可以通過 __proto__ 找到它
Function.prototype 和 Object.prototype 是兩個特殊的對象,他們由引擎來創(chuàng)建
除了以上兩個特殊對象,其他對象都是通過構(gòu)造器 new 出來的
函數(shù)的 prototype 是一個對象,也就是原型
對象的 __proto__ 指向原型, __proto__ 將對象和原型連接起來組成了原型鏈
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98259.html
摘要:使用指定的參數(shù)調(diào)用構(gòu)造函數(shù),并將綁定到新創(chuàng)建的對象。由構(gòu)造函數(shù)返回的對象就是表達(dá)式的結(jié)果。情況返回以外的基本類型實(shí)例中只能訪問到構(gòu)造函數(shù)中的屬性,和情況完全相反,結(jié)果相當(dāng)于沒有返回值。 定義 new 運(yùn)算符創(chuàng)建一個用戶定義的對象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對象的實(shí)例。 ——(來自于MDN) 舉個栗子 function Car(color) { this.color = co...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:忍者級別的函數(shù)操作對于什么是匿名函數(shù),這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數(shù)是一個很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...
摘要:前言秋招宣告結(jié)束,面試了接近家公司,有幸拿到,感謝這段時間一起找工作面試的朋友和陪伴我的人。一定要提前準(zhǔn)備好,不然面試官叫你說遇到的難點(diǎn),或者直接問問題時可能會懵逼。 前言 秋招宣告結(jié)束,面試了接近20家公司,有幸拿到offer,感謝這段時間一起找工作面試的朋友和陪伴我的人。這是一段難忘的經(jīng)歷,相信不亞于當(dāng)年的高考吧,也許現(xiàn)在想起來高考不算什么,也許只有經(jīng)歷過秋招的人才懂得找工作的艱辛...
摘要:前言秋招宣告結(jié)束,面試了接近家公司,有幸拿到,感謝這段時間一起找工作面試的朋友和陪伴我的人。一定要提前準(zhǔn)備好,不然面試官叫你說遇到的難點(diǎn),或者直接問問題時可能會懵逼。 前言 秋招宣告結(jié)束,面試了接近20家公司,有幸拿到offer,感謝這段時間一起找工作面試的朋友和陪伴我的人。這是一段難忘的經(jīng)歷,相信不亞于當(dāng)年的高考吧,也許現(xiàn)在想起來高考不算什么,也許只有經(jīng)歷過秋招的人才懂得找工作的艱辛...
閱讀 2012·2019-08-29 16:27
閱讀 1382·2019-08-29 16:14
閱讀 3383·2019-08-29 14:18
閱讀 3468·2019-08-29 13:56
閱讀 1263·2019-08-29 11:13
閱讀 2136·2019-08-28 18:19
閱讀 3454·2019-08-27 10:57
閱讀 2289·2019-08-26 11:39