個人JS體系整理(一) 一. 原型 JS每聲明一個Function,都有Prototype原型,Prototype原型是函數的一個預設屬性,在函數的創建過程中由JS編譯器自動添加,也就是說每當生產一個Function對象的時候,就有一個原型Prototype。按照Javascript的說法,Func ...
個人JS體系整理(一)
一. 原型
JS每聲明一個Function,都有Prototype原型,Prototype原型是函數的一個預設屬性,在函數的創建過程中由JS編譯器自動添加,也就是說每當生產一個Function對象的時候,就有一個原型Prototype。按照Javascript的說法,Function定義的Object(對象),是一個很特殊的對象,這個使用Function定義的對象與使用New操作符生成的對象之間有一個重要的區別,這個區別就是Function定義的對象有一個Prototype屬性,稱之為函數對象,而使用New生成的對象就沒有這個Prototype屬性,稱之為普通對象。
圖1.1
如上圖所示,第一個Console結果為Undifined,證明瞭New的對象是沒有原型Prototype的;第二個Console結果為增加原型Name以後的原函數,證明瞭只有函數對象才具有原型Prototype;第三個Console的結果為’hangzhou’,證明瞭通過原型增加的屬性是存在的;第四個Console的結果為’hangzhou’,證明瞭通過原型增加的屬性,是可以在該對象所具有的方法裡面進行調用獲取到的。
圖1.2
如上圖所示,如果原函數本身的屬性或方法與利用原型增加的屬性或方法重名時,預設調用的依舊是該函數對象本身的屬性或者方法,即函數本身的屬性或者方法的優先順序高於原型的屬性或者方法。
二. 原型鏈
官方對於原型鏈的解釋是原型鏈是實現繼承的主要方法,其基本思想是利用原型讓一個引用類型繼承另一個應用類型的屬性和方法。通俗一些理解,每個對象都會在其內部初始化一個屬性,就是Prototype(原型),當我們訪問一個對象的屬性時, 如果這個對象內部不存在這個屬性,那麼他就會去Prototype里找這個屬性,這個Prototype又會有自己的Prototype, 於是就這樣一直找下去,也就是我們平時所說的原型鏈的概念。
提到原型鏈,就要提一個詞_proto_,它是基本對象的屬性,相對應的就是每一個函數對象都有一個自己的Prototype原型,由於函數對象也屬於基本對象,所以函數對象也有_proto_,每當去定義一個Prototype的時候,就相當於把該實例的__proto__指向一個結構體,那麼這個被指向結構體就稱為該實例的原型。
圖2.1
當你定義一個函數對象的時候,其內部就有這樣一個鏈表關係。聲明foo對象,自帶了_proto_的屬性,而這個屬性指向了Prototype,從而實現對象的擴展(例如繼承等操作)。
圖2.2
如上圖所示,一個沒有繼承操作的函數的_proto_都會指向Object.Prototype,而Object.Prototype都會指向Null。
圖2.3
如上圖所示,定義兩個函數對象A和B,每個對象分別具有一個屬性和方法。他們兩個函數對象的區別是B繼承了A,而繼承是通過創建A的實例,並將實例賦給B.Prototype實現的。實現的本質是重寫原型的對象,代之以一個新的類型的實例。換句話說,原來存在於A的實例中的所有屬性和方法,現在也存在於B.Prototype中了。在確立了繼承關係之後,我們給B.Prototype添加了一個方法,這樣就繼承A的屬性和方法的基礎上又添加了一個新方法。然後繼續創建一個B的實例C,這樣C就具有了B的全部方法和屬性,所以Console的結果分別是111與222。
前端面試拓展:
圖2.4
p沒有b屬性,會一直通過__proto__向上查找,最後當查找到Object.Prototype時找到,最後列印出b,向上查找過程中,得到的是Object.Prototype,而不是Function.Prototype,所以找不到a屬性,所以結果為Undefined,這就是原型鏈,通過__proto__向上進行查找,最終到Null結束。最終邏輯即下圖:
圖2.5
三. 對象簡介
對象的定義:對象是Javascript的一個基本數據類型,是一種複合值,它將很多值(原始值或者其他對象)聚合在一起,可通過名字訪問這些值。即屬性的無序集合。
第一:Javascript對象是基本數據類型之一,是複合類型;
第二:Javascript中幾乎所有事物都是做對象;
第三:Javascript的對象是擁有屬性和方法的數據;
第四:Javascript 中的對象可以簡單理解成"名稱:值"對(name:value)。名稱(name):"名稱"部分是一個 Javascript 字元串
圖3.1
第一種創建對象方式如上圖所示,直接創建一個對象,或者叫做對象直接量、字面量。
圖3.2
第二種創建對象方式如上圖所示,通過new.object創建對象,該方法只能創建系統自帶的對象,如:new Object(), Array(), Number(),Boolean(), Date()...
圖3.3
第三種創建對象方式如上圖所示,通過構造函數的形式創建對象,構造函數一般使用駝峰式命名方法命名。
圖3.4
第四種創建對象方式如上圖所示,創建一個繼承該原型的實例對象。
四. 數據類型和記憶體圖
棧:原始數據類型(Undefined,Null,Boolean,Number、String)。
堆:引用數據類型(對象、數組和函數)。
兩種類型的區別是:存儲位置不同。原始數據類型是直接存儲在棧(stack)中的簡單數據段,占據空間小、大小固定,屬於被頻繁使用數據,所以放入棧中存儲;引用數據類型是存儲在堆(heap)中的對象,占據空間大、大小不固定。如果存儲在棧中,將會影響程式運行的性能;引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。
圖4.1
五. 作用域
變數的作用域無非就是兩種:全局變數和局部變數。
全局作用域:最外層函數定義的變數擁有全局作用域,即對任何內部函數來說,都是可以訪問的。
局部作用域:和全局作用域相反,局部作用域一般只在固定的代碼片段內可訪問到,而對於函數外部是無法訪問的,最常見的例如函數內部。需要註意的是,函數內部聲明變數的時候,一定要使用var或者let命令。如果不用的話,你實際上聲明瞭一個全局變數!
圖5.1
只要函數內定義了一個局部變數,函數在解析的時候都會將這個變數“提前聲明”。另外Javascript並沒有所謂的塊級作用域,Javascript的作用域是相對函數而言的,可以稱為函數作用域:
全局函數無法查看局部函數的內部細節,但局部函數可以查看其上層的函數細節,直至全局細節。當需要從局部函數查找某一屬性或方法時,如果當前作用域沒有找到,就會上溯到上層作用域查找,直至全局函數。
本文參考鏈接:
1、https://www.cnblogs.com/libin-1/p/5911190.html
2、https://www.cnblogs.com/foodoir/p/5971686.html
3、https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Questions-and-Answers