挑戰堅持學習1024天——前端之JavaScript基礎之一【Day1-Day7】

語言: CN / TW / HK

theme: juejin highlight: tomorrow


大家好我是牛哥,一枚前端程式設計師。雖然從事前端開發,但我個人覺得想要長期端穩程式設計師這碗飯,光會前端是不夠的(業內大佬除外),程式設計行業的生命週期可以總結為:程式設計師(前端/後端)->工程師(全棧)->研發架構師(IT+DT)->解決方案架構師(IT+DT+商業)->CTO->創業者,用年齡劃分:30歲之前死磕技術,技術為王。30歲之後想要擺脫中年危機比較好的辦法就是轉變思維由技術到管理/自主創業。

由此可看出,想要在網際網路行業有所成就且長存不倒,要學的東西太多太多了其中包含:前端、後端、大資料、產品、運營、架構....,可能有些小夥伴看到是不是有點勸退了,就單純把其中一個點單獨拎出來都夠喝一壺了。我個人理解只要把自己從事的這個行業學精,再去學習其他技術就不會那麼難了,有很多東西是觸類旁通的,程式語言這東西在創立之初多少都會互相借鑑,只是不同的語言他的適用場景不同罷了。

可能有人會疑惑了,我一個幹前端的學習什麼後端,一個搞後端的學什麼前端。從事前端的小夥伴學習了後端不一定要從事後端,後端學習了前端不一定要從事後端。但是想要晉升管理是要求綜合技能過關,這樣才能即管的了後端又管的了前端。那可能又有小夥伴疑問為什麼要學習產品運營,可以這麼說,不懂產品的程式設計師不是一個好的程式設計師,產品在我們日常開發中時時刻刻都在。前端後端都會了+產品運營這樣就可以晉升產品+研發+運營的高階管理。下來我們迴歸到技術,技術就像習武之人的基本功,只有基本功紮實,才有能力去練降龍十八掌、羅漢拳這些武功。讓我們一起努力練功升級把!💪💪💪

實時同步更新可以到我github(這裡shaw)

Day1【2022年7月23日】

學習重點:回顧前端基礎知識,複習JavaScript基礎之回顧JS組成(ECMAScript、DOM、BOM)。

1.前端技術回顧

在前端有我們熟悉的三架馬車HTML+CSS+JavaScript

HTML

文字標記語言HTML是設計用於在Web 瀏覽器中顯示的文件的標準標記語言。它可以通過級聯樣式表(CSS) 等技術和 JavaScript 等指令碼語言輔助。 Web 瀏覽器從Web 伺服器或本地儲存接收 HTML 文件,並將文件呈現為多媒體網頁。HTML 從語義上描述了網頁 的結構,並且最初包含了文件外觀的提示。

CSS

CSS層疊樣式表(英語:Cascading Style Sheets,縮寫:CSS;又稱串樣式列表級聯樣式表串接樣式表階層式樣式表)是一種用來為結構化文件(如HTML文件或XML應用)新增樣式(字型、間距和顏色等)的計算機語言,由W3C定義和維護。CSS3現在已被大部分現代瀏覽器支援,而下一版的CSS4仍在開發中。

JavaScript

JavaScript(通常縮寫為JS)是一種高階的、解釋型程式語言[8]。JavaScript是一門基於原型頭等函式的語言[9],是一門多正規化的語言,它支援面向物件程式設計,指令式程式設計,以及函數語言程式設計。它提供語法來操控文字、陣列、日期以及正則表示式等,不支援I/O,比如網路、儲存和圖形等,但這些都可以由它的宿主環境提供支援。它已經由ECMA(歐洲電腦製造商協會)通過ECMAScript實現語言的標準化[8]。它被世界上的絕大多數網站所使用,也被世界主流瀏覽器ChromeIEFirefoxSafariOpera)支援。

如果把這三個與我們人做比較的話HTML可以比喻為骨骼,CSS可以比喻成我們所穿的衣服,JS則可以指揮人進行運動。前兩者都不屬於程式語言其中HTML 是一種用於定義內容結構的標記語言,CSS是一種樣式語言一般用他為HTML新增樣式。JS才算得上是程式語言,引用MDN對JS的解釋————JavaScriptJS)是一種具有函式優先特性的輕量級、解釋型或者說即時編譯型的程式語言。JS可以說對前端非常重要,這門語言上手很快,但是要達到精通要花上很長事件,前端框架都是基於他進行開發的,像我們熟知的vue、react都是基於JS進行開發的。當然前兩者也很重要,優先順序的話是JS為主。而CSS而言想要學到工作中運用的水平並不難,但是要達到精通還是要花費一定的精力的,像CSS我們前端業內有比較牛的大佬-張鑫旭,著名的css三部曲就是由大佬出品,附上大佬的個人網站https://www.zhangxinxu.com/ 感興趣的小夥伴可以看看。

2.JS組成部分

完整的JavaScript由ECMAScript(核心)、文件物件模型(DOM)、瀏覽器物件模型(BOM)三者組成,三者是並列關係。

2.1ECMAScript

ECMAScript是一種由Ecma國際(前身為歐洲計算機制造商協會)在標準ECMA-262中定義的指令碼語言規範。這種語言在全球資訊網上應用廣泛,它往往被稱為JavaScriptJScript,但實際上後兩者是ECMA-262標準的實現和擴充套件。

ECMAScript每年都會更新JavaScript基於ECMAScript,而且每年都會更新標準。 最新標準於今年六月更新引用了Top-level Await,Object.hasOwn(),at(),error.case,正則表示式匹配人索引,類等新特性。

微信圖片_20220722160701.png ——官網https://www.ecma-international.org/

2.2DOM文件物件模型

文件物件模型 (DOM) 是 HTML 和 XML 文件的程式設計介面。它提供了對文件的結構化的表述,並定義了一種方式可以使從程式中對該結構進行訪問,從而改變文件的結構,樣式和內容。DOM 將文件解析為一個由節點和物件(包含屬性和方法的物件)組成的結構集合。簡言之,它會將 web 頁面和指令碼或程式語言連線起來。

一個 web 頁面是一個文件。這個文件可以在瀏覽器視窗或作為 HTML 原始碼顯示出來。但上述兩個情況中都是同一份文件。文件物件模型(DOM)提供了對同一份文件的另一種表現,儲存和操作的方式。 DOM 是 web 頁面的完全的面向物件表述,它能夠使用如 JavaScript 等指令碼語言進行修改。文件物件模型 (DOM) 將 web 頁面與到指令碼或程式語言連線起來。---MDN

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

微信圖片_20220722163007.png

2.2.1document(文件)和element(元素)的關係

Document

Document 介面表示任何在瀏覽器中載入的網頁,並作為網頁內容的入口,也就是DOM 樹。DOM 樹包含了像 <body> 、<table> 這樣的元素,以及大量其他元素。它向網頁文件本身提供了全域性操作功能,能解決如何獲取頁面的 URL ,如何在文件中建立一個新的元素這樣的問題。---MDN。

在瀏覽器中,文件物件 document 是HTMLDocument 的例項(HTMLDocument 繼承 Document),表示整個 HTML 頁面。document 是 window物件的屬性,因此是一個全域性物件。一般來說,appendChild()、removeChild()和 replaceChild()方法不會用在 document 物件上。這是因為文件型別(如果存在)是隻讀的,而且只能有一個 Element 型別的子節點(即html,已經存在了)

Element

Element 是一個通用性非常強的基類,所有 Document 物件下的物件都繼承自它。這個介面描述了所有相同種類的元素所普遍具有的方法和屬性。element 物件實現了 DOM Element 介面以及更基本的 Node 介面

Element 型別就是Web開發中最常用的型別了。Element 表示XML或HTML元素,對外暴露出訪問元素標籤名、子節點和屬性的能力。 Document和Element都繼承自它的祖先介面 Node

js var element = document.createElement(tagName[, options]);

2.2.2總結常用的一些dom操作

js //返回一個匹配特定的元素 document.getElementById(id); //返回一個包括所有給定標籤名稱的元素的 HTML 集合HTMLCollection 整個檔案結構都會被搜尋,包括根節點。返回的 HTML 集合是動態的,之後不用再呼叫會同步跟新 document.getElementsByTagName(name); //方法用於建立一個由標籤名稱 `tagName` 指定的 HTML 元素。如果使用者代理無法識別 `tagName`,則會生成一個未知 HTML 元素 HTMLUnknownElement。 document.createElement(name); //返回文件中與指定選擇器或選擇器組匹配的第一個 Element物件。 如果找不到匹配項,則返回`null`。該方法按照深度優先遍歷parentNode.appendChild(node)//方法將一個節點附加到指定父節點的子節點列表的末尾處。如果將被插入的節點已經存在於當前文件的文件樹中,那麼只會將它從原先的位置移動到新的位置(不需要事先移除要移動的節點)。 document.querySelector(); //設定一個html 可能會產生安全問題 element.innerHTML(); //返回與指定的選擇器組匹配的文件中的元素列表 (使用深度優先的先序遍歷文件的節點)。返回的是一個類陣列物件的集合 Document.querySelectorAll(); //設定指定元素的屬性值。如果屬性已經存在,則更新值;否則將新增具有指定名稱和值的新屬性。 element.setAttribute(); //要獲取屬性的當前值 element.getAttribute();

Element.innerHTML 屬性設定或獲取 HTML 語法表示的元素的後代。 如果要向一個元素中插入一段 HTML,而不是替換它的內容,那麼請使用 insertAdjacentHTML() 方法。DOMString 包含元素後代的 HTML 序列。設定元素的 innerHTML 將會刪除所有該元素的後代並以上面給出的 htmlString 替代。

簡單來說document是對文件/元素進行獲取,只能獲取不能修改,而element則對獲取的元素/標籤標籤進行修改。

2.2.3getElementById和querySelector區別

1.querySelector是按css規範來實現的,它傳入的字串中第一個字元不能是數字。 2.query選擇符選出來的元素及元素陣列是靜態的,而getElement這種方法選出的元素是動態的。靜態的就是說選出的所有元素的陣列,不會隨著文件操作而改變。在使用的時候getElement這種方法效能比較好,query選擇符則比較方便。

2.2.4 Document.querySelectorAll

elementList = parentNode.querySelectorAll(selectors);

返回與指定的選擇器組匹配的文件中的元素列表 (使用深度優先的先序遍歷文件的節點)。返回的物件是 NodeList 。

NodeList 物件是節點的集合,通常是由屬性,如Node.childNodes 和 方法,如document.querySelectorAll 返回的。 NodeList 不是一個數組,是一個類似陣列的物件 (Like Array Object)。雖然 NodeList 不是一個數組,但是可以使用 forEach() 來迭代。你還可以使用 Array.from() 將其轉換為陣列。 不過,有些瀏覽器較為過時,沒有實現 NodeList.forEach() 和 Array.from()。你可以用 Array.prototype.forEach() 來規避這一問題。請檢視該例。 在一些情況下,NodeList 是一個實時集合,也就是說,如果文件中的節點樹發生變化,NodeList 也會隨之變化。 詳情請看mdn

2.3BOM即瀏覽器物件模型

BOM即瀏覽器物件模型,它提供了獨立於內容而與瀏覽器視窗進行互動的物件,其核心物件是 window。總體來說,BOM 主要針對瀏覽器視窗和子視窗(frame),不過一般會把任何特定於瀏覽器的操作都歸在 BOM 的範疇內。雖然 ECMAScript 把瀏覽器物件模型(BOM)描述為 JavaScript 的核心,但實際上 BOM 是使用 JavaScript 開發 Web 應用程式的核心。

2.3.1window 物件一些操作

js //開啟一個新視窗 window.open(); //關閉一個新視窗 window.close(); // 把視窗移動到左上角 window.moveTo(0,0); // 把視窗向下移動 100 畫素 window.moveBy(0, 100); // 把視窗移動到座標位置(200, 300) window.moveTo(200, 300); // 把視窗向左移動 50 畫素 window.moveBy(-50, 0); //警告框 window.alert(); //確認框 window.confirm()

2.3.2定時器

我們常使用的定時器setTimeout()和setInterval()掛在在window下,它們主要的區別如下:setTimeout()用於指定在一定時間後執行某些程式碼,而 setInterval()用於指定 每隔一段時間執行某些程式碼。用法: js // 在 1 秒後顯示警告框 let timeoutIdTi = setTimeout(() => alert("Hello world!"), 10000); //取消定時器 clearTimeout(timeoutIdTi); // 在間隔 1 秒後顯示警告框 let timeoutIdIn = setInterval(() => alert("Hello world!"), 1000); //取消定時器 clearInterval(timeoutIdIn);

2.3.3location物件

location 是最有用的 BOM 物件之一,提供了當前視窗中載入文件的資訊,以及通常的導航功能。 這個物件獨特的地方在於,它既是 window 的屬性,也是 document 的屬性。也就是說, window.location 和 document.location 指向同一個物件。location 物件不僅儲存著當前載入文 檔的資訊,也儲存著把 URL 解析為離散片段後能夠通過屬性訪問的資訊。

常用API:

