JavaScript難點筆記

JavaScript難點筆記

前言

由於工作需求重新回顧了一下JavaScript,以下內容是我以前的學習筆記和其他參考資料整理完善後的內容,都是常用到的,使用頻率比較高的,自己必須得精通的知識點的總結,便以後再複習參考。

第一章JavaScript原型對象與原型鏈

1.1構造函數的缺點

自定義對象時,以構造函數為模板,對象的屬性和方法,可以定義在構造函數內部。每當獲取對象時都會在內存中創建新的對象屬性和方法,這既是增加頁面代碼量有浪費內存(系統資源)。

同一個構造函數的對象實例之間無法共享屬性,而所有的方法都是同樣的行為,因此屬性和方法完全應該共享。但無法共享這就是缺點。

JavaScript難點筆記

1.2prototype屬性

JavaScript中每一個對象都繼承另一個對象,父類對象稱之為“原型”(prototype)對象。只有null除外,其他都有自己的原型對象

而原型對象上的所有屬性和方法,都能被派生(子類)對象共享。通過構造方法生成生成實例化對象時,會自動生成實例化對象分配原型對象。每一個構造方法都有一個prototype屬性,這個屬性就是實例化對象的原型對象。

JavaScript難點筆記

構造函數Student的prototype對象就是實例化對象stu1和stu2的原型對象。在原型對象上添加一個color屬性。結果,實例化對象都能讀取屬性。實現屬性共享;

原型對象的屬性不是實例化對象自身的屬性。但是隻要修改原型對象,變動會立刻回提現到所有實例化對象上。

如果實例化對象自身就有某個屬性或方法,那麼原型對象上的屬性和方法便會失效:

JavaScript難點筆記

他被稱為原型對象的原因,而實例化對象可以視作從原型對象衍生出來的子對象。

由於JavaScript的所有對象都是構造函數(只有null除外)。而所有構造函數都有prototype屬性(其實是所有函數都有prototype屬性),所以所有對象都有自己的原型對象。

1.3原型鏈

對象的屬性和方法,有可能是定義在自身內,也有可能是定義在它的原型對象上。由於原型對象本身也是對象,又有自己的原型,所有生成了一條原型鏈(prototypechain)。例如,a對象是b對象的原型,b對象是c對象的原型,依次類推。

如果那麼一層層地上溯,所有對象的原型最終都可以上溯到Object對象上。Object對象也有原型就是一個沒有任何屬性和方法的null對象,而null對象沒有自己的原型。

獲取對象的原型對象:

JavaScript難點筆記

原型鏈的作用是讀取對象某個屬性時,JavaScript引擎先尋找對象本身的屬性,如果找不到就找它的原型(父類對象),如果還是找不到就找原型的原型。如果直到最頂層的Object。prototype還是找不到,則返回undefined。

如果對象自身和它的原型,都定義了一個同名屬性,那麼悠閒讀取對象自身的屬性,這叫覆蓋(overriding)

注意:一級級向上,在原型鏈尋找某個屬性,對性能是有影響的。如果尋找某個不存在的屬性將會遍歷整個原型鏈。

實際開發上注意事項:通常使用第三方框架(一個類),但是我們發現這個類中並不存在我們想要的屬性或方法時,不能直接修改源代碼,但是可以通過原型對象來添加我們想要的屬性和方法。

1.4原型操作

1.4.1constructor屬性

對象有一個constructor屬性指向原型對象所在的構造函數:

JavaScript難點筆記

1.4.2設置獲取原型對象

Object.getPrototypeOf()方法返回一個對象的原型對象,也就是通過它獲取原型對象,這是標準的方法。

JavaScript難點筆記

Object.setPrototypeOd()為現有對象設置原型對象,第一個參數是現有對象,第二個是要設置成為原型對象的對象,用這個方法來設置原型對象。

JavaScript難點筆記

proto屬性:前面用proto來獲取原型對象,如果給proto屬性賦值,則設置原型對象;最好不用這個,用Object.getPrototypeOf()來讀取,用Object.setPrototypeof()來設置。也就是用這兩個方法來對原型對象做讀寫操作。

