- 前端每日知識點分享(總結版)
- 2020年4月20日(typeof能判斷出哪些數據類型?)
- 2020年4月21日 (CSS 中盒模型有幾種,有什么不同?)
- 2020年4月22日 (new 操作符)
- 2020年4月23日(值類型和引用類型的區別)
- 2020 年4月24日( < !DOCTYPE >的作用)
- 2020年4月25日(語義化標簽的理解)
- 2020年4月26日(new 運算符的底層代碼實現)
- 2020年4月27日(CSS 對於選擇器的解析順序)
- 2020 年4月28日(CSS 中清除浮動常用的兩種方法)
- 2020年4月29日(JS 任務隊列)
- 2020年4月30日(GET 和 POST 的區別)
- 2020 年5月1日(HTTP 中常用的一些狀態碼)
- 2020年5月2日(call,apply,bind 之間的區別)
- 2020年5月3日(比較兩個對象的內容是否相等)
- 2020年5月4日(字節筆試分享)
- 2020年5月5日(JS 中的鍵盤事件)
- 2020年5月6日(簡單理解進程和線程)
- 2020年5月7日(學而思網校面試題分享一)
- 2020年5月8日(學而思網校面試題分享二)
- 2020年5月9日(箭頭函數和普通函數的區別)
- 2020年5月10日(手寫 promise.all 方法)
- 2020年5月10日(手寫 Promise.race 方法)
- 2020年5月12日(CSS 中 src 和 href 的區別)
- 2020年5月13日(閉包中的引用基本數據類型的變量存放在棧中還是堆中)
- 2020年5月14日(TCP 和 UDP 的區別)
- 2020年5月15日(變量提升)
- 2020年5月16日(包裝對象)
- 2010年5月17日(0.1 + 0.2 真的等於 0.3 嗎 )
- 2020年5月18日(CSS 中 opacity,transparent,rgba 設置透明時的區別)
- 2020年5月19日(CSS 中常見的尺寸單位)
- 2020年5月20日(inline 元素設置 padding 之后會生效嗎)
- 2020年5月21日(OSI 七層模型)
- 2020年5月22日(解析 url 中的參數)
- 2020年5月23日(原碼,反碼,補碼)
- 2020年5月24日(兩個不規則的容器,一個 9 L,一個 4 L,怎么得到 6 L 水)
- 2020年5月25日(數組亂序)
- 2020年5月26日(關於 Node 中的 res.writeHead, res.setHeader, res.write 和 res.end)
- 2020年5月27日(利用 CSS 畫三角形)
- 2020年5月28日(如何實現當設置對象的一個屬性時另一個屬性自動改變)
- 2020年5月29日(如何判斷一個屬性是在當前實例中還是原型中)
- 2020年5月30日(關於 Object.keys() 和 Object.getOwnPropertyNames())
- 2020年5月31日(獲取元素的所有子節點)
- 2020年6月1日(瀏覽器中對於空格的處理)
- 2020年6月2日(利用 CSS 畫 0.5px 寬度的線)
- 2020年6月3日(ES6 中 Symbol 的幾個注意點)
- 2020年6月4日(手寫 reduce 方法)
- 2020年6月5日(邏輯題:找出開關對應的燈)
- 2020年6月6日(邏輯題:問路)
- 2020年6月7日(邏輯題:葯丸稱重)
- 2020年6月8日(關於運算符的執行順序)
- 2020年6月9日(let 不能聲明已經聲明過的變量)
- 2020年6月10日(防抖)
- 2020年6月11日(節流)
- 2020年6月12日(正則中常用的重復字符)
- 2020年6月13日(正則中常見的元字符)
- 2020年6月14日(正則中與位置相關的錨字符)
- 2020年6月15日(字符串中可以使用正則的四種方法介紹)
- 2020年6月16日(正則表達式方法)
- 2020年6月17日(數組扁平化方法一)
- 2020年6月18日(數組扁平化方法二)
- 2020年6月19日(數組扁平化方法三)
- 2020年6月20日(數組去重方法一)
- 2020年6月21日(數組去重方法二)
- 2020年6月22日(數組去重方法三)
- 2020年6月23日(數組去重方法四)
- 2020年6月24日(冒泡排序)
- 2020年6月25日(選擇排序)
- 2020年6月26日(插入排序)
- 2020年6月28日(歸並排序)
- 2020年6月28日(快速排序)
- 2020年6月29日(堆排序)
前端每日知識點分享(總結版)
2020年4月20日(typeof能判斷出哪些數據類型?)
typeof能判斷出哪些數據類型?
typeof能直接判斷出 Number,String,Boolean,undefined,Symbol
typeof(null)返回 object,判斷數組和對象返回 object,判斷函數返回 function
2020年4月21日 (CSS 中盒模型有幾種,有什么不同?)
CSS 中盒模型有幾種,有什么不同?
CSS 盒模型分為 W3C 標准盒模型和 IE 模型,W3C 標准盒模型中有元素的寬度 width 還有 border,padding, margin,其中 content 的高度和寬度不包含 padding 和 border。
IE 盒模型中也有這幾種屬性,但是 content 包含 padding 和 margin。
2020年4月22日 (new 操作符)
使用 new 創建對象時,new 運算符做了哪些事?
- 創建一個新對象
- 將構造函數的作用域賦給新對象(this 就指向了這個新對象)
- 執行構造函數中的代碼(初始化對象)
- 返回新對象
2020年4月23日(值類型和引用類型的區別)
值類型和引用類型的區別?
值類型是不可變的數據類型,例如 let a = 10;
在內存中的執行過程是先創建一塊內存空間存儲 10,然后再令 a 指向這塊內存空間。如果這時候令 a=20;
會在內存中再開辟一段內存空間里面存放 20,然后將 a 指向這段內存空間,之前的那個內存空間會被銷毀。所以值類型是不可變的,要想改變必須將之前的銷毀然后創建新的內存空間。
引用類型是可變的數據類型,例如 obj = { x: 1}
在內存中 obj 中存放的是 { x: 1 }
這個對象的地址,這個對象是在堆中存儲的,通過 obj.x=3
改變對象的屬性,這個時候並不會將之前的對象銷毀,因此引用類型是可變的數據類型。
2020 年4月24日( < !DOCTYPE >的作用)
注意 <!DOCTYPE>
並不是 HTML 標簽。
總結來說有這兩個作用:
1)聲明文檔的類型,
2)告訴瀏覽器應該以什么樣的標准解析這個文檔
瀏覽器有兩個模式,怪異模式和標准模式,聲明 <!DOCTYPE html>
作用就是告訴瀏覽器你即將要處理的是 HTML 文檔,並且在渲染文檔時要按照標准模式的方式。
MDN 解釋
在HTML中,文檔類型聲明是必要的。所有的文檔的頭部,你都將會看到"<!DOCTYPE html>
" 的身影。這個聲明的目的是防止瀏覽器在渲染文檔時,切換到我們稱為“怪異模式(兼容模式)”的渲染模式。“<!DOCTYPE html>
" 確保瀏覽器按照最佳的相關規范進行渲染,而不是使用一個不符合規范的渲染模式。
2020年4月25日(語義化標簽的理解)
語義化標簽的理解
按照字面意思理解就是有語義的標簽,要做到用最合適的標簽做最合適的事,例如要做一個導航欄,要用 Nav 而不是 div。
使用語義化標簽有很多好處:
開發者看到文檔后能夠做到見名知義,有利於團隊維護和開發。
能讓頁面呈現清晰的結構,機器更容易理解,有利於爬蟲和搜索引擎的抓取(SEO的優化)
CSS 選擇器優先級
!importtant 優先級最高 > 內聯樣式 > ID 選擇器 > 類,屬性,偽類 > 元素,偽元素
注意點:不會進位,一萬個類選擇器也抵不上一個id選擇器,相同權重,后寫的生效。
2020年4月26日(new 運算符的底層代碼實現)
new 運算符的底層代碼實現
// func 為傳入的構造函數
const new2 = fucntion (func) {
const o = Object.create(func.prototype);
const k = func.call(o);
return k === 'object' ? k : o
}
上面這種寫法沒有考慮帶參數的情況。
2020年4月27日(CSS 對於選擇器的解析順序)
今天的每日一題和大家分享一下 CSS 對於選擇器的解析順序。
以一段代碼為例:
body div .hello {
color: #ccc;
}
按照我們正常的思維是瀏覽器會選取尋找 body,然后再去找 body 下的 div,然后再去找 div 下的 .hello 類。但是瀏覽器與這個過程正好相反,它會先去找到 .hello 這個類,然后再去驗證它有一個父元素是 div,然后 div 再驗證它是否有一個父元素是 body。
之所以按照這樣的流程是出於性能的考慮,如果是第一種方式,找 body 下的 div 時有可能有特別多的 div,然后再在這些 div 中尋找 .hello 類時是非常耗時的。
2020 年4月28日(CSS 中清除浮動常用的兩種方法)
今天和大家分享一下 CSS 中清除浮動常用的兩種方法,學累了就來看看吧。
CSS 清除浮動
-
父元素觸發 BFC(例如可以采用 overflow:hidden 使父元素觸發 BFC)
-
利用偽元素,給父元素的最后面添加一個偽元素,令偽元素 clear: both,這樣就會使得左右兩邊都沒有浮動元素,又因為它是父元素的最下面的一個元素,所以它只能在父元素的最下面並且左右都沒有浮動元素,這樣浮動元素就在它的上面,父元素就不會發生告訴塌陷了。
代碼示例:
父元素::after { content: ''; clear: both; /* 使左右兩邊沒有浮動元素 */ display: block; visibility: hidden; height: 0; }
2020年4月29日(JS 任務隊列)
今天和大家來一起學習一下 JavaScript 事件循環機制中的任務隊列。
任務隊列分為兩種,一種是micro task,另一種是 macro-task。
macro-task大概包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task大概包括: process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性)
優先級: micro-task > macro-task;
推薦給大家一個視頻,生動形象。 兩分鍾了解 JavaScript EventLoop https://www.bilibili.com/video/BV1kf4y1U7Ln
2020年4月30日(GET 和 POST 的區別)
今天和大家一起來學習一下面試中經常問的 GET 和 POST 的區別,主要有以下幾點。
1)GET 在瀏覽器回退時是不會再次發送 GET 請求的,POST 會再次發送請求
2)GET 請求會被瀏覽器主動緩存,而 POST 不會,除非手動設置(在地址欄中輸入url的方式只能發送 GET 請求)
3)GET 請求的參數會被完整保留在瀏覽器歷史記錄里,而 POST 請求中的參數不會被保留
4)GET 請求在 URL 中傳送的參數是有長度限制的,而 POST 沒有限制
5)GET 參數通過 URL 傳遞,POST 數據放在 Request body 中
6)GET 比 POST 安全性低,因為參數直接暴露在 URL 上,所以不能用來傳遞敏感信息
2020 年5月1日(HTTP 中常用的一些狀態碼)
今天和大家一起來學習一下 HTTP 中常用的一些狀態碼。
200 OK 請求成功
206 Partial Content 客戶端發送了一個帶有 Range 頭的 GET 請求,服務端成功響應了該請求。在客戶端請求一個視頻文件的時候,服務端一般都會返回一個 206,表示將部分資源返回給客戶端。
301 Moved Permanently 所請求的資源已被永久地移到別的地方。服務端需要在響應的首部添加一個 Location 屬性來表示資源已經被移動到哪個 url 。(Location屬性是可選的,但是推薦有)
302 Found 所請求的資源被臨時移動到別的地方。服務端需要在響應的首部添加 Location 屬性來臨時定位那個資源。將來還是應該用老的 url 進行訪問。(Location屬性是可選的,但是推薦有)
304 Not Modified 請求的資源在本地有並且和服務端上的資源一致並沒有被修改。
400 Bad Request 告知客戶端發送了一個錯誤的請求,服務端不能理解
401 Unauthorized 請求未經授權,這個狀態碼必須和 WWW.Authenticate 報頭域一起使用 403 Forbidden 請求的資源被禁止訪問。
404 Not Found 請求的資源不存在
500 Internal Server Error 服務器發生不可預期的錯誤原來的緩沖文檔還可以繼續使用
503 Server Unavailable 服務器現在無法為請求提供服務,將來可能可以。如果服務器知道將來什么時候可以提供服務,可以在響應的首部添加一個 Retry-After 屬性告訴客戶端什么時候可以再次提供服務。
2020年5月2日(call,apply,bind 之間的區別)
今天和大家聊一下 call,apply,bind 之間的區別?
先來看一下相同點:
它們三個都是改變函數運行時內部 this 的指向,第一個參數指定的對象就是函數運行時 this 指向的對象。
再來看一下不同點:
call 的第二個參數是參數列表的形式,調用 call 的函數會立即執行。
fn1.call(obj, 1, 2, 3)
apply 的第二個參數是一個數組,調用 apply 的函數也會立即執行。
fn1.apply(obj, [1, 2, 3])
bind 的第二個參數是參數列表的形式,調用 bind 的函數會返回一個函數,而不是立即執行。
fn1 = fn1.bind(obj, 1, 2, 3) // 將 fn1 內部的 this 指向 obj,並且返回綁定后的函數
fn1()
2020年5月3日(比較兩個對象的內容是否相等)
由於自己做的一個鍵盤導航組件中需要用到比較兩個對象的內容是否相等,因此根據深拷貝的思想寫了一個函數分享給大家,大家可以順便復習一下深拷貝。(b 站不支持 MarkDown太不方便了,直接粘貼成圖片了,代碼可以在直播頁面上的 GIthub 倉庫中獲取)
2020年5月4日(字節筆試分享)
今天做了字節的筆試,客觀題只有五道選擇題,一個問答題,還有三道編程題。選擇題考查的外邊距重疊,事件循環機制(場景題讓你寫輸出),object.keys 的返回值(只是一個選項),window.postMessage() 跨域,JSONP 跨域只能發送 GET 請求等。
問答題是給你一段代碼,讓你找出其中的錯誤,並修正,然后針對這個問題讓你寫出盡可能多的方案。剛開始沒有 Get 到考察的點,后來意識到考察的是 this 並不會遵循作用域鏈的規則,因此需要 const that = this,然后在子函數中利用 that 才能訪問到父函數中的 this。
關於盡可能多的方案,題目中使用兩個 class 來實現的,我使用借用構造函數,借助原型鏈,組合模式這幾種都實現了一下。
關於編程題,難度 LeetCode 中等難度上下,平時一定要多刷點題,還有在在線下多練習一下各種形式的輸入讀取和各種形式的輸出,到時候可以把精力都放在算法上。
2020年5月5日(JS 中的鍵盤事件)
今天和大家分享一下 JS 中的鍵盤事件如下:
keydown: 當用戶按下任意鍵
時觸發,如果按住不放會重復觸發此事件。
keypress: 當用戶按下 字符鍵
時觸發,如果按住不放會重復觸發此事件。
keyup: 當用戶釋放按鍵時觸發。
和鍵盤息息相關的還有一個 textInput 事件
textInput: 在文本插入文本框之前觸發,發生在 keydown 和 keypress 之前,keyup 之后。
當用戶按 字符鍵 時,事件觸發順序 keydown -> keypress -> textInput -> keyup
當用戶按 非字符鍵 時,事件觸發順序 keydown -> textInput -> keyup
小伙伴們不想學,感覺效率很低的時候,建議不要硬學了,可以去瘋狂地玩一次,這樣你就會有很深的愧疚感,然后再回來學。
2020年5月6日(簡單理解進程和線程)
今天和大家聊一下操作系統中的進程和線程。
進程是資源分配的基本單位,線程是調度的基本單位。
進程包含線程,線程共用進程的資源。
為了形象化理解進程和線程,可以將進程比作工廠,工廠中的工人比作線程。
一個工廠中可以有多個工人,也就是一個進程中可以有多個線程。
多個工人共用工廠的資源,也就是線程共用進程中的資源。
各個工廠之間是是相互獨立的,也就是進程之間是相互獨立的。進程之間實際上是可以通信的,但是會比較麻煩,例如狀態同步等問題。
明天的每日一題中和大家聊一下我們前端比較關心的 chrome 瀏覽器中的進程。
b 站動態只能發 400 個字,有時真是很無奈。
2020年5月7日(學而思網校面試題分享一)
1)輸入一個 url 到渲染完成,整個過程(提示要說 https),介紹一下https, 它是如何實現加密的
2)CSS 盒模型,清除浮動,重排和重繪,如何減少重排,什么改動會造成重排
3)BFC,如何觸發 BFC
4)實現水平和垂直居中(文字形式的和div形式的),利用 flex 布局如何實現
5)JS 繼承(紅寶書中像寄生繼承等方式還需要去看)
6)深拷貝和淺拷貝的區別,手撕深拷貝(現在寫的代碼中沒有考慮到時間等對象,還有在遍歷時要用 for...in 而不是 for..of )
7)基本數據類型有哪些,基本數據類型和引用數據類型有什么區別,在內存中如何存儲的
8)瀏覽器中緩存分類,強緩存和協商緩存,分別說一下,cookie 和 session 的區別
9)快排,手撕
10)React 生命周期
11)Redux 介紹一下工作流程,源碼看過嗎
12)如何在 Redux 的工作流程中發送一個異步請求(Redux-Thunk中間件)
13)Vue 父子組件之間的通信
2020年5月8日(學而思網校面試題分享二)
14)Redux 和 Vuex 之間的區別
15)常用的 git 命令,當我說到 git rebase 的時候馬上接着問 git rebase 和 git merge 有什么區別
16)回退到某個版本如何實現
17)webpack 中如何加入一個全局變量
18)webpack 如何配置實現懶加載
19)Node 中的 EventLoop
20)如何使用 Node 創建創建一個服務器
21)ES6 中有哪些新特性,用過哪些
22)箭頭函數和普通函數的區別
23)React 中 Hooks 如何使用
24)HTTP 狀態碼(提到 304 的時候馬上提問有關瀏覽器緩存的問題)
25)Promise 是為了解決什么問題
26)數組的遍歷方法,some 和 filter 如何實現,reduce 如何使用,如果讓你實現 reduce,你會如何實現?
27)自己學的比較深的地方
28)還有沒有什么想問的?
2020年5月9日(箭頭函數和普通函數的區別)
今天和大家一起來討論一下箭頭函數和普通函數的區別,最近幾次面試幾乎都會被問到。
1)箭頭函數中的 this 指向的是定義時所在的環境,普通函數中的 this 指向的時運行時所在的環境
2)箭頭函數中不能使用 arguments
3)箭頭函數不能當做構造函數,創建實例
4)箭頭函數不能用作 Generator 函數,因為內部不可以使用 yield 命令。
字節面試題分享:
1)自我介紹
2)手寫實現 Promise.all 方法
3)算法題,n 級台階一次只能走一步或者兩步,請問有多少種方案
4)TCP 三次握手,四次揮手
5)BFC
6)Generator
7)原型
8)webpack 的實現原理
7)還有沒有什么想問的
2020年5月10日(手寫 promise.all 方法)
今天和大家分享字節跳動面試中的一道面試題,手寫實現 promise.all 方法,關於 promise.all 就不再介紹了,不清楚的小伙伴自行百度吧。
大致思路就是遍歷用戶傳遞進來的 promise 數組,然后執行 .then 方法,每執行一次,count 加 1,並將執行結果 push 到 result 數組中,當 count 等於傳入的 promise 數組的長度時,說明所有的 promise 都已經成功執行完了,此時將所有 promise 的執行結果 result, resolve 出去即可。
function promiseAll (arr) {
if (!isArray(arr)) {
throw new TypeError('You must pass array')
}
let result = []
let count = 0
arr.map((item, i) => {
item.then((res) => {
result[i] = res
count++
if (count === arr.length) {
resolve(result)
}
}).catch((e) => {
reject(new Error('fail'))
})
})
}
2020年5月10日(手寫 Promise.race 方法)
有點發燒去診所看了一下,小伙伴們多注意身體。今天和大家分享一下 promise.race 方法的實現,這個實現實現起來比 promise.all 稍微簡單一些,只要其中有一個成功,那么就執行 resolve。
function promiseRace (arr) {
if (!Array.isArray(arr)) {
console.log('You should pass an array')
}
return new Promise ((resolve, reject) => {
arr.forEach((item, i) => {
item.then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
})
}
今天晚上早點睡,大家晚安,多注意身體。
2020年5月12日(CSS 中 src 和 href 的區別)
今天首先和大家分享一下今天下午阿里的面試題,剛開始二話不說就是兩道編程題,1. 比較兩個對象的內容相等(條件很多)2. 找出一個字符串中重復子串的最大長度。然后又讓我自我介紹了一下,之后問我如何學習前端的,然后又問了幾個 React 的問題,例如虛擬 DOM,diff 算法,在設計一個組件的時候需要注意什么,React 和之前的 jQuery 等庫有什么區別等問題。
然后今天和大家分享一下 CSS 中容易混淆的 src 和 href,
href(hypertext reference)超文本引用
src(source)資源
link
和 a
標簽使用 href 屬性。
img, style, script, input, iframe
標簽使用 src 屬性。
接下來用 img 和 a 標簽來演示兩者的區別:
<img src="./1.png" alt=""/>
<a href="./1.png">點我加載圖片</a>
當我們打開頁面之后,發現只有一張圖片(img 加載出來的),a 標簽的圖片需要我們點擊點我加載圖片
才能加載出來。
因此可以看出來,href 指向的資源並不會立即加載出來顯示在頁面上,它是一個鏈接,當我們進行某項操作之后(例如 a 標簽的點擊)它才會去指向的鏈接加載資源。
src 指向的資源會被立即加載出來。
2020年5月13日(閉包中的引用基本數據類型的變量存放在棧中還是堆中)
今天和大家分享一下閉包引用的變量是保存在哪里。不是閉包中引用的變量,我們知道基本數據類型的的值是存在 棧中的,引用數據類型的地址是存在棧中,數據是存在堆中的。
但是閉包中的基本數據的變量是存在堆中還是棧中呢?
function f1 () {
let a = 1
return function () {
console.log(++a)
}
}
const f = f1()
f() // 2
f() // 3
現在 console.dir(f) 可以看到輸出為:
伴隨着 f1 的調用,為了保證變量不被銷毀(閉包中引用的變量會一直保存在內存中,這是閉包的特點),在堆中有一個 [[scope]] 對象,把變量 a 作為 Scope 的屬性給存起來,因此變量 a 並不是保存在棧中的。
詳細解釋可參考這篇博客: https://blog.csdn.net/weixin_40013817/article/details/103287271
2020年5月14日(TCP 和 UDP 的區別)
日程就先不展示在直播頁面上了,有面試了就和大家說一下,明天下午有一個滴滴面試,下周一字節二面。
今天和大家分享一下 UDP 和 TCP 的區別,概括起來主要有以下幾點:
TCP 是面向連接的,UDP 是面向無連接, 面向連接,是指發送數據之前必須在兩端建立連接,TCP 建立連接的方法是采用 “三次握手” 的方式完成的,UDP 是面向無連接的,想發送數據就發了,不需要先建立鏈接,想發送數據了應用層將數據傳遞給傳輸層的 UDP 協議,UDP 給數據增加一個 UDP 頭標識,然后就傳遞給網絡層開始發送了。
UDP 可以進行單播,多播(多對多),廣播(一對多),因為 TCP 是面向連接的所以只支持一對一的單播通信。
UDP 是不可靠的,因為它沒有三次握手的過程,想發送數據就發送數據而不管通信鏈路的情況。TCP 是可靠的,因為它會通過三次握手確認通信鏈路可用才會進行發送。
UDP 沒有擁塞控制,不能根據當前鏈路的情況來調整發送的速率等。TCP 具有擁塞控制,可以根據鏈路的情況來調整發送的速率和流量等。
2020年5月15日(變量提升)
今天和大家分享一個滴滴面試時出的一個面試題,代碼如下:
function sayName() {
console.log(num1)
console.log(num2)
var num1 = 'zhangsan'
let num2 = 'lisi'
}
sayName()
上面代碼的執行結果是什么呢?
答案: 第一個 console.log 輸出 undefied,第二個 console.log 報錯,會提示 num2 並沒有被定義。
這是因為用 var 聲明的變量會進行變量提升,但是並不會賦值,也就是說只會聲明變量 num1,但是里面的值要等到執行完 var num1 = 'zhangsan' 后才有,所以會輸出 undefined。
let 聲明的變量並不會進行變量提升,在第二個 console.log 中使用 num2 時,num2 還沒有被定義,因此會報錯。
2020年5月16日(包裝對象)
今天再和大家分享一個關於包裝對象的面試題。代碼如下:
var str = 'codingOrange'
var str1 = String(str)
var str2 = new String(str)
console.log(typeof str1)
console.log(typeof str2)
console.log(str === str1)
console.log(str === str2)
答案依次是: string, object, true, false
之前我們經常使用字符串的方法,思考過明明是基本類型,可還是能夠調用方法的原因嗎?
答案就是包裝對象,str 在調用 split 方法的時候實際上 JS 已經創建了一個包裝對象,調用的 split 方法是這個包裝對象上的。
var str = 'codingOrange'
var arr = str.split('')
調用 split 方法時,JS 引擎幫我們做的事
var str_obj = new String('condingOrange')
var arr = str_obj.split('')
str_obj = null
可以看到生成的包裝對象在調用完 split 方法之后會被立即銷毀,因此
var str = 'condingOrange'
str.addr = 'shandong'
console.log(str.addr) // undefined
因為在生成的包裝對象立即被銷毀了,所以也獲取不到 addr 屬性了
2010年5月17日(0.1 + 0.2 真的等於 0.3 嗎 )
今天和大家分享一個比較有趣的問題,在 JS 中 0.1 + 0.2 並不等於 0.3,可以在瀏覽器中試一下。
這是因為 JS 采用 IEEE 754 雙精度版本(64位),並且只要采用 IEEE 754 的語言都有該問題。
0.1
在二進制表示為 0.1 = 2^-4 * 1.10011(0011),小數算二進制和整數不同。乘法計算時,只計算小數位,整數位用作每一位的二進制,並且得到的第一位為最高位。所以我們得出 0.1 = 2^-4 * 1.10011(0011)
,那么 0.2
的演算也基本如上所示,只需要去掉第一步乘法,所以得出 0.2 = 2^-3 * 1.10011(0011)
。
回來繼續說 IEEE 754 雙精度。六十四位中符號位占一位,整數位占十一位,其余五十二位都為小數位。因為 0.1
和 0.2
都是無限循環的二進制了,所以在小數位末尾處需要判斷是否進位(就和十進制的四舍五入一樣)。
所以 2^-4 * 1.10011...001
進位后就變成了 2^-4 * 1.10011(0011 * 12次)010
。那么把這兩個二進制加起來會得出 2^-2 * 1.0011(0011 * 11次)0100
, 這個值算成十進制就是 0.30000000000000004
解決方法:
parseFloat((0.1 + 0.2).toFixed(10))
或者在計算浮點時先乘以一個 100000 等比較大的數,在計算完成之后再除以這個數。
2020年5月18日(CSS 中 opacity,transparent,rgba 設置透明時的區別)
我們知道在 CSS 中,opacity,transparent,rgba 這三個都可以實現透明的效果,但是用法上各不相同。
1、opacity用來設置元素的不透明級別,從 0.0 (完全透明)到 1.0(完全不透明)。
2、transparent是顏色的一種,這種顏色叫透明色。
3、rgba(r,g,b,a)
r:紅色值;g:綠色值;b:藍色值。三個顏色值組合在一起就形成最終顏色。
a:alpha透明度。表示像素不透明性的值。像素越不透明,則隱藏越多呈現圖像的背景。取值0~1之間,0表示完全透明的像素,1表示完全不透明的像素。
2020年5月19日(CSS 中常見的尺寸單位)
今天和大家分享一下 CSS 中常見的尺寸單位。
絕對單位:
px pixel 像素
相對單位
% 百分比(相對於父元素,例如設置子元素的寬度為 100%,那么子元素的寬度和父元素的寬度一樣)
em element meter 根據文檔字體計算尺寸
rem root element meter 根據 html 元素字體計算尺寸
ex 文檔字符 “x” 的高度
ch 文檔字符 “0” 的寬度
vh view height 可視范圍高度除以 100
vw view width 可視范圍寬度除以 100
vmin view min 可視范圍的寬度和高度中較小的那個尺寸
vmax view max 可視范圍的寬度和高度中較大的那個尺寸
關於 em 和 rem 的區別,推薦一個之前看過的感覺挺好的視頻: https://www.bilibili.com/video/BV1P7411C7EP
2020年5月20日(inline 元素設置 padding 之后會生效嗎)
今天和大家分享一個字節二面時被問到的問題,那就是 inline 元素在設置 padding 后會生效嗎?
你心里有沒有一個非常確定的答案呢?
答案就不說了,小伙伴們自行思考一下吧。
2020年5月21日(OSI 七層模型)
今天開始先和大家說一下昨天問題的答案,首先非常感謝 二次元的永痕 同學提出的看法,他的一些看法是我之前忽略掉的,最終比較合理的解釋是: inline 元素設置 padding(無論哪個方向) 時對其本身元素的尺寸是有影響的。但是 padding-top 和 padding-bottom 設置的值,在布局時對它上面和下面的元素是沒有影響的。
這段代碼就能體現出來上面說的內容。
然后再來說一下今天的知識點:OSI 的七層模型從下到上以此是:
物理層,數據鏈路層,網絡層,傳輸層,會話層,表示層,應用層
希望小伙伴們發現問題之后,及時糾正,看我的人大多都是前端初學者,咱們看待問題的角度可能比那些工作多年的前輩不太一樣,咱們之前比較容易溝通一些,所以希望大家多多指正,共同進步。
2020年5月22日(解析 url 中的參數)
今天和大家分享一些如何通過 JS 解析 url 中的參數,介紹兩種方法:
url 參數的形式 ?a=1&b=2
方法一:
先通過 substr 方法去掉開頭的 ?
,然后利用 split 方法分成 [a=1, b=2]
然后再通過遍歷和 split 方法就可以獲取到 a 和對應的值 1,b 和對應的值 2,然后再將其存入數組中即可。這樣就可以通過 paraArr.a
就可以獲取到它的值 1.
function queryUrlSearch () {
let searArr = {}
let key,value
const arr = location.search.substr(1).split('&')
arr.forEach(item => {
key = item.split('=')[0]
value = item.split('=')[1]
searArr[key] = value
})
return searArr
}
// 測試代碼
const res = queryUrlSearch()
console.log(res.a)
方法二:
使用 HTML5 中新提供的 API,URLSerarchParams,接收的參數是 location.search,返回結果后可以通過 get 方法獲取到對應參數的值。
const res = new URLSearchParams(location.search)
console.log(res.get('a')) // 輸出 1
建議親自敲一遍哦。
2020年5月23日(原碼,反碼,補碼)
今天面試美團的時候,問了原碼,反碼和補碼,然后又問了在 C 語言中一個 int 類型的變量在內存中是如何存儲的,舉個例子吧,例如數字 1,
// C 語言中,int 類型的變量占四個字節(32位)
// 原碼
0000 0000 0000 0000 0000 0000 0000 0001
// 反碼(所有的位取反)
1111 1111 1111 1111 1111 1111 1111 1110
// 補碼(反碼加1)
1111 1111 1111 1111 1111 1111 1111 1111
在內存中是按補碼來存儲的,所以全是 1.
關於為什么要用補碼簡單來說就是為了加減統一,具體介紹可參考 為什么要用補碼
2020年5月24日(兩個不規則的容器,一個 9 L,一個 4 L,怎么得到 6 L 水)
今天和大家分享一道智力題,有兩個不規則的容器,一個 9 L,一個 4 L,請問怎么得到 6 L 水?
1)將 9 L 的容器裝滿
2)倒入 4 L 的容器,倒掉
3)再次倒入 4 L 的容器,倒掉,這個時候 9 L 的容器內還剩 1 L 水
4)將這 1 L 水倒入 4 L 的容器中
5)再將 9 L 的容器裝滿,倒入 4 L 的容器並裝滿,這個時候 9 L 容器內就剩下 6 L 水了
2020年5月25日(數組亂序)
今天和大家分享一下如何將數組亂序。思想就是隨機將數組中的某些項進行互換,這個隨機性是通過 Math.random 方法來是實現的,它可以返回一個 [0, 1)
的小數,注意是左閉右開。
代碼實現:
function arrRandomOrder (arr) {
let len = arr.length
let temp
for (let i = len - 1; i >= 0; i--) {
let index = Math.floor(Math.random() * i)
temp = arr[index]
arr[index] = arr[i]
arr[i] = temp
}
}
// 測試代碼
const arr = [1, 2, 3, 4, 5, 6,7]
arrRandomOrder(arr)
console.log(arr)
每次運行這段代碼,數組的順序都會不一樣。
大家還有什么其它的方法嗎?
2020年5月26日(關於 Node 中的 res.writeHead, res.setHeader, res.write 和 res.end)
res.writeHead, res.setHeader
用來在返回的報文中設置 header
res.setHeader('Content-Type', 'text/plain')
res.writeHead(200, {'Content-Type', 'text/plain'})
res.write,res.end
用來在返回的報文中設置 body(實體)
let obj = {
err: 0
data: 1
}
res.write(JSON.stringify(obj))
res.end(JSON.stringify(obj))
res.end 實際上包含兩個過程,首先會調用 res.write 發送數據,然后發送信息告訴服務器這次響應結束。
注意報頭一定要在實體發送之前發送,當發送完 body 之后,再使用 res.writeHead 和 res.setHeader 發送的 header 將不會起作用。
另外,無論服務器在處理業務邏輯時是否發生異常,務必在結束時調用 res.end 結束請求,否則客戶端將一直處於等待狀態。當然也可以通過延遲 res.end 的方式實現客戶端和服務端的長連接,但結束時務必關閉連接。
2020年5月27日(利用 CSS 畫三角形)
今天和大家分享一下如何利用 CSS 畫三角形,我們經常設置邊框,不知道大家想過沒有,兩條邊框交界處是什么樣的?實際上,兩條邊框的交界處是一條斜線,這樣當我們利用 border-left,border-right,boder-bottom 左右各有一條斜線,這樣就會構成一個梯形,然后令元素的寬度為零,這個時候上底就變成 0 了,此時就是一個三角形了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>畫一個三角形</title>
<style type="text/css">
.triangle {
width: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid red;
}
</style>
</head>
<body>
<div class="triangle"></div>
</body>
</html>
2020年5月28日(如何實現當設置對象的一個屬性時另一個屬性自動改變)
針對這個問題,可能大家都會想到在設置一個屬性的時候再使用這個屬性自動改變另一個屬性不就可以了嗎?例如下面這樣:
let book = {
year: 2004,
version: 1
}
// 當設置年份的時候 version 自動發生變化
book.year = 2006
book.version = book.year - 2004
上面這樣做可以,但是封裝性並不是那么好,還可以使用 Object.defineProperty()
方法,通過設置對象訪問器屬性中的 set 屬性來實現。
let book = {
_year: 2004,
version: 1
}
Object.defineProperty(book, 'year', { // 不能和對象中已有的屬性重名,否則會報堆棧溢出錯誤
get: function () {
return this._year
},
set: function (newValue) {
this._year = newValue
const year = 2004
if (newValue > 2004) {
this.version = this._year - year
}
}
})
book.year = 2006
console.log(book) // {_year: 2006, version: 2}
console.log(book.year) // 2006
console.log(book.version // 2
注意在設置訪問器屬性時第二個參數的屬性名不能和對象中原有的屬性重名,否則會報堆棧溢出的錯誤。上面代碼的意思就是當執行 book.year = 2006
的時候就會執行 set 函數,然后修改 book._year
和 book.version
.
當執行 book.year
的時候就會執行 get 函數,然后獲取到 this._year
的值。
2020年5月29日(如何判斷一個屬性是在當前實例中還是原型中)
在說這個之前先說一下 in
和 hasOwnProperty
的使用,使用 in
時只要實例能夠訪問到的屬性都會返回 true,使用 hasOwnProperty
時只有當這個屬性在實例上時才會返回 true。
function People (name) {
this.name = name
}
People.prototype.say = () => {
console.log('hello')
}
const zhangsan = new People('zhangsan')
console.log('name' in zhangsan) // true
console.log('say' in zhangsan) // true
console.log(zhangsan.hasOwnProperty('name')) // true
console.log(zhangsan.hasOwnProperty('say')) // false
那么結合這兩者就可以確定一個屬性是在當前實例還是原型上,如下所示
function isPrototypeProperty (obj, prop) {
return !(obj.hasOwnProperty(prop)) && (prop in obj)
}
// 測試代碼
console.log(isPrototypeProperty(zhangsan, 'name')) // false
console.log(isPrototypeProperty(zhangsan, 'say')) // true
2020年5月30日(關於 Object.keys()
和 Object.getOwnPropertyNames()
)
兩者都是用來獲取實例自身的屬性的,並且返回值都是由實例的屬性組成的數組,區別在於 Object.keys()
只能獲取到實例自身可枚舉的屬性([[enumerable]] 為 true
),而 Object.getOwnPropertyNames()
能夠獲取到實例自身所有的可枚舉和不可枚舉的屬性。
let obj = {
a: 1,
b: 2
}
Object.defineProperty(obj, 'a', {
enumerable: false
})
let arr1 = Object.keys(obj)
console.log(arr1) // ['b']
let arr2 = Object.getOwnPropertyNames(obj)
console.log(arr2) // ['a', 'b']
2020年5月31日(獲取元素的所有子節點)
例如要獲取到 ul 元素下的所有的 li 元素。
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
<script>
const ul = document.getElementsByTagName('ul')[0]
const ulChildNodes = ul.childNodes
console.log(ulChildNodes) // NodeList(7) [text, li, text, li, text, li, text]
</script>
這個時候會發現有 7 個子節點,而不是 3 個子節點,這是因為標簽與標簽之間有空格導致的。
<ul><li>item1</li><li>item2</li><li>item3</li></ul>
<script>
const ul = document.getElementsByTagName('ul')[0]
const ulChildNodes = ul.childNodes
console.log(ulChildNodes) // NodeList(3) [li, li, li]
</script>
改成這種形式之后,發現就只有 3 個子節點了。
因此在遍歷子元素時需要判斷一下類型,如果 NodeType 為 1 說明是 Element 類型,說明是 li 元素。
// 遍歷子元素
// 首先將類數組轉換為數組
ulChildNodes = Array.from(ulChildNodes)
ulChildNodes.forEach((item, index) => {
// nodeType === 1 表示是 Element 節點
if (item.nodeType === 1) {
console.log(item)
}
}
會依次輸出三個 li 元素。
HTML 5 中新增加了一個 children 屬性,可以獲取到所有的元素子節點而且不包含其它的節點類型。
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul
<script>
const ul = document.getElementsByTagName('ul')[0]
let ulChildNodes = ul.children
console.log(ulChildNodes) // HTMLCollection(3) [li, li, li]
</script>
只會輸出三個 li 元素,而不包含其它的節點類型。
2020年6月1日(瀏覽器中對於空格的處理)
默認情況下瀏覽器將會將文字前后的空格忽略,然后文字之間的空格保留一個(即使有多個空格)
普通的空格(按空格鍵),制表符(tab 鍵)和回車換行(回車鍵)都是空格,在瀏覽器中將這些都當做空格看待。
<div class="inner">
<a> hell
o </a>
</div>
顯示結果
可以看到只有文字中間的多個空格顯示成了一個空格,文字前后的空格都被瀏覽器忽略了。
在 CSS 中可以通過 white-space
屬性對空格進行處理:
1.white-space: normal
表示瀏覽器以正常方式處理空格。
2.white-space: nowrap
所有文本顯示為一行,不會換行。
3.white-space: pre
所有空格和換行符都保留了
4.white-space: pre-wrap
文首的空格、內部的空格和換行符都保留了,超出容器的地方發生了折行。
5.white-space: pre-line
保留換行符
設置 white-space 為 pre 之后就會保留所有的回車換行和空格等
<style>
.inner a {
white-space: pre
}
</style>
<div class="inner">
<a> hell
o </a>
</div>
2020年6月2日(利用 CSS 畫 0.5px 寬度的線)
畫 0.5px 寬度的線是面試中經常會被問到的問題,今天和大家一起來學習一下。
比較常用的是通過 transform: scale(0.5);
來實現.
.border1px {
background-color: #000;
height: 1px;
margin-bottom: 30px;
}
.border_transform {
background-color: #000;
height: 1px;
transform: scaleY(0.5);
transform-origin: 50% 100%; // 不寫這條語句在 chrome 中會變成模糊的線和正常的線不同
margin-bottom: 30px;
}
<div class="border1px"></div>
<div class="border_transform"></div>
當然還可以通過 svg,設置 initial-scale 等方式,詳細介紹可參看這篇文章 怎么畫一條0.5px的線。
然后除了線之外,可能還會問你如何畫一個 0.5px 的邊框,這個一般情況下可以利用一個子元素,令其寬度和高度為 200%,border 為 1px solid #000
, 然后再設置 transform: scale(0.5),父元素為相對定位,子元素為絕對定位,並且設置子元素的 top 和 left 各為 -50% 來實現。
<div class="container">
<div class="inner"></div>
</div>
.container {
position: relative;
height: 100px;
width: 100px;
background-color: pink;
}
.inner {
position: absolute;
top: -50%;
left: -50%;
height: 200px;
width: 200px;
border: 1px solid #000;
transform: scale(0.5);
transform-origin: center;
}
2020年6月3日(ES6 中 Symbol 的幾個注意點)
Symbol 出現的原因就在於解決對象的屬性容易重名的問題,使用 Symbol 后可以創造出獨一無二的值。Symbol 是第六種簡單數據類型(null, undefined, number, boolean, string),既然是簡單數據類型因此在創建一個 Symbol 類型的值時就不能用 new 運算符。
使用示例:
let sym1 = Symbol()
let sym2 = Symbol('s')
Symbol 可以有一個 string 類型的參數,也可以沒有,因為無論有沒有它們生成的 Symbol 類型的值都是不相同的。
let sym3 = Symbol()
sym1 === sym3 // false
可以看到兩者並不相等,但是為了便於區分每個 Symbol 最好還是帶着相應的字符串。
Symbol 類型的值作為對象的屬性時,必須用方括號括起來,因為如果不括起來那么就是字符串了。
let obj = {
sym1: 1, // 屬性是一個字符串
[sym1]: 2 // 屬性是一個 Symbol 類型的值
}
obj.sym1 // 1
obj[sym1] // 2
Symbol 類型的值只可以顯式地轉換為字符串,不能進行隱式類型轉換。
let s1 = `user: ${sym2}` // 報錯,因為不能進行隱式類型轉換
let s2 = sym2.toString() // 'Symbol(s)' 注意里面的字符串已經沒有引號了
Symbol 類型的值可以轉換為 Boolean 值,任何 Symbol 值都可以轉換為 true。
除此之外,Symbol 的值不能進行任何的類型轉換。
2020年6月4日(手寫 reduce 方法)
首先說一下 reduce 方法的基本用法,該方法接收一個函數和一個初始值作為參數,這個函數有四個參數,previousValue,currentValue,index 和 array。
傳入函數的執行結果會賦值給 previousValue,然后再遍歷下一個數組元素,再次執行傳入的函數。
reduce 方法在求數組和的時候非常方便。
const arr = [1, 2, 3]
arr.reduce((res, item) => {
return res + item
}, 0)
掌握了基本使用之后嗎,面試中會讓你手動實現 reduce 方法。
Array.prototype.myReduce = function (fn, initValue) {
const self = this
let res = initValue
self.forEach((item, index) => {
res = fn (res, item, index, self)
})
return res
}
// 測試代碼
const arr = [1, 2, 3]
arr.myReduce((res, item) => {
return res + item
}, 0)
2020年6月5日(邏輯題:找出開關對應的燈)
題目
有三個開關屋內有三盞燈,只允許進一次屋,請問如何判斷出哪一個開關對應哪一盞燈?
解答
可以先將其中一盞打開兩分鍾后,關閉,然后打開另外一盞燈,現在進入屋內,可以確定屋內亮的那盞燈和外面打開開關的那個開關對應。然后再去摸一下燈的溫度,就可以確定這盞燈對應外面的哪一個開關,這樣三個就都確定了。
關於這方面的題目,需要大膽假設,腦洞大開。
2020年6月6日(邏輯題:問路)
問題
現在有兩個(東西)路口,路口有兩個人,你現在需要去問路,其中一個人說真話,一個人說假話(它們兩個人互相知道對方的答案),它們只回答是或者不是,請問怎樣在只問一個人一個問題的情況下就能知道哪條路是正確的?
解答
假設現在有東西兩個路口,而且假設東邊的路口是正確的,如果直接問肯定得不到正確的答案,因為你並不知道誰說真話誰說假話,可以采取反問的形式。
問說真話的人,我選擇東邊的這個路口,另一個人(假話)會怎樣說? ---- 不是
問說真話的認,我選擇西邊的這個路口,另一個人(假話)會怎樣說? --- 是
問說假話的人,我選擇東邊的這個路口,另一個人(真話)會怎樣說? -- 不是(注意說假話的人此時故意將說真話人的答案說反,這一點要注意,說假話的人無論問什么都會說假話)
問說假話的人,我選擇西邊的這個路口,另一個人(真話)會怎樣說? -- 是(注意說假話的人此時故意將說真話人的答案說反,這一點要注意,說假話的人無論問什么都會說假話)
從上面的結果中可以看出來,當那個人回答 不是
的時候,說明答案是正確的,當那個人回答 是
的時候說明那個人回答是錯誤的。
2020年6月7日(邏輯題:葯丸稱重)
問題
有20瓶葯,其中 19 瓶中每粒葯丸的質量為 1.0 克,另外一瓶的質量為 1.1 克,現在有一個電子秤,在只允許稱量一次的情況下,請問怎樣找出那個每粒葯丸 1.1 克的那瓶葯?
解答
可以將這 20 瓶葯進行編號,並且從第一瓶中取出 1 粒,第二瓶中取出 2 粒,第三瓶中取出 3 粒 ...... 第 20 瓶中取出 20 粒。
假設現在每一粒葯丸的質量都為 1.0 克,那么總質量為 210 克,現在假設稱出來是 210.1 克,多出來 0.1 克,說明 1.1 克的葯在第 1 瓶。如果現在稱出來的質量為 211.1 克,多出來 1.1 克,說明 1.1 克的葯在第 11 瓶。
2020年6月8日(關於運算符的執行順序)
首先給大家下面這段代碼,對象 a,b 的輸出是什么呢?
var a = {k1: 1}
var b = a
a.k3 = a = {k2: 2}
正確答案應該是
a = {
k2: 2
}
b = {
k1: 1,
k3: {
k2: 2
}
}
注意賦值運算符的執行順序是從右往左,因此 a.k3 中的 a 是之前的那個 a 指向的地址,並不是 a = {k2: 2} 中的 a 指向的地址。
如果現在你不能理解,那么請看下面的這段代碼
var a = {k1: 1}
var b = a
a = {k2: 2}
a.k3 = {k2: 2}
結果:
a = {
k2: 2,
k3: {
k2: 2
}
}
b = {
k1: 1
}
此時 a.k3 中的 a 和 a = {k2: 2} 的 a 指向的是同一個地址。
2020年6月9日(let 不能聲明已經聲明過的變量)
首先請大家看一下下面這段代碼能正確運行嗎?如果不能請說明理由,如果能請說出結果。
let a = 1
for (var a = 0; a< 3; a++) {
console.log(a)
}
正確的答案是會報錯,因為 var a = 0 存在變量提升,相當於在 let a = 1 的上面聲明了一個變量 a,由於 let 不能聲明重復的變量,因此會報錯。
2020年6月10日(防抖)
防抖指的是在用戶停止觸發某個事件一段時間后才會去執行相應的事件處理程序,非常的一個場景就是在搜索框中輸入內容時,並不會在每次輸入內容后都觸發按鍵被按下事件對應的事件處理程序,而是當用戶停止輸入一段時間之后才會去觸發對應的事件處理程序。
防抖程序實現起來也比較簡單,當 timer 不為 null 時說明用戶在 delay 時間內又觸發事件了,所以此時將之前的定時器事件清除,不讓其執行事件處理程序。
function debounce (fn, delay) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 調用用戶傳遞進來的 fn 函數
fn.apply(this, arguments)
console.log(this) // 結果為 <input type="text" id='input'>
// 將 timer 賦值為 null,不能再執行上面的 clearTimeout
timer = null
}, delay)
}
}
2020年6月11日(節流)
昨天和大家分享了防抖,今天再和大家分享一下節流,所謂節流就是當用戶在持續觸發某個事件的時候,事件處理程序不是一直被觸發而是每隔一段時間觸發一次,例如我們在頁面上拖動某個元素時,綁定的 mousemove 事件對應的事件處理程序就經過了節流的處理,讓其每隔一段時間后觸發一次,這樣無疑就提升了性能,而且選擇合適的時間間隔后用戶的肉眼也分辨不出來。
在實現上和防抖類似,只是在判斷 timer 不為 null 時就 return,不再開啟新的定時任務,當上一次定時任務到時間執行之后,再去注冊新的定時事件。
function throttle (fn, delay) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
// 外部怎樣將參數傳遞進來呢?arguments 有值嗎?
fn.apply(this, arguments)
// timer 為 null 時,啟動一個新的定時任務
timer = null
}, delay)
}
}
2020年6月12日(正則中常用的重復字符)
接下來的幾天和大家復習一下正則中的一些知識點,今天分享一下正則中常用的重復字符。
正則表達式中的重復字符:
重復字符 | 含義 |
---|---|
{n,m} | 匹配前一項至少n次,但不超過m次 |
{n,} | 匹配前一項n次或更多次 |
{n} | 匹配前一項n次 |
? | 匹配前一項0次或1次,等價於 {0,1} |
+ | 匹配前一項1次或多次,等價於 {1,} |
* | 匹配前一項0次或多次,等價於 {0,} |
2020年6月13日(正則中常見的元字符)
今天和大家分享一下正則中常見的一些元字符。
字符類 | 匹配 |
---|---|
[...] | 方括號內的任意字符 |
[^...] | 不在方括號內的任意字符 |
. | 除換行符和其它Unicode行終止符之外的任意字符 |
\w | 任何ASCII字符組成的單詞,等價於 [a-zA-Z0-9] |
\W | 任何不是ASCII字符組成的單詞,等價於 [^a-zA-Z0-9] |
\s | 任何Unicode空白符 |
\S | 任何非Unicode空白符 |
\d | 任何ASCII數字,等價於 [0-9] |
\D | 除了ASCII數字之外的任何字符,等價於 [^0-9] |
2020年6月14日(正則中與位置相關的錨字符)
今天和大家分享一下正則中與位置相關的一些錨字符。
字符 | 含義 |
---|---|
^ | 匹配字符串的開頭,在多行匹配(有修飾符m)中匹配每一行的開頭 |
$ | 匹配字符串的結尾,在多行匹配(有修飾符m)中匹配每一行的結尾 |
\b | 匹配一個單詞的邊界,簡言之,就是位於字符 \w 和 \W 之間的位置,或位於字符 \w 和字符串的開頭或者結尾之間的位置(但需要注意, [\b] 匹配的是退格符) |
\B | 匹配非單詞邊界的位置 |
(?=p) | 零度正向先行斷言,要求接下來的字符要與p匹配,並且匹配的結果不包含p,如 var result = 'JavaScript:a'.match(\JavaScript(?=:)\) 匹配結果: result[0]='JavaScript' JavaScript 后面要緊跟着是 ':' 才能匹配成功,並且結果中不包含 ':' |
(?!p) | 零度負向先行斷言,要求接下來的字符不與p匹配 |
(?<=p)(提案) | 后行斷言,要求前面的字符要與p匹配,並且匹配的結果中不包含p,如 var result = /(?<=\$)\d+/.exec('$100*80'); // result=['100'] |
(?<!p) | 后行否定斷言,要求前面的字符不能與p匹配,並且匹配結果中不包含p,如 var result= /(?!\$)\d+/.exec('$100*80'); // result=['80'] |
2020年6月15日(字符串中可以使用正則的四種方法介紹)
今天和大家分享一下字符串中可以使用正則的四種方法。
String方法 | 用法 |
---|---|
search() | 參數 :一個正則表達式 ,若不是,會先通過RegExp構造函數將其轉換成正則表達式 返回值 :第一個與之匹配的子串的起始位置,若匹配失敗返回-1 注意 :不支持全局搜索,忽略g修飾符 示例 :'JavaScript'.search(/Script/); // 4 |
replace() | 參數 :第一個是正則表達式,第二個是要進行替換的字符串 返回值 :替換后的字符串 注意 :如果第一個參數不是正則表達式,不會進行轉換 示例 :'javascript'.replace(/\w+/,'JavaScript'); // JavaScript |
match() | 參數 :一個正則表達式,若不是,會先通過RegExp構造函數將其轉換成正則表達式 返回值 :非全局匹配時:一個數組,第一個元素是與正則表達式相匹配的字符串,余下的元素是與圓括號(分組)內的子表達式匹配的字符串 全局匹配時:一個數組,元素由正則表達式相匹配的字符串組成,也就是只有非全局匹配時返回數組的第一個元素 注意 :非全局匹配時,返回的數組帶有兩個屬性,input:要匹配的字符串 index:匹配成功的子串的起始位置 示例 :var result = '1+2=3'.match(/\d+/); //result[0]='1' result.index=0 result.input='1+2=3' var result = '123'.match(/(1)\d+/); // result[0]='123' result[1]='1'result.index=0 result.input='123' 全局匹配時 :var result = '123'.match(/(1)\d+/g); // result[0]='123' 沒有index,input等屬性,也沒有result[1] |
split() | 參數 :可以是一個字符串也可以是一個正則表達式 返回值 :將一個字符串拆分為一個子串組成的數組 示例 :'1, 2, 3'.split(','); // ['1', '2', '3'] '1, 2, 3'.split(/\s*,\s*/); //['1','2','3'] |
2020年6月16日(正則表達式方法)
昨天和大家分享了字符串中可以使用正則的方法,今天和大家分享一下正則表達式的兩個方法。
方法 | 用法 |
---|---|
exec() | 參數 :一個字符串 返回值 :無論是否是全局匹配,都返回一個數組,並且第一個元素是與正則表達式相匹配的字符串,余下的元素是與圓括號內的子表達式相匹配的字串 注意 :無論是否是全局匹配返回的數組都帶有index和input屬性 示例 :var result = /(1)\d+/.exec('123'); // result[0]='123 result[1]='1' result.index=0 result.input='123' var result = /(1)\d+/g.exec('123); //result[0]='123' result[1]='1' result.index=0 result.input='123' 全局匹配 |
test() | 參數 : 一個字符串 返回值 :如果包含正則表達式的一個匹配結果,返回true,否則返回false 示例 :/\d+/.test('123'); // true /\d+/.test('abc'); // false |
2020年6月17日(數組扁平化方法一)
所謂數組扁平化指的就是將一個多維數組變成一維數組的形式,例如
let arr = [1, [2, 3, [4, 5]]]
// 經過數組扁平化之后變為
[1, 2, 3, 4, 5]
對於 arr 調用 toString 方法后會得到
‘1, 2, 3, 4, 5’
因此我們可以利用 split 方法再將其轉換為數組,然后再將其變成 Number 類型即可。
代碼如下:
// toString & split
function flatten (arr) {
return arr.toString().split(',').map(item => {
return parseInt(item)
})
}
明天再和大家分享數組扁平化的其它方法。
2020年6月18日(數組扁平化方法二)
今天和大家繼續分享數組扁平化的方法,利用遞歸結合 reduce 方法,如果遍歷的每一個元素中還有數組,那么就進行遞歸將這些數組再進行扁平化操作。
代碼如下:
function flatten(arr) {
return arr.reduce((result, item)=> {
// 遞歸終止條件 Array.isArray(item) ? flatten(item) : item
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
2020年6月19日(數組扁平化方法三)
今天和大家繼續分享數組扁平化的方法,利用數組的 some 方法,數組的 some 方法作用是遍歷數組的每一項直到回調函數中返回的是 true。下面的代碼中,some 方法中的回調函數的作用是判斷數組中遍歷的每個元素是不是數組,並將判斷結果返回。
some 方法遍歷到的元素要么是數組要么不是數組。
如果是數組,sone 方法的回調返回 true,some 遍歷就截止,進入 while 循環利用擴展運算符將數組進行扁平化,注意擴展運算符只能拆一層,也就是:
let arr = [1, [2, [3, 4]]]
...arr // [1, 2, [3, 4]]
此時會利用改變后的 arr 執行 some 方法。
如果不是數組,some 方法回調的返回值是 false,會繼續執行 some 方法,此時不會進入 while 循環。
完整代碼
function flatten (arr) {
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
2020年6月20日(數組去重方法一)
今天和大家分享一些數組去重,第一種方法就是利用 ES6 中新增的 Set 類型,它有一個特點就是它的實例中的元素不能有重復的,利用這一點就可以實現數組去重。
function unique {
return [...new Set(arr)]
}
上述方法有一個缺點就是對於引用數據類型沒有辦法去重,例如 {a: 1}
和 {a: 1}
並不能去重。
2020年6月21日(數組去重方法二)
今天和大家分享數組去重的第二種方法,利用數組的 indexof,我們知道如果某個元素不在這個數組中的話那么就會返回 -1,利用這一點進行數組去重。
function unique (arr: number[]): number[] {
let res = []
arr.forEach(element => {
if (res.indexOf(element) === -1) {
res.push(element)
}
})
return res
}
上述方法有一個缺點就是對於引用數據類型和 NaN 沒有辦法去重,例如 {a: 1}
和 {a: 1}
並不能去重。
2020年6月22日(數組去重方法三)
今天和大家分享數組去重的第三種方法,利用對象屬性的唯一性。
function unique (arr: number[]): number[] {
let res: number[] = []
let obj: object = {}
arr.forEach((element: number) => {
if (!obj[element]) {
res.push(element)
obj[element] = 1
}
})
return res
}
這種方法可以去重對象字面量和 NaN。
2020年6月23日(數組去重方法四)
今天和大家分享一下數組去重的第四種方法,遍歷兩次數組,如果遇到相同的元素就從數組中刪除一個。
function unique(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j--
}
}
}
return arr
}
上述方法有一個缺點就是對於引用數據類型和 NaN 沒有辦法去重,例如 {a: 1}
和 {a: 1}
並不能去重。
2020年6月24日(冒泡排序)
看到冒泡排序可能大家都會覺得比較簡單,在算法面試中直接讓你寫冒泡排序的可能性不大,但是它排序的過程是我們在解決有些算法問題中需要的.
它排序過程實際上是挨個將最大的元素的挪到最后面,這也是冒泡排序的由來。
例如 LeetCode164. 最大間距 就可以利用冒泡排序的思想。
function bubleSort (arr) {
for (let i = 0, len = arr.length; i < len - 1; i++) {
for (let j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
2020年6月25日(選擇排序)
插入排序也是一種比較簡單的排序方法,大體思想就是依次找出所有元素中最小的元素放在數組的最前面,它的思路很重要,可以應用在一些算法問題上,例如 LeetCode 41. 缺失的第一個正數。
function selectSort (arr) {
for (let i = 0, len = arr.length; i < len; i++) {
let indexMin = i
for (let j = i; j < len; j++) {
if (arr[j] < arr[indexMin]) {
indexMin = j
}
}
if (i !== indexMin) {
[arr[i], arr[indexMin]] = [arr[indexMin], arr[i]]
}
}
return arr
}
2020年6月26日(插入排序)
對於一個數組而言,如果只有一個元素,那么肯定是有序的,這個時候再增加一個元素,並將這兩個元素進行排序,那么這兩個元素也是具有順序,如果再增加一個元素,將這三個元素進行排序,那么這三個元素就是有序的了。
例如現在有兩個元素 1, 3
,現在又有一個元素 2,需要插入到前面已經排好序的數組中,只需要插入到 1 和 3 之間即可。
因此插入排序的思想就是遍歷整個數組,當前遍歷到的元素插入到前面已經有序的數組中合適的位置,使其也變為有序的,直到數組全遍歷完。
function insertSort (arr: number[]): number[] {
for (let i = 0, len = arr.length; i < len; i++) {
let j: number = i
while(j > 0 && arr[j] < arr[j - 1]) {
[arr[j], arr[j - 1]] = [arr[j - 1], arr[j]]
j--
}
}
return arr
}
2020年6月28日(歸並排序)
歸並排序的大體思路是先將數組進行拆分,知道每個數組中只有一個元素,然后再進行合並,合並的過程中會有排序的過程,這樣當所有的數組全都合並完成后數組也就變成有序的了。
function mergeSort (arr: number[]): number[] {
let len: number = arr.length
if (len === 1) {
return arr
}
let mid: number = Math.floor(arr.length / 2)
let left: number = arr.slice(0, mid)
let right: number = arr.slice(mid, len)
return merge (mergeSort(left), mergeSort(right))
function merge (left: number[], right: number[]): number[] {
let res: number[] = []
let i: number = 0
let j: number = 0
while (i < left.lenth && j < right.length) {
if (left[i] > right[j]) {
res.push(right[j++])
} else {
res.push(left[i++])
}
}
while(i < left.length) {
res.push(left[i++])
}
while(j < right.length) {
res.push(right[j++])
}
return res
}
}
2020年6月28日(快速排序)
快速排序是面試中比較喜歡考察的,大體思想就是選一個主元,主元的位置並不是唯一的,一般來說選擇數組的中間位置。然后再定義一個 left 指針從數組的左側開始尋找比主元大的元素,定義一個 right 指針,從數組的右側開始尋找比主元小的元素,如果同時都有那么就交換這個兩個元素,當left > right 時停止。然后將數組分成左右兩個子數組(分離的位置由partition函數得到),繼續遞歸上面的過程。
function quickSort (arr, left, right) {
// 划分左右數組的索引
let index
if (arr.length > 1) {
index = partition(arr, left, right)
if (left < index - 1) {
quickSort(arr, left, index - 1)
}
if (right > index) {
quickSort(arr, index, right)
}
}
function partition (arr, left, right) {
// 設置主元
let pivot = arr[Math.floor((left + right) / 2)],
i = left,
j = right
// 比較左右指針處的元素和主元的大小
while (i <= j) {
while (arr[i] < pivot) {
i++
}
while (arr[j] > pivot) {
j--
}
if (i <= j) {
[arr[i], arr[j]] = [arr[j], arr[i]]
i++
j--
}
}
return i
}
}
2020年6月29日(堆排序)
我們要構建出的堆的特點是父節點大於左右子節點的值,那么整個堆的根節點就是最大值,這個時候將根節點拿出來和最后一個節點進行互換,並且將根節點 push 到一個數組中,並且將堆的大小減一。在經過互換之后還需要再次構建堆,然后再進行互換,再將根節點 push 到結果數組中,並且將堆的大小減 1。循環執行這個過程,直到堆的大小為 1.這個時候結果數組中存儲的就是排好序的數組了。
function heapSort (arr) {
let heapSize = arr.length
buildHeap(arr)
while (heapSize > 1) {
[arr[heapSize - 1], arr[0]] = [arr[0], arr[heapSize - 1]]
heapSize--
heapify(arr, heapSize, 0)
}
function buildHeap (arr) {
let heapSize = arr.length
// 從下往上建立堆,第一個需要建立堆的節點是,arr.length/2
for (let i = Math.floor(heapSize / 2); i >= 0; i--) {
heapify(arr, heapSize, i)
}
}
function heapify (arr, heapSize, i) {
let left = i * 2 + 1,
right = i * 2 + 2,
maxIndex = i
// 遞歸出口
if (i >= heapSize) {
return
}
if (left < heapSize && arr[maxIndex] < arr[left]) {
maxIndex = left
}
if (right < heapSize && arr[maxIndex] < arr[right]) {
maxIndex = right
}
if (maxIndex !== i) {
[arr[maxIndex], arr[i]] = [arr[i], arr[maxIndex]]
heapify(arr, heapSize, maxIndex)
}
}
}
去實習了,暫時停更。。。