```js //包含塊識別符號的DOMString,開頭有一個#。"#contents" window.location.hash; //伺服器名及埠號:"www.wrox.com:80" window.location.host; //伺服器名:"www.wrox.com" window.location.hostname; //當前載入頁面的完整 URL。location 的 toString()方法也返回這個值:"http://www.wrox.com:80/WileyCDA/?q=javascript#contents" window.location.href; //URL 中的路徑和(或)檔名:"/WileyCDA/" window.location.pathname; //請求的埠。如果 URL中沒有埠,則返回空字串:"80" window.location.port; //頁面使用的協議。通常是"http:"或"https:":"http:" window.location.protocol; //URL 的查詢字串。這個字串以問號開頭:"?q=javascript" window.location.search; //域名前指定的使用者名稱:"foouser" window.location.username; //域名前指定的密碼:"barpassword" window.location.password; //URL 的源地址。只讀:"http://www.wrox.com" window.location.origin;

``` 路由實現方式: 主要通過location.hash設定hash Url,也就是url的符號#後面的值。當雜湊值發生變化時,不會向伺服器請求傳送資料,可以通過hashchange事件來監聽hash的變化,實現相應的功能。

hash模式需要注意的幾個點 - 雜湊值不會隨請求傳送到伺服器 - 雜湊值會反映在瀏覽器url上 - 只修改瀏覽器的雜湊部分,按下回車,瀏覽器不會發送任何請求給伺服器,只會觸發hashchange事件並且修改location.hash的值 - html a標籤,設定id錨點,點選觸發時,瀏覽器會自動設定location.hash值,同時觸發hashchange事件,url上也會反映這一變化 - hash模式下,手動重新整理頁面不會向瀏覽器傳送請求,不會觸發hashchange事件,但是會觸發load事件

2.3.4history 物件

DOM window 物件通過 history 物件提供了對瀏覽器的會話歷史的訪問 。它暴露了很多有用的方法和屬性,允許你在使用者瀏覽歷史中向前和向後跳轉,同時——從 HTML5 開始——提供了對 history 棧中內容的操作。它表示當前視窗首次使用以來使用者的導航歷史記錄。因為 history 是 window 的屬性,所以每個 window 都有自己的 history 物件。出於安全考慮,這個物件不會暴露使用者訪問過的 URL,但可以通過它在不知道實際 URL 的情況下前進和後退。

常用API``js //返回歷史記錄中的網頁數量 window.history.length; //前進 window.history.forward(); //後退 window.history.back(); //前往相對於自身的第num個(前後取決於正負)頁面 window.history.go(num); //history堆疊最上層的狀態值 window.history.state; //向當前歷史記錄插入一個狀態值物件stateObj,並在網址後面新增url,title無實際意義 window.history.pushState(stateObj,title,url); //實現 //const state = { 'page_id': 1, 'user_id': 5 } //const url = 'hello-world.html' //history.pushState(state, '', url) //修改history物件的當前記錄 window.history.replaceState(stateObj,title,url); //原生用法 window.onpopstate = function(event) { alert(location: ${document.location}, state: ${JSON.stringify(event.state)}`) }

history.pushState({page: 1}, "title 1", "?page=1") history.pushState({page: 2}, "title 2", "?page=2") history.replaceState({page: 3}, "title 3", "?page=3") history.back() // alerts "location: http://example.com/example.html?page=1, state: {"page":1}" history.back() // alerts "location: http://example.com/example.html, state: null" history.go(2) // alerts "location: http://example.com/example.html?page=3, state: {"page":3}"

```

history實現路由

主要通過history.pushState/replceState向當前歷史記錄中插入狀態物件state,在瀏覽器前進、回退、跳轉等動作發生時觸發popState事件,此時可以通過解析popState事件回撥函式的event引數中的state物件,或者解析當前頁面url來實現路由。\ 建議解析url方式實現路由。如果沒有在頁面首次載入的時候設定pushState/replaceState,那麼首頁一般是沒有state物件的,在執行瀏覽器動作時,如果回退到首頁,那麼當前history物件的state屬性不存在,導致解析state報錯

補充: popstate 事件 每當活動的歷史發生變化時,popstate事件條目被提交給視窗物件。記錄當前活動的歷史記錄項是被pushState建立的,或者如果是由replaceState改變的,那麼popstate事件的狀態屬性state會包含一個當前歷史記錄狀態物件的拷貝

2.3.5兩種路由方式的差異以及需要注意的點

方式的異同

  • 頁面手動重新整理,hash模式不會向伺服器傳送請求,history模式會, 後者會導致404問題,需要後端配置,或者開啟NGINX配置
  • hash模式下url中的雜湊值不會發送到伺服器,history模式url全部會發送至伺服器
  • 設定location.hash和pushState都不會導致瀏覽器重新整理
  • 設定location.hash的時候會觸發hashchange事件和popstate事件
  • 僅當pushState函式設定url引數的值為hash格式時,瀏覽器動作發生會觸發hashchange事件,儘管location.hash值為空
  • a標籤錨點跳轉可以設定hash,觸發hashchange事件

pushState設定跨域請求

如果pushState的url為跨域網址,那麼會報錯.這樣設計的目的是防止惡意程式碼讓使用者以為他們是在另一個網站上。

前端路由的本質為url改變頁面不重新整理、達到檢視資料改變的結果。 以上就是前端路由的兩種模式,至於在vue,react 框架中的路由封裝基本原理都是基於這兩個模式、但是它們在路由中又封裝了動畫等一系列的東西、顯得比較複雜。

其他的一些window物件

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

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

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

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

3.今日精進

學習解決方案架構師13講。

原則定義-架構原則-原則質量-原則名稱-理由依據-資料原則。 原則是說話或行事所依據的法則或標準,原則是客觀、高度抽象、緩慢變化的。 知識壓縮是一個人核心能力的體現,知識提煉是一種自我昇華的過程。 企業原則是標準,架構原則決定產品走向,個人原則影響個人發展。

引用:提升自我認知,用自己的確定性對衝世界的不確定性。-馬志峰

Day2【2022年7月24日】

學習重點:複習HTML中的JS,js基礎語法及關鍵字,基本資料型別

1.基礎使用語法及關鍵字

1.1基本語法

JS語法創立之初借鑑了C語言/Java在語法上面會有相似性,JS語法在書寫時候會區分大小寫,Num和num是兩個不同的變數。 JS中的一些運算總結

2e3b10727661be3924998ccecd65dd8.png

1.2.i++ 和 ++i 區別

i++和++i的意思是一樣的,就是i = i + 1。當做運算子來說,就是a = i++ 和 a = ++i這樣的形式,情況就不一樣了。

a = i++的意思是,先把i的值賦給a,即a = i,再執行i = i + 1;a = ++i是先執行 i = i+1,再把i的值賦給a;如果一開始i=1。那麼執行a=i++這條語句之後,a=1,i=2;那麼執行a=++i這條語句之後,i=1,a=1;同理,i–和--i的用法也是一樣的。 例項: js //i++ i=1; a = i++; console.log(a); console.log(i); //1 // 2 //++i i=1; a = ++i; console.log(a); console.log(i); //2 //2

在迴圈體中的一些區別:

for迴圈中,for(int i = 0;i < 3;i++)和for(int i = 0;i < 3;++i)效果一樣

while(i++)是先用i的初始化值做迴圈變數再i+1

而while(++i)是先用i的初始值+1,再迴圈。

1.3=== 、 ==和Object.is()的區別

使用雙等號(==)進行相等判斷時,如果兩邊的型別不一致,則會進行強制型別轉化後再進行比較。

使用三等號(===)進行相等判斷時,如果兩邊的型別不一致時,不會做強制型別準換,直接返回 false。

使用 Object.is 來進行相等判斷時,一般情況下和三等號的判斷相同,它處理了一些特殊的情況,比如 -0 和 +0 判斷為相等(===判斷為相同),兩個 NaN 是相等的。

1.4常見的分支語句和迴圈遍歷運算子

1.4.1分支語句

c5a6ec08b040f1f6ccce8b003477a31.png

1.4.1.1分支語句if分支結構有三種:

單分支結構

if..

多分支結構

if..else..

if..else if..else..

1.4.1.2三元運算子:

用法 ```js //如果結果為真,則返回 value1,否則返回 value2。 var result = condition ? value1 : value2;

```

##### 1.4.1.3邏輯運算子

6c331bc8d6f8e99db2de5f521870d95.png

##### 1.4.1.4||運算子本質:

aa3b2112bb602a5572a34d54613dfc4.png

從左到右依次計算運算元。

處理每一個運算元時,都將其轉化為布林值(Boolean);

如果結果是 true,就停止計算,返回這個運算元的初始值。

如果所有的運算元都被計算過(也就是,轉換結果都是 false),則返回最後一個運算元。

總結:一個或運算 || 的鏈,將返回第一個真值,如果不存在真值,就返回該鏈的最後一個值。

補充: 如果||運算子與非布林值一起使用,它將返回一個非布林值。(忽略,不理解,有歧義) js console.log(true || null); // true console.log(true || undefined); // true console.log(true || 1); // true console.log(true || ''); // true console.log(false || ''); // '' console.log(false || 'hello'); // 'hello' console.log('hello' || false); // 'hello' console.log('' || false); // false console.log(true || "hello"); // true console.log("hello" || true); // true console.log(true || false); // true console.log(true || true); // true console.log('test' || 'hello'); // 'test' console.log(null || 'hello'); // 'hello' console.log(undefined || 'hello'); // 'hello' console.log(1 || 'hello'); // 1 console.log('' || 'hello'); // 'hello' console.log(false || 'hello'); // 'hello' console.log([] ||false ); // [] console.log({} || false); // {}

1.4.1.5&&運算子本質:

1e7d83bc5493c0c5b75805797fb8d1a.png

1659599075900.png 從左到右依次計算運算元。

在處理每一個運算元時,都將其轉化為布林值(Boolean);

如果結果是 false,就停止計算,並返回這個運算元的初始值(一般不需要獲取到初始值);

如果所有的運算元都被計算過(例如都是真值),則返回最後一個運算元。

總結:與運算返回第一個假值,如果沒有假值就返回最後一個值。

補充: 可以轉換為 false 的表示式示例如下:

false;

null;

NaN;

0;

空字串(""或''或``);

undefined.

舉例: js console.log(true && null); // null console.log(true && undefined); // undefined console.log(true && 1); // 1 console.log(true && ''); // '' console.log(false && ''); // false console.log(false && 'hello'); // false console.log('hello' && false); // false console.log('' && false); // '' console.log(true && "hello"); // "hello" console.log("hello" && true); // true console.log(true && false); // false console.log(true && true); // true console.log('test' && 'hello'); // hello console.log(null && 'hello'); // null console.log(undefined && 'hello'); // undefined console.log(1 && 'hello'); // hello console.log('' && 'hello'); // '' console.log(false && 'hello'); // false 優先順序: 數字越大優先順序越高,說明&&的優先順序大於|| 1659597163861.png js // &&優先順序高於|| //&& 返回第一個假值 都為真返回最後一個值 //|| 返回第一個為真的值 如果都為假則返回最後一個值 let a = 1; let b = 2; let c = a && b; // 2 let y = a || b; // 1 console.log(c); // 2 let d = a || b; console.log(d); // 1 let e = a && b || c; console.log(e); // 2 let f = a || b && c; console.log(f); // 1 let g = a || b || c; console.log(g); // 1 let h = a && b && c; console.log(h); // 2 let i = a && b && c || d; console.log(i); // 2 在實際開發中最好兩邊都是邏輯判斷空陣列和字串預設為true。

1.4.1.6switch語句

它是通過判斷表示式的結果(或者變數)是否等於case語句的常量,來執行相應的分支體的;與if語句不同的是,switch語句只能做值的相等判斷(使用全等運算子 ===),而if語句可以做值的範圍判斷。

一些注意事項 case穿透問題: 一條case語句結束後,會自動執行下一個case的語句; 這種現象被稱之為case穿透; break關鍵字 通過在每個case的程式碼塊後新增break關鍵字來解決這個問題; 注意事項:這裡的相等是嚴格相等,被比較的值必須是相同的型別才能進行匹配。

js // 語法 // switch (表示式/變數) { // case 常量1: // // 語句 // } #### 1.4.2迴圈和遍歷遍歷運算子

##### 1.4.2.1while while 語句是一種先測試迴圈語句(先判斷再執行),即先檢測退出條件,再執行迴圈體內的程式碼。語法如下: js //while迴圈 let i = 0; while(i<10){ console.log(i); i++; } //變數 i 從 0 開始,每次迴圈遞增 1。只要 i 小於 10,迴圈就會繼續。

1.4.2.2do while

do...while語句建立一個執行指定語句的迴圈,直到condition值為 false。在執行statement 後檢測condition,所以指定的statement至少執行一次。 js //模板 do statement while (condition); //用法 let b = 0; do{ console.log(i); i++; }

1.4.2.3 for

這也是一種先測試迴圈語句(先判斷再執行),比較常用,它有三個表示式組成,分別是宣告迴圈變數、判斷迴圈條件、更新迴圈變數。這三個表示式用分號分隔,都可以省略。但是中間的分號不能省略。其用法如下: js //for迴圈 for(let i=0;i<10;i++){ console.log(i); } for迴圈可以用來遍歷陣列,字串,類陣列,DOM節點等,不能用來遍歷物件,物件無length的概念。

結束迴圈的方式: ```js var arr = ['q','w','e','r','t']; for(var i=0, len = arr.length ; i< len ; i++){ console.log(arr[i]); } //continue 跳出當前迴圈直接到下一個 // q , w , e , r , t

for(var i=0, len = arr.length ; i< len ; i++){ if(i == 2){ continue; } console.log(arr[i]); } // q , w , r , t

//breake結束判斷後的迴圈 for(var i=0, len = arr.length ; i< len ; i++){ if(i == 2){ break; } console.log(arr[i]); } // q w ```

1.4.2.4 for in