1.4.3獲取原型對象方法及比較

上面寫過獲取原型對象的方法有三種:

JavaScript難點筆記

其中前兩個方法不是很友好,最新的ES6標準規定,proto屬性只有瀏覽器才需要部署,其他環境可以不部署。而obj.constructor.prototype在手動改變原型對象時,會失效。

JavaScript難點筆記

總結:最好使用Object.getPrototypeOf()方法獲取原型對象,沒了。

JavaScript難點筆記

第二章閉包

2.1閉包的概念

JavaScript有兩種作用域:全局作用域和函數作用域(局部作用域)。函數內部可以直接讀取全局變量,但是函數外部無法讀取函數內部聲明的變量。

但是,有時候卻需要在函數外部訪問函數內部的變量;正常情況下,這是無法的訪問的,只有通過變通方法才能實現訪問,也就是在函數內部再定義一個函數,通過內部函數來訪問函數內部的變量。

JavaScript難點筆記

說明:函數fun2就在函數fun1()內部,這是fun1()內部的所有局部變量對fun2()是可訪問的。反過來就不行,內部函數中的局部變量對父類函數是不可訪問的。這就是JavaScript中特有的**鏈式作用域(chainscope)**結構,子對象會一級一級地向上尋找所有父類對象的變量,所以,父對象的所有變量,對子對象都是可見的,反之則不成立。

既然fun2()可以讀取fun1()中的局部變量,那麼只要把fun2()作為返回值,我們就可以在fun1()外部讀取它的內部變量了。

重點:

閉包就是函數fun2,既能狗讀取其他函數內部變量的函數。由於JavaScript中,只要函數內部的子函數才能夠讀取函數內部的局部變量,因此可以把閉包簡單理解成“定義一個函數內部的函數”。

閉包的最大特點就是它可以“記住”誕生的環境,比如fun2()記住,所以從fun2()可以得到fun1()的內部變量。本質上,閉包就是將函數內部和函數外部連接起來的一座橋樑。

2.2垃圾回收機制及閉包

2.2.1垃圾回收機制

JavaScript難點筆記

說明:在函數內部引入一個變量或者函數時,系統都會開闢一塊內存空間;還會將這塊內存的引用計數器初始化,初始化值為0;如果外部有全局變量或者程序引用了這塊空間,則引用計數器會自動進行+1操作,當函數執行完畢後,變量計數器會重新歸零,系統會運行垃圾回收機制,將函數運行產生的數據銷燬;如果計數器不是0,則不會清楚數據;這過程就是JavaScript的垃圾回收機制

JavaScript的垃圾回收機制原理圖:

JavaScript難點筆記

用閉包的分析話更能體驗此原理:

JavaScript難點筆記

代碼分析(註釋):

因函數fun1被調用時,返回的結果是fun2函數體,也就是說,fun2函數被當做返回值給fun1的調用者,但是fun2函數並沒有在此被調用執行(只是把函數體返回到函數外了);

因此整個fun1函數體,無法判斷子函數fun2會對其產生何種影響,無法判斷變量n是否會被使用,即使fun1函數被調用結束,整個fun1函數始終保留在內存中,不會被垃圾回收機制回收;

也就是運行代碼發現,函數調用一次(在這裡是指fun2),其變量n變化一次;

閉包的最大用處:

可以讀取函數內部的變量。

讓函數內部讀取的變量始終保持在內存中,即閉包可以使得它誕生環境一直存在。

注意的是,外層函數每次運行,都會生成一個新的閉包,而這個閉包有會保留外層函數的內部變量,所以內存消耗很大;因此不能濫用閉包,否則會造成網頁的性能問題。

第三章call和apply方法

關於JavaScript中的this的指向:

1.全局作用域下,this指向window對象

2.構造函數中,this指向實例化對象

如果要在調用函數是直接修改函數內部的this指向使用call或者apply方法來修改指向。

1.call方法格式:函數名稱.call(obj,arg1,arg2,…argN);說明其中:obj是函數內this要指向的對象,arg列表是參數列表,參數與參數之間使用一個逗號隔開

