![JavaScript難點筆記](http://p2.ttnews.xyz/loading.gif)
前言
由於工作需求重新回顧了一下JavaScript,以下內容是我以前的學習筆記和其他參考資料整理完善後的內容,都是常用到的,使用頻率比較高的,自己必須得精通的知識點的總結,便以後再複習參考。
第一章JavaScript原型對象與原型鏈
1.1構造函數的缺點
自定義對象時,以構造函數為模板,對象的屬性和方法,可以定義在構造函數內部。每當獲取對象時都會在內存中創建新的對象屬性和方法,這既是增加頁面代碼量有浪費內存(系統資源)。
同一個構造函數的對象實例之間無法共享屬性,而所有的方法都是同樣的行為,因此屬性和方法完全應該共享。但無法共享這就是缺點。
![JavaScript難點筆記](http://p2.ttnews.xyz/loading.gif)
1.2prototype屬性
JavaScript中每一個對象都繼承另一個對象,父類對象稱之為“原型”(prototype)對象。只有null除外,其他都有自己的原型對象
而原型對象上的所有屬性和方法,都能被派生(子類)對象共享。通過構造方法生成生成實例化對象時,會自動生成實例化對象分配原型對象。每一個構造方法都有一個prototype屬性,這個屬性就是實例化對象的原型對象。
構造函數Student的prototype對象就是實例化對象stu1和stu2的原型對象。在原型對象上添加一個color屬性。結果,實例化對象都能讀取屬性。實現屬性共享;
原型對象的屬性不是實例化對象自身的屬性。但是隻要修改原型對象,變動會立刻回提現到所有實例化對象上。
如果實例化對象自身就有某個屬性或方法,那麼原型對象上的屬性和方法便會失效:
他被稱為原型對象的原因,而實例化對象可以視作從原型對象衍生出來的子對象。
由於JavaScript的所有對象都是構造函數(只有null除外)。而所有構造函數都有prototype屬性(其實是所有函數都有prototype屬性),所以所有對象都有自己的原型對象。
1.3原型鏈
對象的屬性和方法,有可能是定義在自身內,也有可能是定義在它的原型對象上。由於原型對象本身也是對象,又有自己的原型,所有生成了一條原型鏈(prototypechain)。例如,a對象是b對象的原型,b對象是c對象的原型,依次類推。
如果那麼一層層地上溯,所有對象的原型最終都可以上溯到Object對象上。Object對象也有原型就是一個沒有任何屬性和方法的null對象,而null對象沒有自己的原型。
獲取對象的原型對象:
原型鏈的作用是讀取對象某個屬性時,JavaScript引擎先尋找對象本身的屬性,如果找不到就找它的原型(父類對象),如果還是找不到就找原型的原型。如果直到最頂層的Object。prototype還是找不到,則返回undefined。
如果對象自身和它的原型,都定義了一個同名屬性,那麼悠閒讀取對象自身的屬性,這叫覆蓋(overriding)。
注意:一級級向上,在原型鏈尋找某個屬性,對性能是有影響的。如果尋找某個不存在的屬性將會遍歷整個原型鏈。
實際開發上注意事項:通常使用第三方框架(一個類),但是我們發現這個類中並不存在我們想要的屬性或方法時,不能直接修改源代碼,但是可以通過原型對象來添加我們想要的屬性和方法。
1.4原型操作
1.4.1constructor屬性
對象有一個constructor屬性指向原型對象所在的構造函數:
1.4.2設置獲取原型對象
Object.getPrototypeOf()方法返回一個對象的原型對象,也就是通過它獲取原型對象,這是標準的方法。
Object.setPrototypeOd()為現有對象設置原型對象,第一個參數是現有對象,第二個是要設置成為原型對象的對象,用這個方法來設置原型對象。
proto屬性:前面用proto來獲取原型對象,如果給proto屬性賦值,則設置原型對象;最好不用這個,用Object.getPrototypeOf()來讀取,用Object.setPrototypeof()來設置。也就是用這兩個方法來對原型對象做讀寫操作。
1.4.3獲取原型對象方法及比較
上面寫過獲取原型對象的方法有三種:
其中前兩個方法不是很友好,最新的ES6標準規定,proto屬性只有瀏覽器才需要部署,其他環境可以不部署。而obj.constructor.prototype在手動改變原型對象時,會失效。
總結:最好使用Object.getPrototypeOf()方法獲取原型對象,沒了。
第二章閉包
2.1閉包的概念
JavaScript有兩種作用域:全局作用域和函數作用域(局部作用域)。函數內部可以直接讀取全局變量,但是函數外部無法讀取函數內部聲明的變量。
但是,有時候卻需要在函數外部訪問函數內部的變量;正常情況下,這是無法的訪問的,只有通過變通方法才能實現訪問,也就是在函數內部再定義一個函數,通過內部函數來訪問函數內部的變量。
說明:函數fun2就在函數fun1()內部,這是fun1()內部的所有局部變量對fun2()是可訪問的。反過來就不行,內部函數中的局部變量對父類函數是不可訪問的。這就是JavaScript中特有的**鏈式作用域(chainscope)**結構,子對象會一級一級地向上尋找所有父類對象的變量,所以,父對象的所有變量,對子對象都是可見的,反之則不成立。
既然fun2()可以讀取fun1()中的局部變量,那麼只要把fun2()作為返回值,我們就可以在fun1()外部讀取它的內部變量了。
重點:
閉包就是函數fun2,既能狗讀取其他函數內部變量的函數。由於JavaScript中,只要函數內部的子函數才能夠讀取函數內部的局部變量,因此可以把閉包簡單理解成“定義一個函數內部的函數”。
閉包的最大特點就是它可以“記住”誕生的環境,比如fun2()記住,所以從fun2()可以得到fun1()的內部變量。本質上,閉包就是將函數內部和函數外部連接起來的一座橋樑。
2.2垃圾回收機制及閉包
2.2.1垃圾回收機制
說明:在函數內部引入一個變量或者函數時,系統都會開闢一塊內存空間;還會將這塊內存的引用計數器初始化,初始化值為0;如果外部有全局變量或者程序引用了這塊空間,則引用計數器會自動進行+1操作,當函數執行完畢後,變量計數器會重新歸零,系統會運行垃圾回收機制,將函數運行產生的數據銷燬;如果計數器不是0,則不會清楚數據;這過程就是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列表是參數列表,參數與參數之間使用一個逗號隔開
2.apply方法格式:函數名稱.apply(obj,[arg1,arg2,…argN]);說明其中:obj是函數內this要指向的對象,arg列表是參數列表,要求格式為數組
兩種修改this指向方法的區別:
相同點:功能完全一樣,都是為了改變函數內部的hits指向,唯一的不同就在於參數傳遞方式不同
不同點:call方法可能多個參數,第一個要指向的對象,其他參數為函數的實參;apply方法最多隻能有兩個實參,第一個要指向的對象,第二個是數組,數組內容為函數的實參。
第四章私有屬性
JavaScript與其他語言不太一樣,它只有兩種屬性,即公有屬性和私有屬性,概念很好理解,也就是在構造函數內部通過this聲明的屬性就是公有屬性,通過var聲明的就是私有屬性。
第五章對象繼承
JavaScript中的繼承的實現與其他語言也不相同,它沒有關鍵字提供繼承的功能。所謂繼承就是為了子類中提供父類中的屬性和方法,子類能夠使用父類中的屬性和方法;
JavaScript中繼承的實現方式有:
1.通過原型對象實現繼承
2.通過call或apply方法繼承(實質就是改變指向使用父類中的屬性和方法)
第六章定時器
JavaScript提供定時執行代碼的功能叫做定時器;
1.setTimeout():用來指定某個函數或某代碼,在多少秒之後執行。
2.setInterval():指定某個任務每隔一段時間就執行一次,也就是無限次的定時執行。
setTimeout(),setInterval()的第一個參數都是指定執行的函數名稱或者代碼段,第二個參數是時間:
setTimeout()函數和setInterval()函數都返回一個表示計數器編號的整數值,將該整數傳入clearTimeout()和clearInterval()函數,就可以取消對應的定時器。
補充知識點
上面提過內存,計數器,那麼就谷歌一下什麼是內存及計數器。
通常說的內存是計算機的主存儲器(mainmemory),簡稱主存。主存通過控制芯片等與CPU相連,主要負責存儲指令和數據。主存由可讀寫的元素構成,每個字節(1字節=8位)都帶有一個地址編號(也就是所謂的內存地址)。CPU可以通過改地址讀取主存中的指令和數據,當然也可以寫入數據。注意的是,主存中存儲的指令和數據會隨著計算機的關機自動清除(自動銷燬,釋放)。
寄存器是CPU的組件之一,,CPU由控制器,運算器,時鐘,寄存器組成。控制器做的是數據運算以外的處理(主要是輸入和輸出的時機控制),比如內存和磁盤等媒介的輸入輸出,顯示器,打印機的輸出等,都是控制器做的事。
廢話不多說,聽大師們說CPU中程序員只要搞明白寄存器就可以飛上天了。原因是程序是把寄存器作為對象來描述,也就是計算機中每一句命令都是通過寄存器才能執行(數據存儲,假髮運算等),寄存器也有很多種類,根據類型存儲的數據也不一樣,不同的CPU中的寄存器數量也不一樣以及寄存器存儲的數值範圍也不一樣。
寄存器由程序計數器,標誌寄存器,累加寄存器,基址寄存器,變址寄存器,通用寄存器等組成,其中程序計數器和標誌寄存器比較特殊。計數器以二進制形式技術,計數規則是:**CPU每執行一個命令,計數器的值就會自動加1,**例如,CPU執行0100地址的指令後,計數器的值就變成了0101(當執行的指令佔據多個內存地址是,增加與指令長度相應的數值)。然後CPU的控制器就會參照計數器的數值,從內存中讀取命令並執行,也就是說,程序計數器決定著程序的運行流程。
JavaScript權威指南(第6版)
計算機科學導論(第三版)
W3school
菜鳥教程
閱讀更多 渡一教育web前端開發 的文章
關鍵字: 編程語言 JavaScript 技術