以任意順序迭代一個物件的除Symbol以外的可列舉屬性,包括繼承的可列舉屬性。-mdn

官方的解釋比較抽象一些我們可以這樣理解:

可列舉可以理解為是否可以被遍歷被列舉出來,可列舉性決定了這個屬效能否被for…in查詢遍歷到。 js中的基本包裝型別的原型屬性是不可列舉如:number,Object。看後文迭代器補充介紹 在 JavaScript 中,基本型別是沒有屬性和方法的,但是為了便於操作基本型別的值,在呼叫基本型別的屬性或方法時 JavaScript 會在後臺隱式地將基本型別的值轉換為物件。 包裝類型別是針對number,string,boolean三種基本型別來說的三種基本型別,原則上只有值,沒有對應的方法,或屬性,但為了方便,咋們往往會直接呼叫一些,方法或屬性,例如str.replace(),true.toString()等方法.這就是包裝類帶來的好處。 用法:

```js //遍歷陣列 let arr = [1,2,3,4,5]; for(let item in arr){ console.log(item); } // 0 // 1 // 2 // 3 // 4

//遍歷物件 let obj = { name:'張三', age:18 } for(let key in obj){ console.log(key); } // name // age 如果 for-in 迴圈要迭代的變數是 null 或 undefined,則不執行迴圈體。 for in 不僅會遍歷當前的物件所有的可列舉屬性,還會遍歷其原型鏈上的可列舉屬性。 其遍歷陣列返回的是每個元素對應的索引值,遍歷物件返回的是鍵。 **結束迴圈的方式:**js let arr = [1,2,3,4,5]; for(let item in arr){ if(item>=2){ continue; } console.log(item); } //0 1 let arr = [1,2,3,4,5]; for(let item in arr){ if(item>=2){ break; } console.log(item); } //0 1 ``` 由此可以看出 for 和for in結束迴圈的方式相同

1.4.2.5 for of

for...of 語句建立一個迴圈來迭代可迭代的物件。在 ES6 中引入的 for...of 迴圈,以替代 for...in 和 forEach() ,並支援新的迭代協議。for...of 允許遍歷 Arrays(陣列), Strings(字串), Maps(對映), Sets(集合)等可迭代的資料結構等。

用法 ```js let arr = ['a', 'b', 'c', 'd'];

for (let a in arr) { console.log(a); // 0 1 2 3 }

for (let a of arr) { console.log(a); // a b c d } ``` 注意:

  • for…of適用遍歷 陣列/ 類陣列/字串/map/set 等擁有迭代器物件的集合
  • 它可以正確響應break、continue和return語句。
  • for-of迴圈不支援遍歷普通物件,因為沒有迭代器物件。如果想要遍歷一個物件的屬性,可以用for-in迴圈
  • 該方法只會遍歷當前物件的屬性,不會遍歷其原型鏈上的屬性。
  • for…of是作為ES6新增的遍歷方式,允許遍歷一個含有iterator介面的資料結構(陣列、物件等)並且返回各項的值,普通的物件用for..of遍歷是會報錯的。

如果需要遍歷的物件是類陣列物件,用Array.from轉成陣列即可。 js let obj = { 0:'one', 1:'two', length: 2 }; obj = Array.from(obj); for(var k of obj){ console.log(k) //one // two } 如果不是類陣列物件,就給物件新增一個[Symbol.iterator]屬性,並指向一個迭代器即可。 ```js //方法一: var obj = { a:1, b:2, c:3 };

obj[Symbol.iterator] = function(){ var keys = Object.keys(this); var count = 0; return { next(){ if(count<keys.length){ return {value: obj[keys[count++]],done:false}; }else{ return {value:undefined,done:true}; } } } };

for(var k of obj){ console.log(k); }

// 方法二 var obj = { a:1, b:2, c:3 }; obj[Symbol.iterator] = function*(){ var keys = Object.keys(obj); for(var k of keys){ yield [k,obj[k]] } };

for(var [k,v] of obj){ console.log(k,v); } 跳出迴圈:js var arr = ['a', 'b', 'c', 'd']; for (let a of arr) { console.log(a); // a b c d if ( a === 'a') { break; //跳出迴圈 } } // a var arr = ['a', 'b', 'c', 'd']; for (let a of arr) { console.log(a); // a b c d if ( a === 'a') { continue; //跳出迴圈 } } // a b c d ``` 由此可見for of 跳出迴圈的方式與前兩者一致

for...in和for...of的區別 for…of 是ES6新增的遍歷方式,允許遍歷一個含有iterator介面的資料結構(陣列、物件等)並且返回各項的值,和ES3中的for…in的區別如下

  • for…of 遍歷獲取的是物件的鍵值,for…in 獲取的是物件的鍵名;
  • for… in 會遍歷物件的整個原型鏈,效能非常差不推薦使用,而 for … of 只遍歷當前物件不會遍歷原型鏈;
  • 對於陣列的遍歷,for…in 會返回陣列中所有可列舉的屬性(包括原型鏈上可列舉的屬性),for…of 只返回陣列的下標對應的屬性值;

總結:for...in 迴圈主要是為了遍歷物件而生,不適用於遍歷陣列;for...of 迴圈可以用來遍歷陣列、類陣列物件,字串、Set、Map 以及 Generator 物件。

1.4.2.6 forEach()

對陣列每一項都執行傳入的函式,沒有返回值。 用法: ```js //陣列中的每個值都會呼叫回撥函式,回撥函式有三個引數: //currentValue: 必需。當前元素 //index: 可選。當前元素的索引值。 //arr: 可選。當前元素所屬的陣列物件 let arr = [1,2,3,4,5] arr.forEach((item, index, arr) => { console.log(index+":"+item) }) // 0:1 // 1:2 // 2:3 // 3:4 // 4:5 //該方法還可以有第二個引數,用來繫結回撥函式內部this變數(前提是回撥函式不能是箭頭函式,因為箭頭函式沒有this) let arr = [1,2,3,4,5] let arr1 = [9,8,7,6,5] arr.forEach(function(item, index, arr){ console.log(this[index]) // 9 8 7 6 5 }, arr1)

**forEach跳出迴圈的方式:**js var arr = [1,3,5,7,9]; var id = 5; try { arr.forEach(function (curItem, i) { if(curItem === 1) return; console.log(curItem) if (curItem === id) { throw Error(); //滿足條件,跳出迴圈 } }) } catch (e) { } //3 5 ```

for和forEach的區別

foreach()不能使用break和continue這兩個關鍵字,它實現break效果可以通過丟擲異常的方式,實現continue的效果可以直接使用return。

forEach的優勢就是,它傳入的是一個回撥函式,因此形成了一個作用域,它內部所定義的變數不會像for迴圈一樣汙染全域性變數。

forEach()本身無法跳出迴圈,必須遍歷所有的資料才能結束。

forEach會改變原陣列嗎?

這些方法都不改變呼叫它們的陣列。——js高階程式設計第四版是這麼解釋的

詳細的可以看下這篇文章# forEach到底可以改變原陣列嗎-->大致是說forEach會根據情況改變原陣列。(forEach 的基本原理也是for迴圈,使用arr[index]的形式賦值改變,無論什麼就都可以改變了。)

1.4.2.7 map()

map() 方法會返回一個新陣列,陣列中的元素為原始陣列元素呼叫函式處理後的值。該方法按照原始陣列元素順序依次處理元素。

注意: map() 不會對空陣列進行檢測,它會返回一個新陣列,不會改變原始陣列。

js //該方法的第一個引數為回撥函式,他有三個引數: //currentValue: 必須。當前元素的值 //index :可選。當前元素的索引值 //arr :可選。當前元素屬於的陣列物件 let arr13 = [3,2,4,1,4]; let arr14 = arr13.map(item=>item*2); console.log(arr14); // [6, 4, 8, 2, 8] let arr = [1, 2, 3]; //鏈式呼叫 arr13.map(item => item+1).map(item => item+1) // 返回值: [3, 4, 5] //第二個引數用來繫結引數函式內部的this變數,可選: var arr = ['a', 'b', 'c'] [1, 2].map(function (e) { return this[e]; }, arr) // 返回值: ['b', 'c'] 終止迴圈 ```js let arr = [1,2,3,4]

// item:當前項,index:當前索引,arr:原陣列

const result = arr.map((item, index, arr)=> {

if(item >2) {

return item

  }

})

console.log(result); //[ undefined, undefined, 3, 4 ] //return 不會終止迴圈(foreach 同理)

var arr = [1,3,5,7,9]; var id = 5; try { arr.map(function (curItem, i) { if(curItem === 1) return; console.log(curItem) if (curItem === id) { throw Error(); //滿足條件,跳出迴圈 } }) } catch (e) { } //3 5

``` map和foreach一樣無法跳出迴圈,只能通過丟擲異常的方式結束迴圈。

forEach和map方法有什麼區別

這方法都是用來遍歷陣列的,兩者區別如下: - forEach()方法會針對每一個元素執行提供的函式,該方法沒有返回值,是否會改變原陣列取決於陣列元素的型別是基本型別還是引用型別,詳細解釋可參考文章:《forEach到底可以改變原陣列嗎》 - map()方法不會改變原陣列的值,返回一個新陣列,新陣列中的值為原陣列呼叫函式處理之後的值;

1.4.2.8 filter()

對陣列每一項都執行傳入的函式,函式返回 true 的項會組成陣列之後返回。用於過濾陣列,滿足條件的元素會被返回。它的引數是一個回撥函式,所有陣列元素依次執行該函式,返回結果為true的元素會被返回。該方法會返回一個新的陣列,不會改變原陣列。不符合條件的陣列,可以用來刪除或者替換。

```js //該方法的第一個引數是回撥函式,它有三個引數: //currentValue: 必須。當前元素的值 //index :可選。當前元素的索引值 //arr :可選。當前元素屬於的陣列物件 let arr1 = [1, 2, 3, 4, 5] arr1.filter(item => item > 2) // [3, 4, 5] //同樣,他也有第二個引數,用來繫結引數函式內部的this變數。 //實際運用

let arr2 = [1, undefined, 2, null, 3, false, '', 4, 0] arr2.filter(Boolean) // [1, 2, 3, 4] ```

1.4.2.9 every()

every遍歷陣列中每一個元素是否能通過條件的測試返回bool值,對陣列每一項都執行傳入的函式,如果對每一項函式都返回 true,則這個方法返回 true。不改變呼叫它的陣列。 js let arr21 = [3,2,4,1,4]; let arr22 = arr21.every(item=>item>2); console.log(arr22); //false

1.4.2.10 some()

some遍歷陣列中有一個元素是否能通過條件的測試返回bool值,對陣列每一項都執行傳入的函式,如果有一項函式返回 true,則這個方法返回 true。不改變呼叫它的陣列。 js let arr = [1, 2, 3, 4, 5] arr.some(item => item > 4) // true some()和every()方法都接受一個函式作為引數,該引數函式可以接收三個引數,分別是當前陣列元素、當前元素索引、當前元素所在陣列。

1.4.2.11 find()

返回通過函式內判斷的陣列的第一個元素的值。該方法為陣列中的每個元素都呼叫一次函式執行: 當陣列中的元素在測試條件時返回 true 時, find() 返回符合條件的元素,之後的值不會再呼叫執行函式。

注意:  find() 對於空陣列,函式是不會執行的。 該方法並沒有改變陣列的原始值。 如果沒有符合條件的元素返回 undefined js //該方法的第一個引數也是一個函式,它有三個引數: //currentValue :必需。當前元素 //index :可選。當前元素的索引 //arr :可選。當前元素所屬的陣列物件 let arr = [1, 2, 3, 4, 5] arr.find(item => item > 2) // 3

1.4.2.12 findIndex()

findIndex() 方法返回傳入一個測試函式符合條件的陣列第一個元素位置(索引)。該方法為陣列中的每個元素都呼叫一次函式執行:

當陣列中的元素在函式條件時返回 true 時, findIndex() 返回符合條件的元素的索引位置,之後的值不會再呼叫執行函式。 如果沒有符合條件的元素返回 -1

注意:  findIndex() 對於空陣列,函式是不會執行的。該方法並沒有改變陣列的原始值。 ```js //該方法的第一個引數也是一個函式,它有三個引數: //currentValue :必需。當前元素 //index :可選。當前元素的索引 //arr :可選。當前元素所屬的陣列物件 let arr = [1, 2, 3, 4, 5] arr.findIndex(item => item > 2) // 2

``` find()返回第一個匹配的元素,findIndex()返回第一個匹配元素的索引。這兩個方法也都接收第二個可選的引數,用於指定斷言函式內部 this 的值。

1.4.2.13 reduce()

reduce 為陣列中的每一個元素依次執行回撥函式,不包括陣列中被刪除或從未被賦值的元素,接受四個引數:初始值(或者上一次回撥函式的返回值),當前元素值,當前索引,呼叫 reduce 的陣列。 ```js //callback (執行陣列中每個值的函式,包含四個引數) //previousValue (上一次呼叫回撥返回的值,或者是提供的初始值(initialValue)) //currentValue (陣列中當前被處理的元素) //index (當前元素在陣列中的索引) //array (呼叫 reduce 的陣列) //initialValue (作為第一次呼叫 callback 的第一個引數。)

var arr = [1, 2, 3, 4] var sum = arr.reduce((prev, cur, index, arr) => { console.log(prev, cur, index); return prev + cur; }) console.log(arr, sum); //1 2 1 //3 3 3 2 //3 6 4 3 // [1, 2, 3, 4] 10 //加初始值 var arr = [1, 2, 3, 4] var sum = arr.reduce((prev, cur, index, arr) => { console.log(prev, cur, index); return prev + cur; }, 5) console.log(arr, sum);

//3 5 1 0 //3 6 2 1 //3 8 3 2 //3 11 4 3 // [1, 2, 3, 4] 15 ``` 通過上面的兩個例子,我們可以得出結論:如果沒有提供initialValue,reduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供initialValue,從索引0開始

注意,該方法如果新增初始值,就會改變原陣列,將這個初始值放在陣列的最後一位。

reduce()方法從陣列第一項開始遍歷到最後一項。而 reduceRight()從最後一項開始遍歷至第一項。 終止迴圈: ```js reduce跳出迴圈 var arr = [1, 2, 3, 4]; var sum = arr.reduce((prev, cur, index, arr) => { console.log(prev, cur, index); if (index > 1) { throw new Error('error'); } return prev + cur; } , 0) console.log(arr, sum);

// [1, 2, 3, 4] 10; ``` 由此可見reduce迴圈不可終止

1.4.2.14迴圈的跳轉控制
  • break: 直接跳出迴圈, 迴圈結束

  • break 某一條件滿足時,退出迴圈,不再執行後續重複的程式碼

  • continue: 跳過本次迴圈次, 執行下一次迴圈體

  • continue 指令是 break 的“輕量版”。

  • continue 某一條件滿足時,不執行後續重複的程式碼

總結:

514fa1d67563347b70d2d5f19a2d9d3.png

跳出迴圈詳細情況可參考這篇文章-->JS迴圈跳出方法

### 1.5關鍵字

1.5.1什麼是關鍵字

關鍵字就是計算機中面向硬體的指令,簡單的說就是方面使用者和計算機進行互動的速記符,一般要求程式設計時使用者定義的變數不能和關鍵字重名,否則程式執行出錯。

1.5.2JS的關鍵字

break do in typeof

case else instanceof var

catch export new void

class extends return while

const finally super with

continue for switch yield

debugger function this

default if throw

delete import try

2.JS基本資料型別(待完善)

Number

String 屬性: String.length 該屬性返回字串中字元編碼單元的數量。靜態屬性返回1,空字串返回0. js String.length //1 ''.length //0

JS 中的字串是不可變的(immutable),意思是一旦建立,它們的值就不能變了。要修改 某個變數中的字串值,必須先銷燬原始的字串,然後將包含新值的另一個字串儲存到該變數(建立一個新的字串)。

Boolean

Undefined

Null

Object Object是個大類,function函式、array陣列、date日期...等都歸屬於Object BigInt

Symbol

共八種其中Symbol 和 BigInt 是ES6 中新增的資料型別。 其中 Symbol 和 BigInt 是ES6 中新增的資料型別:

  • Symbol 代表建立後獨一無二且不可變的資料型別,它主要是為了解決可能出現的全域性變數衝突的問題。為了避免重名而被覆蓋。 Symbol值是通過Symbol函式來生成的,生成後可以作為屬性名;也就是在ES6中,物件的屬性名可以使用字串,也可以使用Symbol值;Symbol即使多次建立值,它們也是不同的:Symbol函式執行後每次創建出來的值都是獨一無二的;建立相同的Symbol可以使用Symbol.for方法來做到這一點;並且我們可以通過Symbol.keyFor方法來獲取對應的key; ```js //symbol

const s1 = Symbol.for('abc'); const s2 = Symbol.for('abc');

console.log(s1 === s2); // true const key = Symbol.keyFor(s1); console.log(key); // abc

const s3 = Symbol.for('abc');

console.log(s1 === s3); // true ```

  • BigInt 是一種數字型別的資料,它可以表示任意精度格式的整數,使用 BigInt 可以安全地儲存和操作大整數,即使這個數已經超出了 Number 能夠表示的安全整數範圍。
  • 棧:原始資料型別(Undefined、Null、Boolean、Number、String、Symbol、BigInt)這幾種基本資料型別它們是直接按值存放的,所以可以直接訪問。
  • 堆:引用資料型別(物件、陣列和函式) 兩種型別的區別在於儲存位置的不同:
  • 原始資料型別直接儲存在棧(stack)中的簡單資料段,佔據空間小、大小固定,屬於被頻繁使用資料,所以放入棧中儲存;
  • 引用資料型別儲存在堆(heap)中的物件,佔據空間大、大小不固定。如果儲存在棧中,將會影響程式執行的效能;引用資料型別在棧中儲存了指標,該指標指向堆中該實體的起始地址。當直譯器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。

堆和棧的概念存在於資料結構和作業系統記憶體中,在資料結構中:

  • 在資料結構中,棧中資料的存取方式為先進後出。
  • 堆是一個優先佇列,是按優先順序來進行排序的,優先順序可以按照大小來規定。

在作業系統中,記憶體被分為棧區和堆區:

  • 棧區記憶體由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
  • 堆區記憶體一般由開發者分配釋放,若開發者不釋放,程式結束時可能由垃圾回收機制回收。
  • 堆和棧有什麼區別:棧: 先進後出、像杯子的水先進的後出、堆: 先進的先出 像水管先進的先出

基本型別:String,Number,Boolean,Null,Undefined,symbol,BigInt這7種基本資料型別它們是直接按值存放的,所以可以直接訪問。所有基本型別的值都是不可改變的。但需要注意的是,基本型別本身和一個賦值為基本型別的變數的區別。變數會被賦予一個新值,而原值不能像陣列、物件以及函式那樣被改變。

引用型別:Function,Array,Object,當我們需要訪問這三種引用型別的值時,首先得從棧中獲得該物件的地址指標,然後再從堆記憶體中取得所需的資料。

棧(stack):由作業系統自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧; 堆(heap):一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收,分配方式倒是類似於連結串列。

棧賦值的是複製例如: ```js       var a = 10;

var b = a;

console.log(b);//10

//如果a的值改變,b的值不會變;

a = 15;

console.log(b);//10 ```

堆是和棧結合使用: ```js     //陣列的是放在堆裡,但變數a拿到的是一個地址索引,指向這個陣列

var a = [1,2,3];

//把地址索引複製給b,不是複製資料內容

var b = a;

console.log(b);//[1,2,3]

//從新給a賦值,a改變了地址,無法檢視原資料

a = [3,4,5];

console.log(b);[1,2,3] ```

3.今日精進

今天學習了華為佈局物聯網萬億賽道的商業思維模式。在車聯網汽車智慧化時代,華為選擇造智慧電動車的靈魂,而不造智慧電動車的肉體。華為跟各大傳統汽車廠商合作,幫助車企造車,把智慧電動車燒錢多的三電系統交給傳統車企去做。這類似於走谷歌安卓系統的路。華為一切都圍繞著任正非佈局的核心公司戰略,從技術導向轉向伸出導向。新能源汽車正在迅速崛起,新能源汽車比燃油車更加智慧,汽車智慧化是車聯網未來的主戰場。華為正圍繞任正非制定的原則穩步前進,必將會引領一個圍繞鴻蒙系統由車聯網到手機、手錶、汽車、智慧家居一個全場景聯動的萬物互聯新時代。

Day3【2022年7月25日】

學習重點:判斷資料型別的方法

1.Object.prototype.toString.call()

使用 Object 物件的原型方法 toString 來判斷資料型別: 原理:

為什麼需要Object.prototype?

Object物件本身就有一個toString()方法,返回的是當前物件的字串形式,原型上的toString()返回的才是我們真正需要的包含物件資料型別的字串。

為什麼需要call?

由於Object.prototype.toString()本身允許被修改,像Array、Boolean、Number的toString就被重寫過,所以需要呼叫Object.prototype.toString.call(arg)來判斷arg的型別,call將arg的上下文指向Object,所以arg執行了Object的toString方法。

至於call,就是改變物件的this指向,當一個物件想呼叫另一個物件的方法,可以通過call或者apply改變其this指向,將其this指向擁有此方法的物件,就可以呼叫該方法了 原文

js function type(obj){ return Object.prototype.toString.call(obj).slice(8,-1); } console.log(type('abc'));// String console.log(type(123));//Number console.log(type([1,2,3]));//Array console.log(type(new Date()));//Date console.log(type(function(){}));//Function console.log(type(function(){this.name="22";}));//Function console.log(type({name:"33"}));//Object console.log(type(null));//Null console.log(type(undefined));//Undefined console.log(type(NaN));//Number console.log(type(true));//Boolean console.log(type(false));//Boolean console.log(type(Symbol("name")));//Symbol console.log(type(/^abc$/));//RegExp//正則

2.typeof 關鍵字:

其中陣列、物件、null都會被判斷為object,其他判斷都正確。返回的是字串 ```js console.log(typeof('abc'));//string console.log(typeof(123));//Vnumber console.log(typeof([1,2,3]));//object console.log(typeof(new Date()));//object console.log(typeof(function(){}));//function console.log(typeof(function(){this.name="22";}));//function console.log(typeof({name:"33"}));//object console.log(typeof(null));//object console.log(typeof(undefined));//undefined console.log(typeof(NaN));//number console.log(typeof(true));//boolean console.log(typeof(false));//boolean console.log(typeof(Symbol("name")));//symbol console.log(typeof(/^abc$/));//object

```

3.instanceof 關鍵字:

instanceof可以正確判斷物件的型別,其內部執行機制是判斷在其原型鏈中能否找到該型別的原型。 instanceof只能正確判斷引用資料型別,而不能判斷基本資料型別。instanceof運算子可以用來測試一個物件在其原型鏈中是否存在一個建構函式的 prototype屬性。返回Boolean值(基本型別返回false,引用類似返回true) js console.log('abc' instanceof String);//false console.log(123 instanceof Number);//false console.log([1,2,3] instanceof Array);//true console.log(new Date() instanceof Date);// true console.log(function(){} instanceof Function);// true console.log(function(){this.name="22";} instanceof Object);// true console.log({name:"33"} instanceof Object);// true console.log(NaN instanceof Number);// false console.log(true instanceof Boolean);// false console.log(false instanceof Boolean);// false console.log(Symbol("name") instanceof Symbol);// false console.log(/^abc$/ instanceof RegExp);// true

4.constructor 關鍵字:

constructor有兩個作用,一是判斷資料的型別,二是物件例項通過 constructor物件訪問它的建構函式。需要注意,如果建立一個物件來改變它的原型,constructor就不能用來判斷資料型別了: js console.log(('abc').constructor === String);//true console.log((123).constructor === Number);//true console.log(([1,2,3]).constructor === Array);// true console.log((new Date()).constructor === Date);//true console.log((function(){}).constructor === Function);//true console.log((function(){this.name="22";}).constructor === Object);// false console.log(({name:"33"}).constructor === Object);// true console.log((NaN).constructor === Number);// true console.log((true).constructor === Boolean);// true console.log((false).constructor === Boolean);// true console.log((Symbol("name")).constructor === Symbol);// true console.log((/^abc$/).constructor === RegExp);// true 改變原型 ```js function Fn(){};

Fn.prototype = new Array();

var f = new Fn();

console.log(f.constructor===Fn); // false console.log(f.constructor===Array); // true ```

5.今日精進

leetCode 2249. 統計圓內格點數目解題思維:根據公式 (x-y)2 + (x+y)2 = r**2 在x/ymin與x/ymax區間裡面遍歷查詢符合的點count返回。

Day4【2022年7月26日】

學習重點:執行上下文、作用域及閉包

1.執行上下文

執行上下文(以下簡稱“上下文”)的概念在 JavaScript 中是頗為重要的。變數或函式的上下文決定 、了它們可以訪問哪些資料,以及它們的行為。每個上下文都有一個關聯的變數物件(variable object), 而這個上下文中定義的所有變數和函式都存在於這個物件上。雖然無法通過程式碼訪問變數物件,但後臺 處理資料會用到它。

執行上下文可以理解為當前程式碼的執行環境,它會形成一個作用域。

1.1. 執行上下文型別型別

(1)全域性執行上下文

任何不在函式內部的都是全域性執行上下文,它首先會建立一個全域性的window物件,並且設定this的值等於這個全域性物件,一個程式中只有一個全域性執行上下文。全域性上下文就是我們常說的 window 物件,因此所有通過 var 定義的全域性變數和函式都會成為 window 物件的屬性和方法。 組成: - 全域性物件(瀏覽器裡是 Window, Node 環境下是 Global) - this 變數。這裡的 this ,指向的還是全域性變數

(2)函式執行上下文

當一個函式被呼叫時,就會為該函式建立一個新的執行上下文,函式的上下文可以有任意多個。 組成: - 函式上下文建立引數物件(arguments); - this。動態的,如果它被一個引用物件呼叫,那麼 this 就指向這個物件;否則,this 的值會被設定為全域性物件或者 undefined(在嚴格模式下)

(3)Eval 執行上下文

執行在eval函式中的程式碼會有屬於他自己的執行上下文,用的比較少了瞭解即可,跟面試官吹逼的時候可以用。

1.2. 執行上下文棧--執行上下文的管理

  • JavaScript引擎使用執行上下文棧來管理執行上下文
  • 當JavaScript執行程式碼時,首先遇到全域性程式碼,會建立一個全域性執行上下文並且壓入執行棧中,每當遇到一個函式呼叫,就會為該函式建立一個新的執行上下文並壓入棧頂,引擎會執行位於執行上下文棧頂的函式,當函式執行完成之後,執行上下文從棧中彈出,繼續執行下一個上下文。當所有的程式碼都執行完畢之後,從棧中彈出全域性執行上下文。

``` function testA() { console.log('執行第一個測試函式的邏輯'); testB(); console.log('再次執行第一個測試函式的邏輯'); }

  function testB() {
    console.log('執行第二個測試函式的邏輯');
  }

  testA();

//執行第一個測試函式的邏輯 //執行第二個測試函式的邏輯 //再次執行第一個測試函式的邏輯 ``` 執行順序跟壓入棧的順序相反所以會先執行B-A-全域性。 特點: - 棧,先進後出 - 棧底永遠是全域性執行上下文環境window,其餘的都是函式執行上下文 - 當前正在執行的永遠是棧頂的執行上下文

1.3. 建立執行上下文

建立執行上下文有兩個階段:建立階段執行階段

1)建立階段

(1)this繫結

  • 在全域性執行上下文中,this指向全域性物件(window物件)
  • 在函式執行上下文中,this指向取決於函式如何呼叫。如果它被一個引用物件呼叫,那麼 this 會被設定成那個物件,否則 this 的值被設定為全域性物件或者 undefined

(2)建立詞法環境元件

  • 詞法環境是一種有識別符號——變數對映的資料結構,識別符號是指變數/函式名,變數是對實際物件或原始資料的引用。
  • 詞法環境的內部有兩個元件:加粗樣式:環境記錄器:用來儲存變數個函式宣告的實際位置外部環境的引用:可以訪問父級作用域

(3)建立變數環境元件

  • 變數環境也是一個詞法環境,其環境記錄器持有變數宣告語句在執行上下文中建立的繫結關係。

2)執行階段

此階段會完成對變數的分配,最後執行完程式碼。

整個過程是一個動態的過程。

簡單來說執行上下文就是指:

在執行一點JS程式碼之前,需要先解析程式碼。解析的時候會先建立一個全域性執行上下文環境,先把程式碼中即將執行的變數、函式宣告都拿出來,變數先賦值為undefined,函式先宣告好可使用。這一步執行完了,才開始正式的執行程式。

  • 全域性上下文:變數定義,函式宣告
  • 函式上下文:變數定義,函式宣告,thisarguments

在一個函式執行之前,也會建立一個函式執行上下文環境,跟全域性執行上下文類似,不過函式執行上下文會多出this、arguments和函式的引數。上下文在其所有程式碼都執行完畢後會被銷燬,包括定義在它上面的所有變數和函式(全域性上下文在應用程式退出前才會被銷燬,比如關閉網頁或退出瀏覽器)。

2.作用域

2.1什麼是作用域

程式語言最基本的能力都是能夠儲存變數當中的值、並且允許我們對這個變數的值進行訪問和修改。那約定程式語言把變數放在那裡,程式如何找到,怎麼對變數進行儲存訪問變數的規則就叫作用域。一般情況作用域的時候,指的是這個規則約束下的一個變數、函式、識別符號可以被訪問的區域。就是能夠儲存變數當中的值,並且能在之後對這個 值進行訪問或修改。作用域分為三類:全域性作用域、函式作用域、塊級作用域(ES6後)。

2.2全域性作用域

2.2.1宣告在任何函式之外的頂層作用域的變數就是全域性變數,這樣的變數擁有全域性作用域:

```js var name = 'xiuyan'; // 全域性作用域內的變數

// 函式作用域 function showName() { console.log(name); }

// 塊作用域 { name = 'BigBear' }

showName(); // 輸出 'BigBear'

window.name // 'BigBear' ``` 如果把 var改成let還會輸出 'BigBear'嗎? 答案是會,但是不會掛在在window上,window.name 為空,說明宣告的不是全域性遍歷,具體為什麼會這樣js高階程式設計已經給出解釋了:

所有通過 var 定義的全域性變數和函式都會成為 window 物件的屬性和方法。使用 let 和 const 的頂級宣告不會定義在全域性上下文中,但在作用域鏈解析上效果是一樣的。

2.2.2 所有未定義直接賦值的變數自動宣告為全域性作用域(不推薦這麼做,不規範)

js function aa(){ //報錯 預設為函式內var宣告的區域性變數 // var a =1; // a += 1; // a = a+1; // a++; //不報錯,因為a是全域性變數 // a=1; console.log(a); } aa(); console.log(a);

2.2函式作用域

在函式內部定義的變數,擁有函式作用域,稱為函式作用域。一般作用域是分層的,內層作用域可以訪問外層作用域,反之不行。 ```js var name = 'xiuyan'; // name 是全域性變數 function showName(myName) { // myName 是傳入 showName 的區域性變數 console.log(myName); } function sayHello() { // hello 被定義成區域性作用域變數 var helloString = 'hello everyone'; console.log(helloString); }

showName(name); // 輸出 'xiuyan' sayHello(); // 輸出 'hello everyone' console.log(myName); // 丟擲錯誤:myName 在全域性作用域未定義 console.log(helloString); // 丟擲錯誤:hello 在全域性作用域未定義

{ console.log(helloString, myName) // 丟擲錯誤 } ``` 在這個例子裡,myName 和 hello 都是在函式內部定義的變數,它們就被“畫地為牢” ,作用域僅侷限於函式內部。全域性作用域和塊作用域裡都訪問不到它們。

2.3塊級作用域

使用ES6中新增的let和const指令可以宣告塊級作用域,塊級作用域可以在函式中建立也可以在一個程式碼塊中的建立(由{ }包裹的程式碼片段就稱為塊級作用域),let和const宣告的變數不會有變數提升,也不可以重複宣告,在迴圈中比較適合繫結塊級作用域,這樣就可以把宣告的計數器變數限制在迴圈內部。 js { var a = 1; let b = 2; } console.log(a);//1 console.log(b);//報錯 const同理 對於物件來說不是一個塊級作用域如下箭頭函式查詢規則可看出。 ```js var obj = { name: "obj", foo: () => { var bar = () => { console.log("bar:", this) } return bar

}

} var fn = obj.foo() fn.apply("bbb")//window ``` 可以看出,塊作用域內的變數只要出了自己被定義的那個程式碼塊,那麼就無法訪問了,而var沒有塊級作用域的概念。這點和函式作用域比較相似 —— 它們都只在“自己的地盤”上生效,所以它們也統稱為” 區域性作用域 “。

3.作用域鏈

在當前作用域中查詢所需變數,但是該作用域沒有這個變數,那這個變數就是自由變數。如果在自己作用域找不到該變數就去父級作用域查詢,依次向上級作用域查詢,直到訪問到window物件就被終止,這一層層的關係就是作用域鏈。

作用域鏈的作用是保證對執行環境有權訪問的所有變數和函式的有序訪問,通過作用域鏈,可以訪問到外層環境的變數和函式。

作用域鏈的本質上是一個指向變數物件的指標列表。變數物件是一個包含了執行環境中所有變數和函式的物件。作用域鏈的前端始終都是當前執行上下文的變數物件。全域性執行上下文的變數物件(也就是全域性物件)始終是作用域鏈的最後一個物件。當查詢一個變數時,如果當前執行環境中沒有找到,可以沿著作用域鏈向後查詢。

舉例: ```js function addA(a) { console.log(a + b) console.log(c) // 報錯 }

var b = 1

addA(2) //3 ``` 關係示意圖如下所示:

1658822923526.png

addA 這個函式裡訪問變數 b 的時候,函式作用域內並沒有對 b、c 這兩個變數作定義,所以一開始肯定是找不到的。要想找到 b、c 去上層作用域(全域性作用域找),找到了 b ,那麼就可以直接拿來用了;沒找到 c,並且全域性作用域已經沒有上層作用域了(頭探不出去了),那就歇菜,報錯!這就是上文“執行階段 ”裡我們描述的那個過程。在這個查詢過程中,層層遞進的作用域,就形成了一條作用域鏈。 關係圖如下:

1658823148965.png

4.閉包

4.1什麼是閉包?

一個變數在函式中被使用,但它既不是函式引數、也不是函式的區域性變數,而是一個不屬於當前作用域的變數,此時它相對於當前作用域來說,就是一個自由變數函式中引用了自由變數的函式,就叫閉包。

簡單來說: 閉包是指有權訪問另一個函式作用域中變數的函式,建立閉包的最常見的方式就是在一個函式內建立另一個函式,建立的函式可以訪問到當前函式的區域性變數。

為了解決突破函式作用域的限制才有了閉包這種概念 這裡先來看一下閉包的定義,分成兩個:在電腦科學中和在JavaScript中。

在電腦科學中對閉包的定義(維基百科):

  • 閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函式閉包(function closures);

  • 是在支援 頭等函式 的程式語言中,實現詞法繫結的一種技術;

  • 閉包在實現上是一個結構體,它儲存了一個函式和一個關聯的環境(相當於一個符號查詢表);

  • 閉包跟函式最大的區別在於,當捕捉閉包的時候,它的 自由變數 會在捕捉時被確定,這樣即使脫離了捕捉時的上下文,它也能照常執行;

閉包的概念出現於60年代,最早實現閉包的程式是 Scheme,那麼我們就可以理解為什麼JavaScript中有閉包:

  • 因為JavaScript中有大量的設計是來源於Scheme的;

我們再來看一下MDN對JavaScript閉包的解釋:

  • 一個函式和對其周圍狀態(lexical environment,詞法環境)的引用捆綁在一起(或者說函式被引用包圍),這樣的組合就是閉包(closure);

  • 也就是說,閉包讓你可以在一個內層函式中訪問到其外層函式的作用域;

  • 在 JavaScript 中,每當建立一個函式,閉包就會在函式建立的同時被創建出來;

那麼我的理解和總結:

一個普通的函式function,如果它可以訪問外層作用域的自由變數,那麼這個函式和周圍環境就是一個閉包;

  • 從廣義的角度來說:JavaScript中的函式都是閉包;

  • 從狹義的角度來說:JavaScript中一個函式,如果訪問了外層作用域的變數,那麼它是一個閉包;

4.2閉包的運用

(1) 模擬私有變數的實現

為什麼要有私有變數:像有些比較敏感的資訊如果定義在函式中可直接被訪問是非常危險的,涉及到安全問題。在 JS 中,無法像java通過 private 這樣的關鍵字直接在類裡宣告變數的私有性。

通過使用閉包,可以通過在外部呼叫閉包函式,從而在外部訪問到函式內部的變數,可以使用這種方法來建立私有變數。

(2)偏函式與柯里化(持久保持)

柯里化

柯里化是把接受 n 個引數的 1 個函式改造為只接受 1個引數的 n 個互相巢狀的函式的過程。也就是 fn (a, b, c)fn(a,b,c) 會變成 fn (a)(b)(c)fn(a)(b)(c)。

```js

function generateName(prefix) {
return function(type) { return function (itemName) { return prefix + type + itemName }
} }

// 生成大賣網商品名專屬函式 var salesName = generateName('大賣網')

// “記住”prefix,生成大賣網母嬰商品名專屬函式 var salesBabyName = salesName('母嬰')

// "記住“prefix和type,生成洗菜網生鮮商品名專屬函式 var vegFreshName = generateName('洗菜網')('生鮮')

// 輸出 '大賣網母嬰奶瓶' salesBabyName('奶瓶') // 輸出 '洗菜網生鮮菠菜' vegFreshName('菠菜')

// 啥也不記,直接生成一個商品名 var itemFullName = generateName('洗菜網')('生鮮')('菠菜') **偏函式** 原有的一次性傳三個改造成先傳一個引數在傳兩個引數js function generateName(prefix, type, itemName) { return prefix + type + itemName }

// 呼叫時一口氣傳入3個入參 var itemFullName = generateName('大賣網', '母嬰', '奶瓶') 改造後js function generateName(prefix) { return function(type, itemName) { return prefix + type + itemName } }

// 把3個引數分兩部分傳入 var itemFullName = generateName('大賣網')('母嬰', '奶瓶') ``` 柯里化和偏函式的區別: 柯里化是將一個 n 個引數的函式轉換成 n 個單引數函式。你有 10 個入參,就得巢狀 10 層函式,且每層函式都只能有 1 個入參。它的目標就是把函式的入參拆解為精準的 n 部分。

偏函式應用相比之下就 “隨意” 一些了。偏函式是說,固定你函式的某一個或幾個引數,然後返回一個新的函式(這個函式用於接收剩下的引數)。你有 10 個入參,你可以只固定 2 個入參,然後返回一個需要 8 個入參的函式 —— 偏函式應用是不強調 “單引數” 這個概念的。它的目標僅僅是把函式的入參拆解為兩部分。

4.2例項:

防抖節流是一種閉包的實際運用。(面試可提) ```js function addABC(){ var a = 1,b = 2;

function add(){
  return a+b+c;
}
return add;

}

var c = 3

var globalAdd = addABC()

console.log(globalAdd()) // 6 ` 面試的坑: 下面程式碼輸出什麼js for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); }

console.log(i); //5 5 5 5 5 5 換成letjs for (let i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 1000); }

console.log(i); // Uncaught ReferenceError: i is not defined // 0 // 1 // 2 // 3 // 4 ``` 這個函式第一次被執行,也是 1000ms 以後的事情了(setTimeout非同步),此時它試圖向外、向上層作用域(這裡就是全域性作用域)去找一個叫 i 的變數。此時 for 迴圈早已執行完畢, i 也進入了塵埃落定的最終狀態 ——5。所以 1000ms 後,當這個函式第一次真正被執行的時候,引用到的 i 值已經是 5 了。

改造方法: 第一種

可以把 setTimeout 函式的第三個引數利用起來。setTimeout 從第三個入參位置開始往後,是可以傳入無數個引數的。這些引數會作為回撥函式的附加引數存在。 在這裡我們可以把每一輪迴圈裡 i 的值,存進 setTimout 的第三個引數裡: js for (var i = 0; i < 5; i++) { setTimeout(function(j) { console.log(j); }, 1000, i); } 第二種

在 setTimeout 外面再套一層函式,利用這個外部函式的入參來快取每一個迴圈中的 i 值: ```js var output = function (i) { setTimeout(function() { console.log(i); }, 1000); };

for (var i = 0; i < 5; i++) { // 這裡的 i 被賦值給了 output 作用域內的變數 i output(i);
} ``` 第三種

和第二種思路比較相似,同樣是在 setTimeout 外面再套一層函式,只不過這個函式是一個立即執行函式。利用立即執行函式的入參來快取每一個迴圈中的 i 值: js for (var i = 0; i < 5; i++) { // 這裡的 i 被賦值給了立即執行函式作用域內的變數 j (function(j) { setTimeout(function() { console.log(j); }, 1000); })(i); }

閉包的弊端

1)由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體洩露。解決方法是,在退出函式之前,將不使用的區域性變數全部刪除。

2)閉包會在父函式外部,改變父函式內部變數的值。所以,如果你把父函式當作物件(object)使用,把閉包當作它的公用方法(Public Method),把內部變數當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函式內部變數的值。