JavaScript難點筆記

2.apply方法格式:函數名稱.apply(obj,[arg1,arg2,…argN]);說明其中:obj是函數內this要指向的對象,arg列表是參數列表,要求格式為數組

JavaScript難點筆記

兩種修改this指向方法的區別:

相同點:功能完全一樣,都是為了改變函數內部的hits指向,唯一的不同就在於參數傳遞方式不同

不同點:call方法可能多個參數,第一個要指向的對象,其他參數為函數的實參;apply方法最多隻能有兩個實參,第一個要指向的對象,第二個是數組,數組內容為函數的實參。

第四章私有屬性

JavaScript與其他語言不太一樣,它只有兩種屬性,即公有屬性和私有屬性,概念很好理解,也就是在構造函數內部通過this聲明的屬性就是公有屬性,通過var聲明的就是私有屬性。

JavaScript難點筆記

第五章對象繼承

JavaScript中的繼承的實現與其他語言也不相同,它沒有關鍵字提供繼承的功能。所謂繼承就是為了子類中提供父類中的屬性和方法,子類能夠使用父類中的屬性和方法

JavaScript中繼承的實現方式有:

1.通過原型對象實現繼承

JavaScript難點筆記

2.通過call或apply方法繼承(實質就是改變指向使用父類中的屬性和方法)

JavaScript難點筆記

第六章定時器

JavaScript提供定時執行代碼的功能叫做定時器;

1.setTimeout():用來指定某個函數或某代碼,在多少秒之後執行。

2.setInterval():指定某個任務每隔一段時間就執行一次,也就是無限次的定時執行。

setTimeout(),setInterval()的第一個參數都是指定執行的函數名稱或者代碼段,第二個參數是時間:

JavaScript難點筆記

setTimeout()函數和setInterval()函數都返回一個表示計數器編號的整數值,將該整數傳入clearTimeout()和clearInterval()函數,就可以取消對應的定時器。

JavaScript難點筆記

補充知識點

上面提過內存,計數器,那麼就谷歌一下什麼是內存及計數器。

通常說的內存是計算機的主存儲器(mainmemory),簡稱主存。主存通過控制芯片等與CPU相連,主要負責存儲指令和數據。主存由可讀寫的元素構成,每個字節(1字節=8位)都帶有一個地址編號(也就是所謂的內存地址)。CPU可以通過改地址讀取主存中的指令和數據,當然也可以寫入數據。注意的是,主存中存儲的指令和數據會隨著計算機的關機自動清除(自動銷燬,釋放)。

寄存器是CPU的組件之一,,CPU由控制器,運算器,時鐘,寄存器組成。控制器做的是數據運算以外的處理(主要是輸入和輸出的時機控制),比如內存和磁盤等媒介的輸入輸出,顯示器,打印機的輸出等,都是控制器做的事。

廢話不多說,聽大師們說CPU中程序員只要搞明白寄存器就可以飛上天了。原因是程序是把寄存器作為對象來描述,也就是計算機中每一句命令都是通過寄存器才能執行(數據存儲,假髮運算等),寄存器也有很多種類,根據類型存儲的數據也不一樣,不同的CPU中的寄存器數量也不一樣以及寄存器存儲的數值範圍也不一樣。

寄存器由程序計數器,標誌寄存器,累加寄存器,基址寄存器,變址寄存器,通用寄存器等組成,其中程序計數器和標誌寄存器比較特殊。計數器以二進制形式技術,計數規則是:**CPU每執行一個命令,計數器的值就會自動加1,**例如,CPU執行0100地址的指令後,計數器的值就變成了0101(當執行的指令佔據多個內存地址是,增加與指令長度相應的數值)。然後CPU的控制器就會參照計數器的數值,從內存中讀取命令並執行,也就是說,程序計數器決定著程序的運行流程。

JavaScript權威指南(第6版)

計算機科學導論(第三版)

W3school

菜鳥教程

JavaScript難點筆記


分享到:


相關文章: