什麼是JavaScript?

nice1022發表於2022-03-21

一、什麼是JavaScript

1、DOM

文件物件模型(DOM,Document Object Model)是一個應用程式設計介面(API),用於在HTML中使用擴充套件的HTML。DOM將整個頁面抽象為一組分層節點。

DOM通過建立表示文件的樹,讓開發者可以隨心所欲的控制網頁的內容和結構。使用DOM API可以輕鬆地刪除、新增、替換、修改節點。

對瀏覽器而言,DOM就是使用ECMAScript實現的,如今已經成為JavaScript語言的一大組成部分。

言而言之,DOM提供與網頁內容互動的方法和介面。


2、BOM

IE3和Netscape Navigator3提供了瀏覽器物件模型(BOM)API,用於支援訪問和操作瀏覽器的視窗。使用BOM,開發者可以操控瀏覽器顯示頁面之外的部分。

BOM的能力展示:


彈出新瀏覽器視窗的能力;

移動、縮放和關閉瀏覽器視窗的能力;

navigator物件,提供關於瀏覽器的詳盡資訊;

location物件,提供瀏覽器載入頁面的詳盡資訊;

screen物件,提供關於使用者螢幕解析度的詳盡資訊;

performance物件,提供瀏覽器記憶體佔用、導航行為和時間統計的詳盡資訊;

對cookie的支援;

其它自定義物件,如XMLHttpRequest和IE的ActiveXObject。

簡而言之,BOM提供與瀏覽器互動的方法和介面。

二、HTML中的JavaScript

1、script標籤

<script>標籤有8大屬性,可以包含來自外部域的JavaScript檔案,<script>的src屬性可以是一個完整的URL,並且這個URL指向的資源可以跟包含它的HTML頁面不在同一個域中,瀏覽器解析這個資源時,會想src屬性指定的路徑傳送一個GET請求,以取得相應資源,這個初始的請求不受瀏覽器同源策略限制,但返回並被執行的JavaScript則受限制。當然,這個請求仍然受父頁面HTTP/HTTPS協議的限制。


2、標籤位置

過去,所有的<script>標籤都放在head標籤中,這就意味著所有JavaScript程式碼都要下載、解析和解釋完成後,才能開始渲染頁面(頁面在瀏覽器解析到<body>標籤的起始標籤時開始渲染)。對於需要很多JavaScript的頁面,會導致頁面渲染的明顯延遲,在此期間瀏覽器視窗完全空白。

為了解決這個問題,現代Web應用程式通常將所有JavaScript引用放在<body>元素中的頁面內容後面。


3、延遲執行指令碼

正常情況下,JavaScript的程式碼是書序執行的。

defer指令碼會延遲到整個頁面都解析完畢後再執行,只適用於外部腳步。

async指令碼與defer指令碼類似,都是隻適用於外部指令碼,但async指令碼並不能保證按照它們出現的次序執行。


4、動態載入指令碼

JavaScript可以通過向DOM中動態新增script元素同樣可以載入指定的指令碼,只要建立一個script元素並將其新增到DOM即可。


let script = document.createElement('script');

script.src = 'nezha.js';

document.head.appendChild(script);


預設情況下,以這種方式建立的<script>標籤都以非同步方式載入,相當於加了async標籤。


5、將JavaScript程式碼獨立於HTML的好處

可維護性

快取。瀏覽器會根據特定的設定快取所有外部連結的JavaScript檔案,這意味著如果兩個頁面都用到同一個JavaScript檔案,則該檔案只需載入一次,這最終意味著頁面載入更快。

三、語言基礎

1、嚴格區分大小寫

2、ECMAScript中的語句推薦以分號結尾

加分號有助於防止省略造成的問題

避免輸入內容不完整

便於開發者通過刪除空行來壓縮程式碼(如果沒有結尾的分號,只刪除空行,則會導致語法錯誤)

加分號有助於提升性效能,因為解析器會嘗試在合適的位置補上分號以糾正語法錯誤。

四、對比var與let、const

let不具備宣告提升,var具備宣告提升

let宣告的範圍是塊作用域,而var宣告的範圍是函式作用域。

let是ES6才引入的宣告關鍵字

for迴圈中的let宣告

const與let很相似,最大的區別是const必須初始化,且不能再次賦值。

結語:


不使用var,有了let和const,大多數開發者會發現自己不再需要var了,限制自己只使用let和const,有助於提升程式碼質量,因為變數有了明確的作用域、宣告位置、以及不變的值。

const優先,let次之。使用const宣告可以讓瀏覽器執行時強制保持變數不變,也可以讓靜態程式碼分析工具提前發現不合法的賦值操作。

五、物件池管理

通過建立物件池管理一組可回收的物件,應用程式可以向這個物件池請求一個物件、設定其屬性,使用他,然後

在操作完成後再把它交還給物件池,因為沒有物件初始化,垃圾回收探測劊發現兌現更替,因此垃圾回收程式就不會頻繁呼叫了。


let v1 = vectorPool.allocate();

let v2 = vectorPool.allocate();

let v3 = vectorPool.allocate();


v1.x = 10;

v1.y = 5;

v2.x = 11;

v2.y = 12;


addVector(v1,v2,v3);


vectorPool.free(v1);

vectorPool.free(v2);

vectorPool.free(v3);


由於陣列是動態可變的,當建立一個大小為100的陣列,使用時發現不夠大,引擎會刪除這個陣列,然後建立一個新的,

垃圾回收程式會看到這個刪除操作,然後很快的就來收一次垃圾,要避免這種動態分配操作。


六、變數、作用域、記憶體

JavaScript變數可以儲存兩種型別的值:原始值和引用值。


1、原始值6種資料型別

Undefined

Null

Boolean

Number

String

Symbol

2、原始值和引用值特點

原始值大小固定,儲存在棧記憶體中

從一個變數到另一個變數複製原始值,會建立該值得第二個副本

引用值是物件,儲存在堆記憶體中

包含引用值的變數實際上只包含指向相應物件的一個指標,而不是物件本身

從一個變數到另一個變數複製引用值,只會複製指標,因此結果是兩個變數都指向同一個物件

typeof操作費可以確定值的原始型別,instanceof操作符用於確保值得引用型別

3、執行上下文

任何變數都存在於某個執行上下文中(也稱為作用域)。這個上下文(作用域)決定了變數的生命週期,以及它們可以訪問程式碼的哪些部分。

執行上下文可以總結如下:


執行上下文分為 ① 全域性上下文、② 函式上下文、③ 塊級上下文

程式碼執行流每進入一個新上下文,都會建立一個作用域鏈,用於搜尋變數和函式

函式或塊的區域性上下文不僅可以訪問自己作用域內的變數,也可以訪問任何包含上下文乃至全域性上下文的變數

全域性上下文只能訪問全域性上下文中的變數和函式,不能直接訪問區域性上下文中的任何資料

變數的執行上下文用於確定什麼時候釋放記憶體

4、JavaScript垃圾回收

JavaScript是使用垃圾回收的程式語言,開發者不需要操心記憶體分配和回收。


離開作用域的值會被自動標記為可回收,然後在垃圾回收期間被刪除

主流的垃圾回收演算法是標記演算法,即先給當前不使用的值加上標記,再回來回收它們的記憶體

引用計數是另一種垃圾回收策略,需要記錄值被引用了多少次。JavaScript引擎不再使用這種演算法,但某些舊版本的IE仍然會受這種演算法的影響,原因是JavaScript會訪問非原生JavaScript物件(如DOM物件)。

引用計數在程式碼中迴圈引用時會出現問題

解除變數的引用不僅可以消除迴圈引用,而且對垃圾回收也有幫助。為促進記憶體回收,全域性物件、全域性物件的屬性和迴圈引用都應該在不需要時解除引用。

七、map基礎API

1、set()方法新增鍵值對

2、get()和has()進行查詢

3、delete()和clear()進行刪除

4、size獲取map獲取鍵值對數量

八、順序與迭代

與Object的主要差異是,map例項會維護鍵值對的插入順序,可以根據插入順序執行迭代操作。

對映例項可以通過迭代器Iterator,能以插入順序生成[key,value]形式的陣列,可以通過entries()方法,或者Symbol.iterator屬性,它引用entries()取得這個迭代器。


const map = new Map([

["id","1"],

["name","哪吒"],

["age","18"]

]);


for(let x of map.entries()){

alert(x);

}

//[id,1]

//[name,哪吒]

//[age,18]


for(let x of map[Symbol.iterator]()){

alert(x);

}

//[id,1]

//[name,哪吒]

//[age,18]


map.foreach((val,key) => alert(`${key} -> ${val}`));

//id -> 1

//name -> 哪吒

//age -> 18


for(let key of map.keys()){


}


for(let v of map.values()){


}


九、Object和Map到底有什麼區別?

1、記憶體佔用

給定固定大小記憶體的情況下,Map一般會比Object多儲存50%的鍵值對。


2、插入效能

插入Map一般會稍微快一點。


3、查詢速度

相差無幾。


4、刪除效能

Map的刪除效能完勝Object。

綜上四點,選擇Map顯然是更好地選擇。


十、Set

1、基礎API

新增add()

查詢has()

獲取數量size

刪除delete()

清空clear()

2、順序與迭代

Set會維護值插入時的順序,因此支援按順序迭代。

集合例項可以提供一個迭代器Iterator,能以插入順序生成集合內容。可以通過values()方法及其別名方法keys(),或者Symbol.iterator屬性,他引用values(),取得這個迭代器。


const s = new Set("哪吒","雲韻","比比東");


alert(s.keys === s[Symbol.iterator]);//true

alert(s.values === s[Symbol.iterator]);//true


for(let value of s.values()){


}


for(let value of s[Symbol.iterator]){


因為values()是預設迭代器,所以可以直接對集合例項使用擴充套件操作,把集合轉為陣列:


const s = new Set("哪吒","雲韻","比比東");

console.log([...s]);//["哪吒","雲韻","比比東"]


十一、什麼是生成器

1、生成器簡介

生成器是ECMAScript6新增的一個極為靈活的結構,擁有在一個函式塊內暫停和恢復程式碼執行的能力。這種新能力具有較深遠的影響,比如,使用生成器可以自定義迭代器和實現協程。

生成器的形式是一個函式,函式名稱前面加一個星號*,表示它是一個生成器。只要是可以定義函式的地方,就可以定義生成器。

呼叫生成器函式會產生一個生成器物件,生成器物件一開始處於暫停執行(suspended)狀態。與迭代器相似,生成器物件也實現了Iterator介面,因此具有next()方法。呼叫這個方法會讓生成器開始或恢復執行。

next()方法的返回值類似於迭代器,有一個done屬性和一個value屬性。函式體為空的生成器函式中間不會停留,呼叫一次next()就會讓生成器到達done:true狀態。

value屬性是生成器函式的返回值,預設值為undefined,可以通過生成器函式的返回值指定:


function * generatorFn(){

return '哪吒程式設計'

}

1

2

3

生成器函式只會在初次呼叫next()方法之後開始執行。


2、通過yield中斷執行

yield關鍵字可以讓生成器停止和開始執行,也是生成器最有用的地方,生成器函式在遇到yield關鍵字之前會正常執行。遇到這個關鍵字之後,執行器會停止,函式作用域的狀態會被保留。停止執行的生成器函式只能通過在生成器物件上呼叫next()方法來恢復執行。


生成器物件作為可迭代物件

使用yield實現輸入和輸出

產生可迭代物件

使用yield*實現遞迴演算法

3、生成器作為預設迭代器

因為生成器物件實現了Iterable介面,而且生成器函式和預設迭代器被呼叫之後都產生迭代器,所以生成器格外適合作為預設迭代器。


4、提前終止生成器

1、使用return()終止生成器

2、使用throw()

throw()方法會在暫停的時候提供一個錯誤注入到生成器物件中。如果錯誤未被處理,生成器就會關閉。不過如果生成器函式內部處理了這個錯誤,生成器就不會關閉,還可以恢復執行。


5、生成器小結

迭代是一種所有程式語言中都可以看到的模式。ECMASCript正式支援迭代模式並引入兩個新的語言特性:迭代器和生成器。

迭代器是一個可以由任意物件實現的介面,支援連續獲取物件產出的每一個值。任何實現Iterable介面的物件都有一個Symbol.iterator屬性,這個屬性引用預設迭代器。預設迭代器就像一個迭代器工廠,也就是一個函式,呼叫之後會產生一個實現Iterator介面的物件。

迭代器必須通過連續呼叫next()方法才能連續獲取值,這個方法返回一個IteratorObject。這個物件包含一個done屬性和一個value屬性。前者時刻一個布林值,表示十分還有更多值可以訪問;後者包含迭代器返回的當前值。這個介面可以通過手動反覆呼叫next()方法來消費,也可以通過原生消費者,比如for迴圈來自動消費。

生成器是一種特殊的函式,呼叫之後會返回一個生成器物件。生成器物件實現了Iterable介面,因此可用在任何消費可迭代物件的地方。生成器的獨特之處在於支援yield關鍵字,這個關鍵字能夠暫停執行生成器函式。使用yield關鍵字還可以通過next()方法接收輸入和產生輸出。在加上星號之後,yield關鍵字可以將跟在後面的可迭代物件序列化為一連串值。


十二、原型鏈

ECMA-262把原型鏈定義為ECMAScript的主要繼承方式。其基本思想就是通過原型繼承多個引用型別的屬性和方法。


1、建構函式、原型、例項三者的關係?

每個建構函式都有一個原型物件,原型有一個屬性指回建構函式,而例項有一個內部指標指向原型。如果原型是另一個型別的例項呢?那就意味著這個原型本身有一個內部指標指向另一個原型,相應地另一個原型也有一個指標指向另一個建構函式,這樣就在例項和原型之間構造了一條原型鏈。


2、盜用建構函式

為了解決原型鏈包含引用值導致的繼承問題,引入了盜用建構函式的概念。基本思路很簡單,在子類建構函式中呼叫父類建構函式,因為畢竟函式就是在特定上下文中執行程式碼的簡單物件,所以可以使用apply()或call()方法,重新建立上下文執行建構函式。

相比於使用原型鏈,盜用建構函式的一個優點就是可以在子類建構函式中向父類建構函式傳參。


3、組合繼承

組合繼承綜合了原型鏈和盜用建構函式,將兩者的優點集中了起來,基本的思路是使用原型鏈繼承原型上的屬性和方法,而通過盜用建構函式繼承例項屬性。這樣既可以把方法定義在原型上以實現重用,又可以讓每個例項都有自己的屬性。

組合繼承也保留了instanceof操作符和isPrototypeOf()方法識別合成物件的能力。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70011332/viewspace-2878953/,如需轉載,請註明出處,否則將追究法律責任。

相關文章