5.今日精進

leetCode 劍指 Offer II 079. 所有子集(回溯演算法解題)。

提升自己前要做好計劃,合理安排時間,個人精力是有限的,抓住重點,做好筆記,定時覆盤,最重要的是堅持。

Day5【2022年7月27日】

學習重點:定義變數方式(var、let、const)

js有 3 個關鍵字可以宣告變數:var、const 和 let。其中,var 在 ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6 (ES6)及更晚的版本中使用。

1.var

1.1作用域

var關鍵字用來宣告變數時如果在函式中定義是函式作用域中的區域性變數,數執行完的時候會銷燬。在全域性來定義變數是會掛載在window/global下的全域性作用域中的全域性變數。 示例如下 js function test() { var message = "hi"; // 區域性變數 } test(); console.log(message); // 出錯! 這裡定義的是一個區域性作用域,呼叫之後變數隨即被銷燬,因此示例中的最後一行會導致錯誤。不過,在函式內定義變數時省略 var 操作符,可以建立一個全域性變數(不推薦這麼做,嚴格模式下會直接報錯): ```js function test() { message = "hi"; // 全域性變數 } test(); console.log(message); // "hi"

```

在嚴格模式下,不能定義名為 eval 和 arguments 的變數,否則會導致語法錯誤。

1.2.變數提升

從概念的字面意義上說,“變數提升”意味著變數和函式的宣告會在物理層面移動到程式碼的最前面,但這麼說並不準確。實際上變數和函式宣告在程式碼裡的位置是不會動的,而是在編譯階段被放入記憶體中。()

JavaScript 只會提升宣告,不會提升其初始化。如果一個變數先被使用再被宣告和賦值的話,使用時的值是 undefined。參見例子:

```js console.log(num); // Returns undefine var num; num = 6; //改造 console.log(num) //如過只下下面其中一個 var num //提升 num //報錯

//函式內 function foo() { console.log(age); var age = 26; } foo(); // undefined 函式內實際上js執行是看成下面的程式碼js function foo() { var age; console.log(age); age = 26; } foo(); // undefined //這就是所謂的“提升”(hoist),也就是把所有變數宣告都拉到函式作用域的頂部。 ```

var 宣告會被拿到函式或全域性作用域的頂部,位於作用域中所有程式碼之前。這個現象叫作“提升” (hoisting)。提升讓同一作用域中的程式碼不必考慮變數是否已經宣告就可以直接使用。可是在實踐中,提 升也會導致合法卻奇怪的現象,即在變數宣告之前使用變數。在實際開發中還是要嚴謹一點少用這個屬性,避免不必要的問題。

1.3.可重複定義變數

js function foo() { var age = 16; var age = 26; var age = 36; console.log(age) } foo(); // 36 示例方便理解: ```js var x = 0; // x 是全域性變數,並且賦值為 0

console.log(typeof z); // // undefined,因為 z 還不存在

function a() { var y = 2; // y 被宣告成函式 a 作用域的變數,並且賦值為 2

console.log(x, y); // 0 2

function b() { x = 3; // 全域性變數 x 被賦值為 3 y = 4; // 已存在的外部函式的 y 變數被賦值為 4 z = 5; // 建立新的全域性變數 z,並且賦值為 5 // (在嚴格模式下丟擲 ReferenceError) }

b(); // 呼叫 b 時建立了全域性變數 z console.log(x, y, z); // 3 4 5 }

a(); // Also calls b. console.log(x, z); // 3 5 console.log(typeof y); // undefined,因為 y 是 a 函式的區域性變數

```

2.let

ES6 新增的 let 關鍵字跟 var 很相似,但它的作用域是塊級的,這也是 JavaScript 中的新概念。塊 級作用域由最近的一對包含花括號{}界定。換句話說,if 塊、while 塊、function 塊,甚至連單獨 的塊也是 let 宣告變數的作用域。最明顯的區別是,let 宣告的範圍是塊作用域,var 宣告的是函式作用域。

驗證var有沒有塊級作用域 js { var a =1; let b =2; } console.log(a); console.log(b); // 1 // Uncaught ReferenceError: b is not defined

2.1.作用域

let語句宣告一個塊級作用域的區域性變數 例項 js if (true) { let age = 26; console.log(age); // 26 } console.log(age); // 26 // Uncaught ReferenceError: age is not defined

2.2.暫時性死區

一個例項可以說明什麼是暫時性死區 js // name 會被提升 console.log(name); // undefined var name = 'Matt'; // age 不會被提升 console.log(age); // ReferenceError:age 沒有定義 let age = 26;

在解析程式碼時,JavaScript 引擎也會注意出現在塊後面的 let 宣告,只不過在此之前不能以任何方 式來引用未宣告的變數。在 let 宣告之前的執行瞬間被稱為“暫時性死區”(temporal dead zone),在此 階段引用任何後面才宣告的變數都會丟擲 ReferenceError。只可以在聲明後使用。

2.3.不可重複定義

js let b =3; let b =4; // Uncaught SyntaxError: Identifier 'b' has already been declared

2.4.全域性不掛載window

與 var 關鍵字不同,使用 let 在全域性作用域中宣告的變數不會成為 window 物件的屬性(var 宣告的變數則會)。 ```js var name = 'Matt'; console.log(window.name); // 'Matt'

let age = 26; console.log(window.age); // undefined let 的行為非常適合在迴圈中宣告迭代變數。使用 var 宣告的迭代變數會洩漏到迴圈外部,這種情況應該避免。js for (var i = 0; i < 10; ++i) {} console.log(i); // 10

for (let j = 0; j < 10; ++j) {} console.log(j); // ReferenceError: j 沒有定義 ```

嚴格來講,let 在 JavaScript 執行時中也會被提升,但由於“暫時性死區”(temporal dead zone)的緣故,實際上不能在宣告之前使用 let 變數。因此,從寫 JavaScript 程式碼的角度說,let 的提升跟 var是不一樣的。

3.const

const 的行為與 let 基本相同,唯一一個重要的區別是用它宣告變數時必須同時初始化變數,且嘗試修改 const 宣告的變數會導致執行時錯誤。(常量的值是無法通過重新賦值改變的,也不能被重新宣告。)宣告的是一個記憶體地址值為不可改變的物件。

定義普通型別資料時,無法改變其值(最好大寫定義),但是const定義引用型資料型別時,例如物件,可以修改它的值,改變屬性的值,增加屬性,方法等,但不可以修改物件的指標(變數的引用便不可修改)。

js const age = 26; age = 36; // TypeError: 給常量賦值 // const 也不允許重複宣告 const name = 'Matt'; const name = 'Nicholas'; // SyntaxError // const 宣告的作用域也是塊 const name = 'Matt'; if (true) { const name = 'Nicholas'; } console.log(name); // Matt

const 宣告的限制只適用於它指向的變數的引用。換句話說,如果 const 變數引用的是一個物件,那麼修改這個物件內部的屬性並不違反 const 的限制,改變指向就會報錯。 ``` const foo = {}; // 為 foo 新增一個屬性,可以成功 foo.prop = 123; foo.prop // 123 // 將 foo 指向另一個物件,就會報錯 foo = {}; // TypeError: "foo" is read-only //如果將另一個數組賦值給a會報錯 const a = []; a.push('Hello'); // 可執行 a.length = 0; // 可執行 a = ['Dave']; // 報錯

``` JavaScript 引擎會為 for 迴圈中的 let 宣告分別建立獨立的變數例項,雖然 const 變數跟 let 變數很相似,但是不能用 const 來宣告迭代變數(因為迭代變數會自增):

js for (const i = 0; i < 10; ++i) {} // TypeError:給常量賦值 不過,如果你只想用 const 宣告一個不會被修改的 for 迴圈變數,那也是可以的。也就是說,每次迭代只是建立一個新變數。這對 for-of 和 for-in 迴圈特別有意義: js let i = 0; for (const j = 7; i < 5; ++i) { console.log(j); } // 7, 7, 7, 7, 7 for (const key in {a: 1, b: 2}) { console.log(key); } // a, b for (const value of [1,2,3,4,5]) { console.log(value); } // 1, 2, 3, 4, 5

## 4.宣告風格及最佳實踐

ECMAScript 6 增加 let 和 const 從客觀上為這門語言更精確地宣告作用域和語義提供了更好的支援。行為怪異的 var 所造成的各種問題,已經讓 JavaScript 社群為之苦惱了很多年。隨著這兩個新關鍵字的出現,新的有助於提升程式碼質量的最佳實踐也逐漸顯現。

4.1. 不使用 var

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

4.2. const 優先,let 次之

使用 const 宣告可以讓瀏覽器執行時強制保持變數不變,也可以讓靜態程式碼分析工具提前發現不合法的賦值操作。因此,很多開發者認為應該優先使用 const 來宣告變數,再使用 let。這樣可以讓開發者更有信心地推斷某些變數的值永遠不會變,同時也能迅速發現因意外賦值導致的非預期行為。 開發實踐表明,如果開發流程並不會因此而受很大影響,就應該儘可能地多使用 const 宣告,除非確實需要一個將來會重新賦值的變數。這樣可以從根本上保證提前發現 重新賦值導致的 bug

5.let、const、var的區別

(1)塊級作用域:塊作用域由 { }包括,let和const具有塊級作用域,var不存在塊級作用域。塊級作用域解決了ES5中的兩個問題:

  • 內層變數可能覆蓋外層變數
  • 用來計數的迴圈變數洩露為全域性變數

(2)變數提升:var存在變數提升,let和const不存在變數提升,即在變數只能在宣告之後使用,否在會報錯。

(3)給全域性新增屬性:瀏覽器的全域性物件是window,Node的全域性物件是global。var宣告的變數為全域性變數,並且會將該變數新增為全域性物件的屬性,但是let和const不會。

(4)重複宣告:var宣告變數時,可以重複宣告變數,後宣告的同名變數會覆蓋之前宣告的遍歷。const和let不允許重複宣告變數。

(5)暫時性死區:在使用let、const命令宣告變數之前,該變數都是不可用的。這在語法上,稱為暫時性死區。使用var宣告的變數不存在暫時性死區。

(6)初始值設定:在變數宣告時,var 和 let 可以不用設定初始值。而const宣告變數必須設定初始值。

(7)指標指向:let和const都是ES6新增的用於建立變數的語法。 let建立的變數是可以更改指標指向(可以重新賦值)。但const宣告的變數是不允許改變指標的指向。

在函式內部如果用var宣告變數和不用時有很大差別,用var宣告的是區域性變數,在函式外部訪問這個變數是訪問不到的,沒var宣告的是全域性變數。在函式外部是可以訪問到的。 var 命令和 function 命令宣告的全域性變數,依舊是頂層物件的屬性,但 let命令、const命令、class命令宣告的全域性變數,不屬於頂層物件的屬性var const 宣告的全域性作用域下的變數不會掛載在windows下所以不會被this引用。在全域性作用域中,用 let 和 const 宣告的全域性變數並沒有在全域性物件中,只是一個塊級作用域(Script)中。

函式擁有塊級作用域,但是外面依然是可以訪問的:這是因為引擎會對函式的宣告進行特殊的處理,允許像var那樣進行提升;

js // foo() 會報錯 不能在宣告之前呼叫 不能跟var變數一樣 console.log(a); // undefined { var a = 1; let b = 2; function foo() { console.log("foo function") } } console.log(b) // 報錯 塊級作用域中的變數不能在外部訪問 foo() // foo function console.log(a); // 1

6.今日精進

59 - I. 滑動視窗的最大值(維護一個單調佇列,隊頭元素依次遞減)。

Day6【2022年7月28日】

學習重點: 總結一下js字串常用的一些原生方法(API)(上)

相信應該會有小夥伴遇到這種情況,用過的一些js原生方法忘記具體用法或是每個引數的含義。總結一下常用的一些方法,以後不懂可以查本篇,不常用的不展開敘述。

1.字串方法總結(上)

1.1 at()

返回指定字串索引值(位置對應的值),當傳遞負數時,支援從字串末端開始的相對索引;也就是說,如果使用負數,返回的字元將從字串的末端開始倒數。

用法: js at(index) //index要返回的字串字元的索引(位置) 返回值:

由位於指定位置的單個 UTF-16 碼元組成的 String。如果找不到指定的索引,則返回 undefined

例項 ```js let a = "a bcd" let c = a.at(1); let d = a.at(-1); let e = a.at(4); let f = a.at(5);

console.log(c);//
console.log(d);// d console.log(e);// d console.log(f);// undefinde console.log(a.at())//a ``` 不傳索引預設索引為0. ' '也是有意義的字串,也佔位。

1.2 charAt()

charAt() 方法從一個字串中返回指定的字元。

用法: js str.charAt(index) //index一個介於 0 和字串長度減 1 之間的整數。(0~length-1) 如果沒有提供索引,charAt() 將使用 0。 返回值:

存在就返回存在的對應字串,不存在則返回空字串。

例項: ```js let a = "a bcd"; let b = a.charAt(1); let c = a.charAt(-1); let d = a.charAt(4);

console.log(b); console.log(c); console.log(d); //
// // d console.log(a.charAt())//a ``` 不傳索引預設索引為0. 由此可以看出charAt如果是負索引返回的是空字串,如果索引不存在返回的也是空字串,這跟at還是有一定區別的。

1.3 charCodeAt()

charCodeAt() 方法返回 0 到 65535 之間的整數,表示給定索引處的 UTF-16 程式碼單元。

用法: js str.charCodeAt(index) //一個大於等於 `0`,小於字串長度的整數。如果不是一個數值,則預設為 `0`。 返回值:

指定 index 處字元的 UTF-16 程式碼單元值的一個數字;如果 index 超出範圍,charCodeAt() 返回 NaN

例項: ```js let a = "a bcd"; let b = a.charCodeAt(1); let c = a.charCodeAt(-1); let d = a.charCodeAt(4); let e = a.charCodeAt('h'); let f = a.charCodeAt(0);

console.log(b);// 32 console.log(c);// NaN console.log(d);// 100 console.log(e);// 97 console.log(f);// 97 console.log(a.charCodeAt())//97 ``` 不傳索引預設索引為0.

1.4 codePointAt()

返回 一個 Unicode 編碼點值的非負整數。

用法: js str.codePointAt(pos) //pos 這個字串中需要轉碼的元素的位置。 返回值:

返回值是在字串中的給定索引的編碼單元體現的數字,如果在索引處沒找到元素則返回 undefined 。

例項: ```js let a = "a bcd"; let b = a.codePointAt(1); let c = a.codePointAt(-1); let d = a.codePointAt(4); let e = a.codePointAt('h'); let f = a.codePointAt(0); let g = a.codePointAt();

console.log(b); console.log(c); console.log(d); console.log(e); console.log(f); console.log(g); // 32 // undefined // 100 // 97 // 97 // 97 ``` 總結一下(畫表格)

1.5 concat()

將一個或多個字串與原字串連接合並,形成一個新的字串並返回。

用法: js str.concat(str2, [, ...strN]) // str 需要連線的字串 返回值:

一個新的字串,包含引數所提供的連線字串

例項: ```js let a = "a bcd"; let b = a.concat('vv'); let c = a.concat('vv', 'vv'); let d = a.concat(['vv', 'vv']); let e = a.concat(['vv', 'vv'], 'vv'); let f = a.concat(1); let g = a.concat(1, 'vv'); let h = a.concat();

console.log(b); console.log(c); console.log(d); console.log(e); console.log(f); console.log(g); console.log(h); // a bcdvv // a bcdvvvv // a bcdvv,vv // a bcdvv,vvvv // a bcd1 // a bcd1vv // a bcd ```

concat 方法將一個或多個字串與原字串連接合並,形成一個新的字串並返回。 concat 方法並不影響原字串。如果引數不是字串型別,它們在連線之前將會被轉換成字串(例如toString等)。 強烈建議使用賦值操作符(+, +=)代替 concat 方法(效能不好)。

1.6 endsWith()

endsWith()方法用來判斷當前字串是否是以另外一個給定的子字串“結尾”的,根據判斷結果返回 true 或 false。

用法: js str.endsWith(searchString[, length]) //searchString要搜尋的子字串。 length 可選作為 str 的長度。預設值為 str.length。 返回值:

如果傳入的子字串在搜尋字串的末尾則返回true;否則將返回 false。

例項: ```js let a = "a bcd"; let b = a.endsWith('d'); let c = a.endsWith('d', 1); let d = a.endsWith('d', 5); let j = a.endsWith(); let k = a.endsWith('bcd');

console.log(b); console.log(c); console.log(d); console.log(j); console.log(k); console.log(a.length); // true // false // true // false // true // 5 ```

1.7 includes()

用於判斷一個字串是否包含在另一個字串中,根據情況返回 true 或 false。該方法區分大小寫。

用法: ```js str.includes(searchString[, position]) // searchString要在此字串中搜索的字串。

//position 可選從當前字串的哪個索引位置開始搜尋子字串,預設值為 0

``` 返回值:

如果當前字串包含被搜尋的字串,就返回 true;否則返回 false。

例項: ```js 'Blue Whale'.includes('blue'); //false let a = "a bcd"; let b = a.includes('b'); let c = a.includes(); let d = a.includes('a', 1);

console.log(b); console.log(c); console.log(d); // true // false // false ```

1.8 indexOf()

indexOf() 方法返回呼叫它的 String 物件中第一次出現的指定值的索引,從開始制度查詢處進行搜尋,不填預設為0(第二個引數)。如果未找到該值,則返回 -1。

用法: js str.indexOf(searchValue [, fromIndex]) searchValue

要被查詢的字串值。如果沒有提供確切地提供字串,searchValue 會被強制設定為 "undefined", 然後在當前字串中查詢這個值。

舉個例子:'undefined'.indexOf() 將會返回 0,因為 undefined 在位置 0 處被找到,但是 'undefine'.indexOf() 將會返回 -1 ,因為字串 'undefined' 未被找到。

fromIndex 可選

數字表示開始查詢的位置。可以是任意整數,預設值為 0。如果 fromIndex 的值小於 0,或者大於 str.length ,那麼查詢分別從 0 和str.length 開始。fromIndex 的值小於 0,等同於為空情況; fromIndex 的值大於或等於 str.length ,那麼結果會直接返回 -1 。

舉個例子,'hello world'.indexOf('o', -5) 返回 4 ,因為它是從位置0處開始查詢,然後 o 在位置4處被找到。另一方面,'hello world'.indexOf('o', 11) (或 fromIndex 填入任何大於11的值)將會返回 -1 ,因為開始查詢的位置11處,已經是這個字串的結尾了。

轉化一下 a = 'abcd',a.length =4,索引為0-3,如果 fromIndex<0 那麼預設會從0開始查詢(相當於為空),如果fromIndex>=4 那麼會從4開始查詢則會返回-1。

返回值:

查詢的字串 searchValue 的第一次出現的索引,如果沒有找到,則返回 -1。

例項: ```js let a = "a bcd" let b = a.indexOf('b'); let c = a.indexOf('b', 1); let d = a.indexOf('b', 5); let e = a.indexOf('b', -1); let f = a.indexOf(); let g ='undefinde'.indexOf(); let h = a.indexOf('d', 8); let i = a.indexOf('a', 0); let j = a.indexOf('d', 4);

console.log(b); console.log(c); console.log(d); console.log(e); console.log(f); console.log(g); console.log(h); console.log(i); console.log(j); // 2 // 2 //-1 // 2 // -1 // -1 // -1 // 0 // 4 ```

1.9 localeCompare()

返回一個數字來指示一個參考字串是否在排序順序前面或之後或與給定字串相同(大部分是結合排序使用)。

新的 locales 和 options 引數能讓應用程式定製函式的行為,即指定用來排序的語言。locales 和 options 引數完全取決於實現,在舊的實現中忽略這兩個引數。 用法: ```js

referenceStr.localeCompare(compareString[, locales[, options]])

``` compareString用來比較的字串 locales(語言環境):用來指定比較字母的語言環境(預設是英文) options(選項):指定一些排序的規則如靈敏度等 返回值: 如果引用字元存在於比較字元之前則為負數; 如果引用字元存在於比較字元之後則為正數; 相等的時候返回 0 . - 當 引用字串 在 比較字串 前面時返回 -1 (sort相同) - 當 引用字串 在 比較字串 後面時返回 1(sort相同) - 相同位置時返回 0

切勿依賴於 -1 或 1 這樣特定的返回值。 不同瀏覽器之間(以及不同瀏覽器版本之間)返回的正負數的值各有不同,因為 W3C 規範中只要求返回值是正值和負值,而沒有規定具體的值。一些瀏覽器可能返回 -2 或 2 或其他一些負的、正的值。 相當於給定26個字母的順序排序。 例項: js let a = "a bcd"; let b = " bcd"; let c = "bcd"; console.log(a.localeCompare("a"));// 1 console.log(a.localeCompare("b"));// -1 console.log(a.localeCompare("a bcd"));// 0 console.log(b.localeCompare("a"));// -1 console.log(b.localeCompare("b"));// -1 console.log(b.localeCompare(" bcd"));// 0 console.log(c.localeCompare("a"));// 1 console.log(c.localeCompare("b"));// 1 console.log(c.localeCompare("bcd"));// 0 比較規則總結 js console.log("b".localeCompare("a")); console.log("b".localeCompare("b")); console.log("b".localeCompare("c")); console.log("b".localeCompare("a bcd")); console.log("b".localeCompare(" bcd")); console.log("b".localeCompare("bcd")); console.log("b".localeCompare("accd")); 如上所示,b對比多個字串時候會先比較第一個若第一個相同再比較下一個,依次比較到處結果為止,''比較特殊預設是在a前面。 js console.log("a".localeCompare(" "));

1.10 match()

方法檢索返回一個字串匹配正則表示式的結果及分組捕獲組的迭代器。

用法: js str.match(regexp)

一個正則表示式物件。如果傳入一個非正則表示式物件,則會隱式地使用 new RegExp(obj) 將其轉換為一個 RegExp 。如果你沒有給出任何引數並直接使用 match() 方法 ,你將會得到一個包含空字串的 Array :[""] 。

返回值:

如果使用 g 標誌(全域性匹配模式),則將返回與完整正則表示式匹配的所有結果,但不會返回捕獲組。 如果未使用 g 標誌(全域性匹配模式),則僅返回第一個完整匹配及其相關的捕獲組(Array)。 在這種情況下,返回的專案將具有如下所述的其他屬性。

附加屬性 如上所述,匹配的結果包含如下所述的附加特性。 groups: 一個命名捕獲組物件,其鍵是捕獲組名稱,值是捕獲組,如果未定義命名捕獲組,則為 undefined。有關詳細資訊,請參閱組和範圍。 index: 匹配的結果的開始位置 input: 搜尋的字串。 一個Array,其內容取決於 global(g)標誌的存在與否,如果未找到匹配則為null。

例項: js let a = "aBcd"; //匹配大寫字母不帶g 會返回捕獲器 console.log(a.match(/[A-Z]/));// ['B', index: 1, input: 'aBcd', groups: undefined] console.log(...a.match(/[A-Z]/));// B //全域性匹配模式 匹配所有的大寫字母 僅返回第一個完整匹配及其相關的捕獲組 console.log(a.match(/[A-Z]/g));// ['B'] console.log(a.match(/[a-z]/g) instanceof Array);// true //如果匹配不存在則返回null let a = "aBcd"; console.log(a.match(/h/g));// null console.log(a.match(/h/));// null 擴充套件符(…)可以解析出來 不是特別熟悉的小夥伴可以看下這篇文章補一下正則表示式->點選這裡

1.11 matchAll()

返回一個包含所有匹配正則表示式的結果及分組捕獲組的迭代器。

用法: js str.matchAll(regexp)

regexp正則表示式物件。如果所傳引數不是一個正則表示式物件,則會隱式地使用 new RegExp(obj) 將其轉換為一個 RegExp 。RegExp必須是設定了全域性模式g的形式,否則會丟擲異常TypeError。(跟match的區別)

返回值: 一個迭代器(不可重用,結果耗盡需要再次呼叫方法,獲取一個新的迭代器)。

例項: js let str = 'test1test2'; console.log([...str.matchAll(/t/g)][0]);// ['t', index: 0, input: 'test1test2', groups: undefined] console.log([...str.matchAll(/t/g)][1]);// ['t', index: 3, input: 'test1test2', groups: undefined] console.log( [...str.matchAll(/t/g)]);// (4) [Array(1), Array(1), Array(1), Array(1)] console.log( ...str.matchAll(/t/g));//返回匹配的四個迭代器陣列 //報錯 console.log( [...str.matchAll(/t/)]); match可以帶g也可以不帶g,如果帶g將返回與完整正則表示式匹配的所有結果,不會返回捕獲組,不帶g僅返回第一個完整匹配及其相關的捕獲組(Array)。matchAll必須是全域性匹配。

1.12 normalize()

會按照指定的一種 Unicode 正規形式將當前字串正規化。(如果該值不是字串,則首先將其轉換為一個字串)

用法: js str.normalize([form]) form可選四種 Unicode 正規形式(Unicode Normalization Form)"NFC""NFD""NFKC",或 "NFKD" 其中的一個,預設值為 "NFC"。具體含義感興趣的小夥伴可以去查。 返回值: 含有給定字串的 Unicode 規範化形式的字串。

1.13 padEnd()

方法會用一個字串填充當前字串(如果需要的話則重複填充),返回填充後達到指定長度的字串。從當前字串的末尾(右側)開始填充。

用法: js str.padEnd(targetLength [, padString])

targetLength當前字串需要填充到的目標長度。如果這個數值小於當前字串的長度,則返回當前字串本身。

padString 可選 填充字串。如果字串太長,使填充後的字串長度超過了目標長度,則只保留最左側的部分,其他部分會被截斷。此引數的預設值為 " "(U+0020)。

返回值:

在原字串末尾填充指定的填充字串直到目標長度所形成的新字串。

例項: ```js let a = "aBcd";

console.log(a.padEnd(10));//aBcd 後面填充空格 console.log(a.padEnd(10).length);//10
console.log(a.padEnd(10, "foo"));//aBcdfoofoo console.log(a.padEnd(6, "123456"));// aBcd12 console.log(a.padEnd(1));// aBcd ```

1.14 padStart()

法用另一個字串填充當前字串 (如果需要的話,會重複多次),以便產生的字串達到給定的長度。從當前字串的左側開始填充。

用法: js str.padStart(targetLength [, padString])

targetLength 當前字串需要填充到的目標長度。如果這個數值小於當前字串的長度,則返回當前字串本身。 padString 可選 填充字串。如果字串太長,使填充後的字串長度超過了目標長度,則只保留最左側的部分,其他部分會被截斷。此引數的預設值為 " "(U+0020)。

返回值:

在原字串開頭填充指定的填充字串直到目標長度所形成的新字串。

例項: ```js let a = "aBcd";

console.log(a.padStart(10));// aBcd console.log(a.padStart(10).length);//10
console.log(a.padStart(10, "foo"));//foofooaBcd console.log(a.padStart(6, "123456"));// 12aBcd console.log(a.padStart(1));// aBcd ``` 實際上跟padEnd是一樣的只不過一個是頭另一個是尾部。

1.15 raw()(Spring.raw())

用來獲取一個模板字串的原始字串的,比如說,佔位符(例如 ${foo})會被處理為它所代表的其他字串,而轉義字元(例如 \n)不會。在大多數情況下,String.raw() 是用來處理模版字串的。還模板字串為原生的 String 物件

其他的方法都是掛載在# prototype(原型物件)下他是直接掛載String下的API所以使用方法會用不同,String原型物件下沒有宣告的字串不能直接呼叫。 如圖所示:

image.png

用法: ```js String.raw(callSite, ...substitutions)

String.rawtemplateString ```

callSite 一個模板字串的“呼叫點物件”。類似 { raw: ['foo', 'bar', 'baz'] }。 ...substitutions 任意個可選的引數,表示任意個內插表示式對應的值。 templateString 模板字串,可包含佔位符(${...})。

返回值:

給定模板字串的原始字串。

例項: ``js // raw方法 String.rawHi\n${2+3}!`; // 'Hi\n5!',Hi 後面的字元不是換行符,\ 和 n 是兩個不同的字元

String.raw Hi\u000A!; // "Hi\u000A!",同上,這裡得到的會是 \、u、0、0、0、A 6 個字元, // 任何型別的轉義形式都會失效,保留原樣輸出,不信你試試.length

let name = "Bob"; String.raw Hi\n${name}!; // "Hi\nBob!",內插表示式還可以正常執行

String.rawHi${2+3}!;\'Hi5!'

// 正常情況下,你也許不需要將 String.raw() 當作函式呼叫。 // 但是為了模擬 t${0}e${1}s${2}t 你可以這樣做: String.raw({ raw: 'test' }, 0, 1, 2); // 't0e1s2t' // 等同於 String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);

// 注意這個測試,傳入一個 string,和一個類似陣列的物件 // 下面這個函式和 foo${2 + 3}bar${'Java' + 'Script'}baz 是相等的。 String.raw({ raw: ['foo', 'bar', 'baz'] }, 2 + 3, 'Java' + 'Script'); // 'foo5barJavaScriptbaz'

``` 可以看出 \n 轉換會多出一個\這個要注意

1.16 repeat()

構造並返回一個新字串,該字串包含被連線在一起的指定數量的字串的副本(重複拼接當前字串)。

用法: `js str.repeat(count) count 介於 0 和 +Infinity(正無窮大) 之間的整數。表示在新構造的字串中重複了多少遍原字串。

返回值: 包含指定字串的指定數量副本的新字串。 例項: js let a="aBcd" console.log(a.repeat(2));//aBcdaBcd

2.今日精進

選擇大於努力,思路決定出路,選擇比努力更重要。

Day7【2022年7月29日】

學習重點: 總結一下js字串常用的一些原生方法(API)(下)

1.字串方法總結(下)

1.1 replace()

指定字串中目標替換成給定的值,如果第一個值傳入的是字串則只會替換第一個,正則表示式則按照標準替換。

用法: js str.replace(regexp|substr, newSubStr|function) regexp 正則表示式(會全部替換) substr 字串,只會替換第一個目標元素。 newSubStr 用來替換的字串元素。 function 用來替換的函式 function 怎麼傳值可以參考這個MDN

返回值:

被替代的新字串。

例項: ```js let a = "abcdaabd"; console.log(a.replace(/a/g, 'ff' ));//ffbcdffffbd console.log(a.replace('a', 'ff' ));// ffbcdaabd console.log(a.replace(/a/, 'ff' ));// ffbcdaabd

函式替換例項: var str="他今年22歲,她今年20歲,他的爸爸今年45歲,她的爸爸今年44歲,一共有4人" function test($1){ var gyear=(new Date()).getYear()-parseInt($1)+1; return $1+"("+gyear+"年出生)"; } var reg=new RegExp("(\d+)歲","g"); var newstr=str.replace(reg,test);

console.log(newstr)//他今年22歲(101年出生),她今年20歲(103年出生),他的爸爸今年45歲(78年出生),她的爸爸今年44歲(79年出生),一共有4人 console.log(str)//他今年22歲,她今年20歲,他的爸爸今年45歲,她的爸爸今年44歲,一共有4人 ```

1.2 replaceAll()

將指定字串中目標全部替換成給定的值,如果正則表示式不是全域性則會報錯。

用法: js const newStr = str.replaceAll(regexp|substr, newSubstr|function) 引數含義跟replace一樣。

返回值:

新的字串

例項: js let a = "abcdaabd"; console.log(a.replaceAll(/a/g, 'ff' ));// ffbcdffffbd console.log(a.replaceAll('a', 'ff' ));// ffbcdffffbd console.log(a.replaceAll(/a/, 'ff' ));//報錯

1.3 search()

用正側表示式去匹配字串對應的值,並返回索引。

用法:

js str.search(regexp) regexp表示一個正則表示式物件。如果傳入一個非正則表示式物件,則會使用 new RegExp(regexp) 隱式地將其轉換為正則表示式物件。

返回值:

返回首次匹配項的索引,匹配不成功返回-1。

例項: js let a = "a Bcd"; console.log(a.search(/a/));// 0 console.log(a.search(/e/));//-1 相對與match()方法該方法會快一些。 search()對應正則表示式中的test()方法,match()對應正則表示式中的exec()方法。 search()返回第一個索引,match()返回捕獲的迭代器。

1.4 slice()

擷取字串的一部分,並返回一個新的字串,不會對原字串改動。

用法:

js str.slice(beginIndex[, endIndex]) beginIndex 開始匹配的索引,如果是負數則會根據公式(str.length+beginIndex)轉換。

endIndex 可選 如果不選改引數,預設一直提取到字串末尾,選擇則提取到當前。若為負責則會跟begin一樣加上字串長度。

返回值:

返回一個被截取出來的的新字串。

例項: js let a = "a Bcd"; console.log(a.length);//5 //正數 console.log(a.slice(0,1)); //'a' console.log(a.slice(0,2));//'a ' console.log(a.slice(0));'6 a Bcd' //帶負 console.log(a.slice(0,-1));'a Bc' console.log(a.slice(-1,3));'' console.log(a.slice(-1));'d' console.log(a.slice(-1,-2)); //試錯 console.log(a.slice());'' console.log(a.slice(0,1,2));'a Bcd' console.log(a.slice(6));// console.log(a.slice(2,3)); //B 由此可以總結處,如果輸入的索引超出字串的長度會返回一個空字串。 而且查詢過程中包含開始的索引不包含結尾的索引。

1.5 split()

用指定的分隔字串將目標字串拆分成子陣列。

用法: js str.split([separator[, limit]]) separato指定表示每個拆分應發生的點的字串,limit一個整數,限定返回的分割片段數量。

返回值:

陣列

例項: js let myString = "Hello World. How are you doing?"; console.log( myString.split(" ", 3));// ['Hello', 'World.', 'How'] console.log( myString.split(" ", 3).join(" "));// Hello World. How console.log( myString.split(" "));// ['Hello', 'World.', 'How', 'are', 'you', 'doing?'] console.log( myString.split(" ").join(" "));// Hello World. How are you doing? 一般配合join(根據傳入字串為間隔拼接每個陣列元素為字串)方法一起用。

1.6 startsWith()

用來判斷某個字串是否以特定的字串開頭,返回ture/false,區分大小寫。

用法: js str.startsWith(searchString[, position]) searchString要搜尋的子字串。 position 可選要搜尋的初始位置,預設為0。 返回值: ture/false

例項: js let a = "a Bcd"; console.log(a.startsWith("a "));//true console.log(a.startsWith("a B"));//true console.log(a.startsWith("a bc"));//false

1.7 toLocaleLowerCase()

將指定字串轉換為小寫格式

用法: js str.toLocaleLowerCase() str.toLocaleLowerCase(locale) str.toLocaleLowerCase([locale, locale, ...]) locale 為指定要轉換成小寫格式的特定語言區域。預設值是當前計算機的語言格式。

返回值:

轉換成小寫格式的新字串。

例項: js let a = "a Bcd"; console.log(a.toLocaleLowerCase());//a bcd

1.8 toLocaleUpperCase()

將指定字串轉換為大寫寫格式,具體用法跟上面差不多,這裡只舉例說明。 js let a = "a Bcd"; console.log(a.toLocaleLowerCase());//a bcd console.log(a.toLocaleUpperCase());//A BCD

1.9 toLowerCase()

將一個字串轉換為小寫的形式並返回。

用法: js str.toLowerCase()

返回值:

一個小寫新的字串。

例項: js let a = "a Bcd"; console.log(a.toLocaleLowerCase());//a bcd console.log(a.toLocaleUpperCase());//A BCD console.log(a.toLowerCase());//a bcd console.log(a);//a bcd 這裡可以看出,toLocaleLowerCase和toLowerCase都不會影響字串本身,都是返回一個新的字串。 可以理解為toLocaleLowerCase是toLowerCase的一個加強版本,前者可以選擇以什麼樣的特定語言來返回字串的大小寫,而後者不行,後者只能預設為計算機的語言。

1.10 toString()

返回一個字串/某些值轉化為字串 用法: js toString()

返回值:

String 包裝物件的字串值。

例項: let a = "abc"; let b = new String("abcd"); let c = 2 console.log(a.toString());// abc console.log(b.toString());// abcd console.log(true.toString());// true console.log(c.toString());// 2

1.11 toUpperCase()

將字串轉換為大寫(如果呼叫該方法的值不是字串型別會被強制轉換)。

用法: js str.toUpperCase() 返回值:

返回一個轉化為大寫的字串,不改變原字串。

例項: js let a = "abc"; console.log(a.toUpperCase());// ABC 非字串轉化為字串 const b = String.prototype.toUpperCase.call(true); console.log( b);.// TRUE

與toLocaleUpperCase的關係 同toLocaleLowerCase和toLowerCase類似

1.12 trim()

移除字串左右兩邊的空白字串。

用法: js str.trim() 返回值:

一個去掉兩端空白字串的新字串

例項: js let a = " a Bcd "; let b = a.trim(); console.log(b);//a Bcd console.log(a.length);//13 console.log(b.length);//5

在這個上下文中的空白字元是所有的空白字元 (space, tab, no-break space 等) 以及所有行終止符字元(如 LF,CR 等)。

1.13 trimEnd()

也稱之為trimRight()移除字串的末尾空白字串,不會直接修改原字串,改名稱標準叫法為trimEnd(),只是為了相容性,還保留了trimRight()名稱。

用法: js str.trimEnd(); str.trimRight(); 返回值:

符合要求的新字串。

例項: js let a = "a Bcd "; let b = a.trimEnd(); console.log(b);//a Bcd console.log(a.length);//9 console.log(b.length);//5

1.14 trimStart()

也稱之為trimLeft(),trimStart()是標準叫法,移除字串左邊的空字串。為了相容性保留了trimLeft()。

用法:

js str.trimStart(); str.trimLeft(); 返回值:

新字串

例項: js let a = " a Bcd "; let b = a.trimStart(); console.log(b);//a Bcd console.log(a.length);//13 console.log(b.length);//9 trim,trimStart,trimEnd三個方法都是用來去除字串中的空字串,如果要去除陣列中所有的字串可以使用replaceAll 替換或者用for變數字串替換。

image.png 由此圖可以看出trimStart,trimEnd是標準的官網推薦的方法,在不久相信trimleft和trimleft會被廢棄。

valueOf()

返回物件的初始值,通常在js內部呼叫而不是顯示帶呼叫。

2.今日精進

  1. 字母異位詞分組​(雜湊表解題)

參考資料

結語

志同道合的小夥伴可以加我,一起交流進步,我們堅持每日精進(互相監督思考學習,如果堅持不下來我可以監督你)。另外共享學習資料的小夥伴也可以聯絡我,進群技術交流資料free共享,我們交換資料一起努力鴨! ——>點選這裡

備註

按照時間順序倒敘排列,完結後按時間順序正序排列方便檢視知識點。