一些概念(私人筆記)

私人筆記

私人筆記,僅做方便手機閱讀使用javascript

私人筆記,僅做方便手機閱讀使用css

私人筆記,僅做方便手機閱讀使用html

markdown 給文字添加顏色

  1. <font color=red>我是紅色</font> => 我是紅色前端

  2. $\color{red}{我是紅色}$ => \color{red}{我是紅色}vue

  3. 使用帶!的引用(預覽時可能看不到效果)java

    這是一段帶紅色感嘆號的引用 node

http請求頭 Content-Type 類型

(待補充···)react

HTTP請求行、請求頭、請求體詳解webpack

Http中Content-Type的詳解ios

常見的媒體格式類型以下:

text/html : HTML格式
text/plain :純文本格式      
text/xml :  XML格式
image/gif :gif圖片格式    
image/jpeg :jpg圖片格式 
image/png:png圖片格式
以application開頭的媒體格式類型:

application/xhtml+xml :XHTML格式
application/xml     : XML數據格式
application/atom+xml  :Atom XML聚合格式    
application/json    : JSON數據格式
application/pdf       :pdf格式  
application/msword  : Word文檔格式
application/octet-stream : 二進制流數據(如常見的文件下載)
application/x-www-form-urlencoded : <form encType=''>中默認的encType,form表單數據被編碼爲key/value格式發送到服務器(表單默認的提交數據的格式)
複製代碼

另一種常見的媒體格式是上傳文件之時使用的:

multipart/form-data : 須要在表單中進行文件上傳時,就須要使用該格式
複製代碼

以上就是咱們在平常的開發中,常常會用到的若干content-type的內容格式。

es6新特性

經常使用的有:

默認參數

let、const

模板文字(${})

多行字符串(``)

for···of

解構賦值

箭頭函數

Promises

延展操做符(...)

import 和 export

ES6新特性概覽

ES6經常使用新特性總結

*ES6這些就夠了

數據類型

最新的 ECMAScript 標準定義了 7 種數據類型:

  • 6 種原始類型:

    • Boolean
    • Null
    • Undefined
    • Number
    • String
    • Symbol (ECMAScript 6 新定義)
  • Object

判斷數據類型 (typeof vs instanceof)

typeof 是否能正確判斷類型?instanceof 能正確判斷對象的原理是什麼?

typeof

typeof 對於原始類型來講,除了 null 均可以顯示正確的類型

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
// typeof 不能判斷null
typeof null // 'object'
複製代碼

typeof 對於對象來講,除了函數都會顯示 object (typeof console.log // 'function'),因此說 typeof 並不能準確判斷變量究竟是什麼類型

instanceof

若是咱們想判斷一個對象的正確類型,這時候能夠考慮使用 instanceof,由於內部機制是 經過原型鏈來判斷 的。

對於原始類型來講,你想直接經過 instanceof 來判斷類型是不行的

const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true

var str = 'hello world'
str instanceof String // false

var str1 = new String('hello world')
str1 instanceof String // true
複製代碼

值類型 vs 引用類型

除了原始類型,ES 還有引用類型,typeof 識別出來的類型中,只有objectfunction是引用類型,其餘都是值類型。

根據 JavaScript 中的變量類型傳遞方式,又分爲值類型和引用類型,值類型變量包括 BooleanStringNumberUndefinedNull引用類型包括了 Object 類的全部,如 DateArrayFunction 等。在參數傳遞方式上,值類型是按值傳遞,引用類型是按共享傳遞。

Ramda 經常使用函數

Ramda文檔

函數 做用 示例
R.clone() 克隆一個對象/數組 const a = R.clone(b)
R.defaultTo() 設置默認值(若參數爲undefined或null則取默認值) const defaultTo42 = R.defaultTo(42);
R.difference() 找出前一個數組中不包含於後一個數組中的元素 R.difference([7,6,5,4,3], [1,2,3,4]); //=> [7,6,5]
R.without() 找出後一個數組中不包含於前一個數組中的元素 R.without([7,6,5,4,3], [1,2,3,4]); //=> [1,2]
R.dissoc() 返回不包含prop屬性的新對象。(R.omit()能夠傳一個數組 R.dissoc('b', {a: 1, b: 2, c: 3}); //=> {a: 1, c: 3}
R.omit() 返回省略指定鍵的對象的部分副本。(R.dissoc()只能傳一個字符串參數) R.omit(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, c: 3}
R.pick() 返回僅包含指定鍵的對象的部分副本。不存在則返回空對象{} R.pick(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1, d: 4}
R.pickBy() 返回僅包含知足所提供謂詞的鍵的對象的部分副本。 const isUpperCase = (val, key) => key.toUpperCase() === key; R.pickBy(isUpperCase, {a: 1, b: 2, A: 3, B: 4}); //=> {A: 3, B: 4}
R.equals() 判斷是否相等 const isEquals = R.equals({info:[1,{2}]},{info:[1,{3}]}); //=>false
R.flatten() 扁平化數組 const fla = R.flatten([1,[2,3,[4,5,[6,[7,[8],[9],[10]]]]]]); //=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
R.fromPairs() 從列表鍵值對建立新對象。若是一個鍵出如今多個對中,則最右邊的對包含在對象中。 R.fromPairs([['a', 1], ['b', 2], ['c', 3]]); //=> {a: 1, b: 2, c: 3}
R.isEmpty() 判斷目標是否爲空(數組/對象/字符串) const isEmptyObj = R.isEmpty([]);//=> true
R.isNil() 判斷目標是否爲null或者undefined const isNil = R.isNil(null);//=> true
R.keys() 返回一個列表,其中包含所提供對象的全部可枚舉屬性的名稱。 R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c']
R.values() 返回所提供對象的全部可枚舉自身屬性的列表。 R.values({a: 1, b: 2, c: 3}); //=> [1, 2, 3]
R.uniq() 返回一個新列表,其中只包含原始列表中每一個元素的一個副本。([...new Set()]去重沒法處理數組和對象) R.uniq([1,2,1,1,'1',3,2,5,[6],[6],{c:'c'},{c:'c'},{c:'d'}]); //=> [1, 2, "1", 3, 5, [6],{c:'c'},{c:'d'}]
R.zipObj() 從鍵列表和值列表中建立新對象。鍵/值配對被截斷爲兩個列表中較短者的長度。 R.zipObj(['a', 'b', 'c', 'd'], [1, 2, 3]); //=> {a: 1, b: 2, c: 3}

react 高階組件

定義:高階組件就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件。

分類:代理方式、繼承方式

做用:

  1. 代碼複用
  2. 更改 props
  3. 經過 refs 獲取組件實例
  4. 抽象 state
  5. 包裝組件

高階組件示例 :

const MyContainer = (WrappedComponent) => {
    return class extends WrappedComponent {
        render() {
            const elementsTree = super.render();
            let newProps = {};
            if (elementsTree && elementsTree.type === 'input') {
                newProps = {value: 'may the force be with you'};
            }
            const props = Object.assign({}, elementsTree.props, newProps);
            const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children);
            return newElementsTree;
        }
    }
}

export default MyContainer;
複製代碼

react生命週期

生命週期(官方文檔)

componentWillMount()componentWillUpdata()componentWillReceiveProps() 在 v16.3 版本後不推薦使用,在 v17 版本計劃刪除。

componentWillMount()static getDerivedStateFromProps() 取代;componentWillUpdata()getSnapshotBeforeUpdate()取代;componentWillReceiveProps() 應避免使用

來談談Reactv16.3新生命週期知識點及遇到的問題

Markdown

安裝階段

在建立組件的實例並將其插入DOM時,將按如下順序調用這些方法:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

更新

state的更改可能致使更新。從新渲染組件時,將按如下順序調用這些方法:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

卸載

從DOM中刪除組件時調用此方法:

  • componentWillUnmount()

setState()會觸發哪些生命週期

static getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()


web前端開發,如何提升頁面性能優化?

優化原則和方向

性能優化的原則是以更好的用戶體驗爲標準,具體就是實現下面的 目標

  • 多使用內存、緩存或者其餘方法
  • 減小 CPU 和GPU 計算,更快展示

優化的 方向 有兩個:

  • 減小頁面體積,提高網絡加載
  • 優化頁面渲染

減小頁面體積,提高網絡加載

  • 靜態資源的壓縮合並(JS 代碼壓縮合並、CSS 代碼壓縮合並、雪碧圖)
  • 靜態資源緩存(資源名稱加 MD5 戳,好處
  • 使用 CDN 讓資源加載更快

優化頁面渲染

  • CSS 放前面,JS 放後面
  • 懶加載(圖片懶加載、下拉加載更多)
  • 減小DOM 查詢,對 DOM 查詢作緩存
  • 減小DOM 操做,多個操做盡可能合併在一塊兒執行(DocumentFragment)
  • 事件節流
  • 儘早執行操做(DOMContentLoaded)
  • 使用 SSR(server side render,服務端渲染) 後端渲染,數據直接輸出到 HTML 中,減小瀏覽器使用 JS 模板渲染頁面 HTML 的時間(適合首屏渲染,利於SEO)

性能優化怎麼作

上面提到的都是性能優化的單個點,性能優化項目具體實施起來,應該按照下面步驟推動:

  1. 創建性能數據收集平臺,摸底當前性能數據,經過性能打點,將上述整個頁面打開過程消耗時間記錄下來
  2. 分析耗時較長時間段緣由,尋找優化點,肯定優化目標
  3. 開始優化
  4. 經過數據收集平臺記錄優化效果
  5. 不斷調整優化點和預期目標,循環2~4步驟

性能優化是個長期的事情,不是一蹴而就的,應該本着 先摸底、再分析、後優化 的原則逐步來作。

前端性能優化最佳實踐

內容方面:

  1. 減小 HTTP 請求 (Make Fewer HTTP Requests)

  2. 減小 DOM 元素數量 (Reduce the Number of DOM Elements)

  3. 使得 Ajax 可緩存 (Make Ajax Cacheable)

針對CSS:

  1. 把 CSS 放到代碼頁上端 (Put Stylesheets at the Top)

  2. 從頁面中剝離 JavaScript 與 CSS (Make JavaScript and CSS External)

  3. 精簡 JavaScript 與 CSS (Minify JavaScript and CSS)

  4. 避免 CSS 表達式 (Avoid CSS Expressions)

針對JavaScript :

  1. 腳本放到 HTML 代碼頁底部 (Put Scripts at the Bottom)

  2. 從頁面中剝離 JavaScript 與 CSS (Make JavaScript and CSS External)

  3. 精簡 JavaScript 與 CSS (Minify JavaScript and CSS)

  4. 移除重複腳本 (Remove Duplicate Scripts)

面向圖片(Image):

  1. 優化圖片

  2. 不要在 HTML 中使用縮放圖片

  3. 使用恰當的圖片格式

  4. 使用 CSS Sprites 技巧對圖片優化

http狀態碼?

關於http部分狀態碼

1xx:1開頭的是信息狀態碼

2xx:2開頭的是請求成功

3xx:3開頭的是重定向

4xx:4開頭的是客戶端錯誤

5xx:5開頭的是服務器錯誤


爲何使用React與Redux的,它們有什麼優點

React(優點、劣勢)

優點

  1. React速度很快 :它並不直接對DOM進行操做,引入了一個叫作虛擬DOM的概念,安插在javascript邏輯和實際的DOM之間,性能好。

  2. 跨瀏覽器兼容 :虛擬DOM幫助咱們解決了跨瀏覽器問題,它爲咱們提供了標準化的API,甚至在IE8中都是沒問題的。

  3. 一切都是component :代碼更加模塊化,重用代碼更容易,可維護性高。

  4. 單向數據流 :Flux是一個用於在JavaScript應用中建立單向數據層的架構,它隨着React視圖庫的開發而被Facebook概念化。

  5. 同構、純粹的javascript :由於搜索引擎的爬蟲程序依賴的是服務端響應而不是JavaScript的執行,預渲染你的應用有助於搜索引擎優化。

  6. 兼容性好 :好比使用RequireJS來加載和打包,而Browserify和Webpack適用於構建大型應用。它們使得那些艱難的任務再也不讓人望而生畏。

缺點

  • React自己只是一個V(視圖庫)而已,因此若是是大型項目想要一套完整的框架的話,也許還須要引入Redux和route相關的東西。

虛擬DOM

虛擬DOM是在DOM的基礎上創建了一個抽象層,對數據和狀態所作的任何改動,都會被自動且高效的同步到虛擬DOM,最後再批量同步到DOM中

在React中,render執行的結果獲得的並非真正的DOM節點,而僅僅是JavaScript對象,稱之爲 虛擬DOM

innerHTML:render html字符串 + 從新建立全部 DOM 元素

虛擬DOM:render 虛擬DOM + diff + 更新必要的 DOM 元素

調用this.setState會致使re-render(從新渲染),但不會影響到整個頁面,而只會影響組件自己及其children組件。父母和兄弟姐妹都不會受到影響。當咱們有一個層級很深的組件鏈時,這會讓狀態更新變得很是方便,由於咱們只須要重繪(redraw)它的一部分。

虛擬DOM的優勢

最終表如今DOM上的修改只是變動的部分,能夠保證很是高效的渲染。

虛擬DOM的缺點

首次渲染大量DOM時,因爲多了一層虛擬DOM的計算,會比innerHTML插入慢。

淺談React的最大亮點——虛擬DOM

Redux(優點)

Redux、Reflux、Mobx 對比

Redux VS Reflux

  1. Reflux沒有reducer的概念。取而代之,和action作互動的的是store。Reflux的功能流程以下:
    Markdown

組件就是用戶界面,actions就是組件的動做,store用於執行actions的命令,並返回一個state對象給組件。組件經過state來更新界面。

而Redux的功能流程以下:

Markdown

state就是數據,組件就是數據的呈現形式,action是動做,action是經過reducer來更新state的。

  1. Reflux能夠直接調用action的方法,而Redux必須將方法綁定在組件的props上,或者使用props的dispatch方法來執行actions的方法。

Redux VS Mobx

  1. store 與多 store。在 Redux 中,你將全部的 state 都放在一個全局的 store。這個 store對象就是你的單一數據源。另外一方面,多個 reducers 容許你修改不可變的 stateMobx 則相反,它使用多 stores
  2. Redux偏向於函數式編程,️而Mobx偏向於面向對象編程和響應式編程。Redux偏向於函數式編程,因此用的是純函數,State 不可變。而MobxState 則能夠改變。

React 與 Vue 對比

Vue特色

  1. 輕量級的框架

  2. 雙向數據綁定 -- 好比你改變一個輸入框 Input 標籤的值,會自動同步更新到頁面上其餘綁定該輸入框的組件的值

  3. 組件化 -- 頁面上小到一個按鈕均可以是一個單獨的文件.vue,這些小組件直接能夠像樂高積木同樣經過互相引用而組裝起來

  4. 單向響應的數據流

  5. 指令

  6. 插件化

React特色

  1. 聲明式設計 -- React採用聲明範式,能夠輕鬆描述應用。

  2. 高效 -- React經過對DOM的模擬,最大限度地減小與DOM的交互。

  3. 靈活 --  −React能夠與已知的庫或框架很好地配合。

  4. JSX -- JSX 是 JavaScript 語法的擴展。React 開發不必定使用 JSX ,但咱們建議使用它。

  5. 組件 -- 經過 React 構建組件,使得代碼更加容易獲得複用,可以很好的應用在大項目的開發中。

  6. 單向響應的數據流 -- React 實現了單向響應的數據流,從而減小了重複代碼,這也是它爲何比傳統數據綁定更簡單。

React 和 Vue 有許多類似之處

  1. 使用 Virtual DOM(虛擬 DOM經過 diff 比對,找到變動節點,從新渲染)

  2. 提供了響應式(Reactive)和組件化(Composable)的視圖組件。

  3. 將注意力集中保持在覈心庫,而將其餘功能如路由和全局狀態管理交給相關的庫。

  4. 使用Prop傳遞數據,prop 是單向綁定的,當父組件的屬性變化時,將傳導給子組件,可是不會反過來。子組件不該該直接改變prop的值。

  5. 都提供了路由、狀態管理器(React對應的Redux,Vue對應Vuex)等。

  6. 都提供合理的鉤子函數,可讓開發者定製化地去處理需求。

  7. 在組件開發中都支持mixins的特性。

Vue 與 React 差別

性能上

  1. React 和 Vue 在大部分常見場景下都能提供近似的性能。一般 Vue 會有少許優點,由於 Vue 的 Virtual DOM 實現相對更爲輕量一些。

  2. 在 React 應用中,當某個組件的狀態發生變化時,它會以該組件爲根,從新渲染整個組件子樹。如要避免沒必要要的子組件的重渲染,有相應的處理機制PureComponent(React.Component 與 React.PureComponent)。在 Vue 應用中,組件的依賴是在渲染過程當中自動追蹤的,因此係統能精確知曉哪一個組件確實須要被重渲染。

  3. 用 Vue 和 React 開發大多數應用的速度都是足夠快的。假如你要開發一個對性能要求比較高的數據可視化或者動畫的應用時,你須要瞭解到下面這點:在開發中,Vue 每秒最高處理 10 幀,而 React 每秒最高處理不到 1 幀。

HTML & CSS

  1. 在 React 中,一切都是 JavaScript。HTML 能夠用 JSX 來表達。Vue 的總體思想是擁抱經典的 Web 技術(採用template方式,好比v-on的各類修飾符,在 JSX 中實現對應的功能會須要多得多的代碼),事實上 Vue 也提供了render渲染函數,甚至支持 JSX。

  2. 在 React 中,如今的潮流也愈來愈多地將 CSS 也歸入到 JavaScript 中來處理(有其優越性,具體不詳說),經過依賴引入css模塊,而 Vue 可讓你在每一個單文件組件中徹底訪問 CSS,方便的規定css做用域,也可引入css模塊。

其餘

  1. 二者另外一個重要差別是,Vue 的路由庫和狀態管理庫都是由官方維護支持且與核心庫同步更新的。React 則是選擇把這些問題交給社區維護,所以建立了一個更分散的生態系統。但相對的,React 的生態系統相比 Vue 更加繁榮。

  2. 從二者提供的路由、狀態管理器等向上擴展來看,Vue、React作得都比較完善,從向下擴展來看,Vue就相似於 jQuery。你只要把以下標籤放到頁面就能夠運行:

    <script src="https://unpkg.com/vue/dist/vue.js"></script>

  3. 本地渲染。ReactNative 能使你用相同的組件模型編寫有本地渲染能力的 APP(iOS 和 Android)。能同時跨多平臺開發,對開發者是很是棒的。相應地,Vue 和Weex會進行官方合做,Weex 是阿里的跨平臺用戶界面開發框架,Weex 的 JavaScript 框架運行時用的就是 Vue。這意味着在 Weex 的幫助下,你使用 Vue 語法開發的組件不只僅能夠運行在瀏覽器端,還能被用於開發 iOS 和 Android 上的原生應用。固然在如今,Weex 還在積極發展,成熟度也不能和 ReactNative 相抗衡。

  4. Vue.js在模板中提供了指令,過濾器等,能夠很是方便,快捷地操做DOM。


axios

axios 是目前最經常使用的 http 請求庫,能夠用於瀏覽器和 node.js

Axios 的主要特性包括:

基於 Promise

  • 支持瀏覽器和 node.js
  • 可添加攔截器從而轉換請求和響應數據
  • 請求能夠取消(axios.CancelToken)
  • 自動轉換 JSON 數據
  • 客戶端支持防範 XSRF (跨站請求僞造)
  • 支持各主流瀏覽器及 IE8+

手寫 XMLHttpRequest 不借助任何庫

let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
    // 這裏的函數異步執行,可參考以前 JS 基礎中的異步模塊
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {
            alert(xhr.responseText)
        }
    }
}
xhr.open("GET", "/api", false)
xhr.send(null)
複製代碼

狀態碼說明

上述代碼中,有兩處狀態碼須要說明。xhr.readyState是瀏覽器判斷請求過程當中各個階段的,xhr.status是 HTTP 協議中規定的不一樣結果的返回狀態說明。

xhr.readyState的狀態碼說明:

  • 0 -代理被建立,但還沒有調用 open() 方法。

  • 1 -open() 方法已經被調用。

  • 2 -send() 方法已經被調用,而且頭部和狀態已經可得到。

  • 3 -下載中, responseText 屬性已經包含部分數據。

  • 4 -下載操做已完成

xhr.status即 HTTP 狀態碼,有 2xx 3xx 4xx 5xx 這幾種,比較經常使用的有如下幾種:

  • 200 正常
  • 3xx
    • 301 永久重定向。如http://xxx.com這個 GET 請求(最後沒有/),就會被301到http://xxx.com/(最後是/)
    • 302 臨時重定向。臨時的,不是永久的
    • 304 資源找到可是不符合請求條件,不會返回任何主體。如發送 GET 請求時,head 中有If-Modified-Since: xxx(要求返回更新時間是xxx時間以後的資源),若是此時服務器 端資源未更新,則會返回304,即不符合要求
  • 404 找不到資源
  • 5xx 服務器端出錯了

前端跨域

同源策略:同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能。所謂同源指的是協議、域名、端口均相同。

跨域:瀏覽器從一個域名的網頁去請求另外一個域名的資源時,協議、域名、端口有一個不一樣(違反同源策略),即視爲跨域。

Jsonp

利用 src 屬性不受同源策略影響實現跨域。例如<script/><img/><iframe/> 標籤。

注意src中帶上回調函數callback

<script type="text/javascript" src="http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler"></script>
<script type="text/javascript" src=''>
    // 獲得航班信息查詢結果後的回調函數
    var flightHandler = function(data){
        alert('你查詢的航班結果是:票價 ' + data.price + ' 元,' + '餘票 ' + data.tickets + ' 張。');
    };
</script>
複製代碼

iframe跨子域

基於iframe實現的跨域要求兩個域具備aa.xx.com,bb.xx.com 這種特色, 也就是兩個頁面必須屬於一個基礎域(例如都是xxx.com),使用同一協議和同一端口,這樣在兩個頁面中同時添加document.domain,就能夠實現父頁面調用子頁面的函數。

eg:a.study.cn/a.html 請求 b.study.cn/b.html

<head>
<meta charset="UTF-8">
<title>Insert title here</title>
    <script type="text/javascript">
        document.domain = 'study.cn';
        function test() {
            alert(document.getElementById('a').contentWindow);
        }
    </script>
</head>
<body>
    <iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
複製代碼

Ajax

JQuery已經將跨域封裝到了Ajax中

<script type="text/javascript">
$(document).ready(function(){
    var name = 'chenshishuo';
    var sex = 'man';
    var address = 'shenzhen';
    var looks = 'handsome ';
     $.ajax({
         type : 'get',
         url:'http://192.168.31.137/train/test/testjsonp',
        data : {
            name : name,
            sex : sex,
            address : address,
            looks : looks,
        },
        cache :false,
        jsonp: "callback",
        jsonpCallback:"success",
        dataType : 'jsonp',
        success:function(data){
            alert(data);
        },
        error:function(data){
            alert('error');
        }        
    });
});
</script>
複製代碼

後端添加白名單


React建立組件的三種方式及其區別


token 與 session

爲何要有token與session?——身份驗證,用以判斷兩次請求是否予以經過

session生成方式?

瀏覽器第一次訪問服務器,服務器會建立一個session,而後同時爲該session生成一個惟一的會話的key,也就是sessionid。而後服務器再把sessionid,以cookie的形式發送給客戶端。這樣瀏覽器下次再訪問時,會直接帶着cookie中的sessionid。而後服務器根據sessionid找到對應的session進行匹配;

還有一種是瀏覽器禁用了cookie或不支持cookie,這種能夠經過URL重寫的方式發到服務器;

簡單來說,用戶訪問的時候說他本身是張三,他騙你怎麼辦? 那就在服務器端保存張三的信息,給他一個id,讓他下次用id訪問。

token的生成方式?

瀏覽器第一次訪問服務器,根據傳過來的惟一標識userId,服務端會經過一些算法,如經常使用的HMAC-SHA256算法,而後加一個密鑰,生成一個token,而後經過BASE64編碼一下以後將這個token發送給客戶端;客戶端將token保存起來,下次請求時,帶着token,服務器收到請求後,而後會用相同的算法和密鑰去驗證token,若是經過,執行業務操做,不經過,返回不經過信息;

token和session的區別?

token和session其實都是爲了身份驗證,session通常翻譯爲會話,而token更多的時候是翻譯爲令牌; session服務器會保存一份,可能保存到緩存,文件,數據庫;一樣,session和token都是有過時時間一說,都須要去管理過時時間;

雖然確實都是「客戶端記錄,每次訪問攜帶」,但 token 很容易設計爲自包含的,也就是說,後端不須要記錄什麼東西,每次一個無狀態請求,每次解密驗證,每次當場得出合法 /非法的結論。這一切判斷依據,除了固化在 CS 兩端的一些邏輯以外,整個信息是自包含的。這纔是真正的無狀態。 而 sessionid ,通常都是一段隨機字符串,須要到後端去檢索 id 的有效性。萬一服務器重啓致使內存裏的 session 沒了呢?萬一 redis 服務器掛了呢?

方案 A(session) :我發給你一張身份證,但只是一張寫着身份證號碼的紙片。你每次來辦事,我去後臺查一下你的 id 是否是有效。

方案 B(token) :我發給你一張加密的身份證,之後你只要出示這張卡片,我就知道你必定是本身人。


轉Boolean

在條件判斷時,除了 undefinednullfalseNaN''0-0,其餘全部值都轉爲 true,包括全部對象。

[]{}都會轉爲Number[]會轉爲0{}會轉爲NaN

if ([] == false) console.log(1);
if ([]) console.log(2);
if ({} == false ) console.log(3);
if ({}) console.log(4);
if ([1] == [1]) console.log(5);

// => 1 2 4
複製代碼

關於[]{}須要注意的點:

  1. 空數組[]和空對象{}都是object類型,所以直接用於if判斷條件時就會被轉化爲 true
  2. 任意值與布爾值比較,都會將兩邊的值轉化爲Number
  3. 若是將空數組[]與布爾值false比較,false轉化爲0,而空數組[]也轉化爲0,所以[] == false的判斷獲得true
  4. 若是將空對象{}與布爾值false比較,false轉化爲0,而空對象{}轉化爲NaN,因爲NaN與任何數都不相等,所以{} == false的判斷獲得false
  5. 引用類型之間的比較是內存地址的比較,不須要進行隱式轉換,因此 [] == [] //false 地址不同
console.log(([0]) ? true : false); // true
console.log(([0] == false) ? true : false); // true
console.log(({x:0} == false) ? true : false); // false
複製代碼

[0]直接用於if判斷條件時會被轉化爲true。 與布爾值比較,都會將兩邊的值轉化爲Number[0]轉換爲0{x:0}轉換爲NaN


深淺拷貝

淺拷貝

首先能夠經過 Object.assign 來解決這個問題。固然咱們也能夠經過展開運算符 來解決

let a = {
    age: 1
}
let b = Object.assign({}, a)
let c = {...a}
a.age = 2
console.log(b.age) // 1
console.log(c.age) // 1

複製代碼

一般淺拷貝就能解決大部分問題了,可是當咱們遇到以下狀況就須要使用到深拷貝了

let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) // native
複製代碼

淺拷貝只解決了第一層的問題,若是接下去的值中還有對象的話,那麼就又回到剛開始的話題了,二者享有相同的引用。要解決這個問題,咱們須要引入深拷貝。

深拷貝

這個問題 一般 能夠經過 JSON.parse(JSON.stringify(object)) 來解決。

let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
複製代碼

可是該方法也是有侷限性的:

  • 會忽略 undefined
  • 不能序列化函數
  • 不能解決循環引用的對象

$.extend([deep],target,Object1,Object2)

deep爲布爾值,默認不傳爲false(若是傳值只能傳true),爲true時表示深拷貝

或者藉助Ramda函數庫,Ramda.clone()


關於Promise

Promise

Promise 是異步編程的一種解決方案,用於抽象異步處理對象。能夠避免回調地獄。

Promise 對象有如下兩個特色:

  • 對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)fulfilled(已成功)rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。

  • 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從 pending 變爲 fulfilled 和從 pending 變爲 rejected 。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved(已定型)。若是改變已經發生了,你再對 Promise 對象添加回調函數,也會當即獲得這個結果。

有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操做更加容易。

await & async

使用注意點:

  1. await命令後面的 Promise對象,運行結果多是rejected,因此最好把await命令放在try...catch代碼塊中。

    async function myFunction() {
      try {
        await somethingThatReturnsAPromise();
      } catch (err) {
        console.log(err);
      }
    }
    
    // 另外一種寫法
    
    async function myFunction() {
      await somethingThatReturnsAPromise()
      .catch(function (err) {
        console.log(err);
      });
    }
    複製代碼
  2. 多個await命令後面的異步操做,若是不存在繼發關係,最好讓它們同時觸發。

    let foo = await getFoo();
    let bar = await getBar();
    複製代碼

    上面代碼中,getFoogetBar是兩個獨立的異步操做(即互不依賴),被寫成繼發關係。這樣比較耗時,由於只有getFoo完成之後,纔會執行getBar,徹底可讓它們同時觸發。

    // 寫法一
    let [foo, bar] = await Promise.all([getFoo(), getBar()]);
    
    // 寫法二
    let fooPromise = getFoo();
    let barPromise = getBar();
    let foo = await fooPromise;
    let bar = await barPromise;
    
    複製代碼

上面兩種寫法,getFoogetBar 都是同時觸發,這樣就會縮短程序的執行時間。

  1. await命令只能用在async函數之中,若是用在普通函數,就會報錯。

JS異步機制

主線程、執行棧、任務隊列:

  • 主線程不斷讀取執行棧中的同步事件,直到執行棧空
  • 異步任務結束放入任務隊列,執行棧空時主線程讀取任務隊列
  • 任務隊列讀取完畢,回到步驟1

帶你完全弄懂Event Loop

event loop是一個執行模型,在不一樣的地方有不一樣的實現。

  • js是單線程的

  • js進程分爲宏任務與微任務,優先執行微任務,後執行宏任務(其實不許確,頁面加載先執行一個<script>標籤,屬於宏任務),若微任務執行過程當中產生了新的微任務,則將改新產生的微任務置於隊列底部(例如隊列爲A -> B -> C,執行A過程當中產生了微任務A1,則將A1置於C後面A -> B -> C -> A1)

  • 常見的微任務:promiseprocess.nextTick (Node獨有,以及其餘不太常見的本次不討論)

  • 常見的宏任務:setTimeoutsetIntervalsetImmediate (Node獨有,以及其餘不太常見的本次不討論)

  • 關於process.nextTic,優先級 promise > process.nextTick > promise.then,例如:

    new Promise(function(resolve) {
        console.log('1');
        resolve();
    }).then(function() {
        console.log('2')
    })
    process.nextTick(function() {
      console.log('3');
    })
    輸出結果:1,3,2
    複製代碼
    setImmediate(function () {
      console.log(1);
    }, 0);
    setTimeout(function () {
      console.log(2);
    }, 0);
    new Promise(function (resolve) {
      console.log(3);
      resolve();
      console.log(4);
    }).then(function () {
      console.log(5);
    });
    console.log(6);
    process.nextTick(function () {
      console.log(7);
    });
    console.log(8);
    // 3 4 6 8 7 5 1 2
    複製代碼
    async function async1 () {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
    }
    
    async function async2 () {
        console.log('async2');
    }
    
    console.log('script start');
    
    setTimeout(function () {
        console.log('setTimeout');
    }, 0);
    
    async1();
    
    new Promise(function (resolve) {
        console.log('promise1');
        resolve();
    }).then(function () {
        console.log('promise2');
    });
    
    console.log('script end');
    
    輸出結果:
        script start
        async1 start
        async2
        promise1
        script end
        async1 end
        promise2
        setTimeout
        
    複製代碼

清空和截短數組

最簡單的清空和截短數組的方法就是改變 length 屬性:

const arr = [11, 22, 33, 44, 55, 66];

// 截取
arr.length = 3;
console.log(arr); //=> [11, 22, 33];

// 清空
arr.length = 0;
console.log(arr); //=> []
console.log(arr[2]); //=> undefined
複製代碼

扁平化數組

使用擴展運算符能夠快速扁平化數組(或直接使用ES6的flat()):

const arr = [11, [22, 33], [44, 55], 66];
const flatArr = [].concat(...arr); // => [11, 22, 33, 44, 55, 66]
const flatArr2 = arr.flat(); // => [11, 22, 33, 44, 55, 66]
複製代碼

不幸的是,上面的技巧只能適用 二維數組 ,可是使用遞歸,咱們能夠扁平化任意緯度數組:

function flattenArray(arr) {
  const flattened = [].concat(...arr);
  return flattened.some(item => Array.isArray(item)) ? 
    flattenArray(flattened) : flattened;
}

const arr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
const flatArr = flattenArray(arr);  //=> [11, 22, 33, 44, 55, 66, 77, 88, 99]
複製代碼

或者使用ramda函數,Ramda.flatten()

let arr = [1,2,[4,{name:'jack'},[5,{name:'tom',info:{add:'wuhan'}}]]]
R.flatten(arr) //=> [1,2,4,{name:'jack'},5,{name:'tom',info:{add:'wuhan'}}]
複製代碼

事件委託

事件委託


部分位運算的使用

位運算符

// 生成六位隨機數(數字字母組成)
// 先生成隨機數而後轉成36進制字符串(36進制 0-9a-z),而後在小數位取六位
Math.random().toString(36).slice(2,8)

(~~(Math.random()*(1<<30))).toString(36)
複製代碼

實現千分位

例如:1234567890 => 1,234,567,890

最優實現:

正則表達式的零寬斷言 /\d{1,3}(?=(\d{3})+$)/g

let a = 12345678900;
    let b = a.toString(); // b => '1234567890'
    let c = b.replace(/\d{1,3}(?=(\d{3})+$)/g, res => res + ','); // c => '1,234,567,890'
    
複製代碼

或者(不用正則)函數實現:

const  qianfen = number => {
  let earr = [];
  let arr = number.toString().split('').reverse();
  arr.forEach((i,idx) => earr.push((idx != arr.length - 1 && idx % 3 == 2) ? ',' + i : i));
  return earr.reverse().join('');
}

console.log(qianfen(123456789));  // => 123,456,789
console.log(qianfen(1234567890)); // => 1,234,567,890
複製代碼

匹配特定關鍵字並使其變顏色

思路:先將特定關鍵字取出改變,而後再渲染。此時用到 dangerouslySetInnerHTML 屬性

render() {
    let words = '杭州杭城科技有限公司';
    let keys = '杭';
    let reg = new RegExp(keys, 'ig'); // reg => /杭/gi
    const showw = w => {
      //let ss = w.replace(/杭/ig,"<b style='color: green;'>$&</b>");
      let ss = w.replace(reg,"<b style='color: green;'>$&</b>");
      return ss;
    }
    let kk = showw(words);

    return (<div className="card">
        <div className='show' style={{color:'red'}} dangerouslySetInnerHTML={{__html:kk}}></div>
    </div>)
  }
複製代碼

緩存

在介紹緩存的時候,咱們習慣將緩存分爲 強緩存協商緩存 兩種。二者的主要區別是使用本地緩存的時候,是否須要向服務器驗證本地緩存是否依舊有效。

前端緩存最佳實踐


三次握手四次揮手

以女友爲例講解 TCP/IP 三次握手與四次揮手


實現超出整數存儲範圍的兩個大整數相加function add(a,b)。注意a和b以及函數的返回值都是字符串。

function add (a, b) {
    let lenA = a.length,
        lenB = b.length,
        len = lenA > lenB ? lenA : lenB;

    // 先補齊位數一致
    if(lenA > lenB) {
        for(let i = 0; i < lenA - lenB; i++) {
            b = '0' + b;
        }
    } else {
        for(let i = 0; i < lenB - lenA; i++) {
            a = '0' + a;
        }
    }
    
    // arr 存儲最終結果的數組,carryAdd 逐位相加時產生的進位
    let arrA = a.split('').reverse(),
        arrB = b.split('').reverse(),
        arr = [],
        carryAdd = 0;

    // 逐位相加。若產生進位則carryAdd 爲 1,不然carryAdd 爲 0
    // carryAdd 逐位存儲兩數逐位相加產生的個位
    for(let i = 0; i < len; i++) {
        let temp = Number(arrA[i]) + Number(arrB[i]) + carryAdd;
        arr[i] = temp > 9 ? temp - 10 : temp;
        carryAdd = temp >= 10 ? 1 : 0;
    }

    // 最後判斷一次首位是否產生進位
    if(carryAdd === 1) {
        arr[len] = 1;
    }

    return arr.reverse().join('');
    
}

複製代碼

節流與防抖

JavaScript函數節流和函數防抖之間的區別

↑上文中節流防抖效果demo

函數節流 是指 必定時間內js方法只跑一次。好比人的眨眼睛,就是必定時間內眨一次。這是函數節流最形象的解釋。

函數防抖 是指頻繁觸發的狀況下,只有足夠的空閒時間,才執行代碼一次。好比生活中的坐公交,就是必定時間內,若是有人陸續刷卡上車,司機就不會開車。只有別人沒刷卡了,司機纔開車。

// 函數節流
let flag = false;
const throttling = (func, ms = 500) => {
  if (flag) return;
  flag = true;
  setTimeout(() => {
    func();
    flag = false;
  }, ms);
};

// 思路:定義一個flag,若是當前是空閒的則執行函數,若是當前非空閒,直接return出去。
// setTimeout()用於在固定時間後將狀態設置爲空閒。也就是固定時間(ms)只能執行一次js。
// 存在的問題:若是執行函數耗時超過了ms 那麼仍是會出現上一次沒執行完又執行下一次請求的狀況
複製代碼
// 函數防抖
let timer = 0;
const debounce = (func, ms = 500) => {
  if (timer) clearTimeout(timer);
  timer = setTimeout(() => {
    func();
  }, ms);
};


// 思路:設置一個定時器,延遲處理請求函數;若是等待時間await以後沒人執行請求,則執行函數
// 若是用戶調用該函數的間隔小於wait的狀況下,上一次的時間還未到就被清除了,並不會執行函數
複製代碼

圖片大小計算

大小 = 分辨率 * 位深/8 (/8計算的是字節數。)

分辨率 = 寬 * 高(如:1024 * 768,640 * 480)

位深:如24位,16位,8位

例如: 一幅圖像分辨率:1024*768,24位,則其大小計算以下:

大小 = 1024 * 768 * 24 / 8 = 2359296byte = 2304KB


瀏覽器從加載頁面到渲染頁面的過程(輸入URL後發生了什麼)

加載過程

要點以下:

  • 瀏覽器根據 DNS 服務器獲得域名的 IP 地址 (中間有三次握手)
  • 向這個 IP 的機器發送 HTTP 請求
  • 服務器收到、處理並返回 HTTP 請求
  • 瀏覽器獲得返回內容

例如在瀏覽器輸入https://juejin.im/timeline,而後通過 DNS 解析,juejin.im對應的 IP 是36.248.217.149(不一樣時間、地點對應的 IP 可能會不一樣)。而後瀏覽器向該 IP 發送 HTTP 請求。server 端接收到 HTTP 請求,而後通過計算(向不一樣的用戶推送不一樣的內容),返回 HTTP 請求,返回一堆 HMTL 格式的字符串,由於只有 HTML格式瀏覽器才能正確解析。接下來就是瀏覽器的渲染過程。

渲染過程

要點以下:

  • 根據 HTML 結構生成 DOM
  • 根據 CSS 生成 CSSOM
  • DOMCSSOM 整合造成 RenderTree
  • 根據 RenderTree 開始渲染和展現
  • 遇到<script>時,會執行並阻塞渲染

重繪與迴流

當元素的樣式發生變化時,瀏覽器須要觸發更新,從新繪製元素。這個過程當中,有兩種類型的操做,即重繪與迴流。

  • 重繪(repaint): 當元素樣式的改變不影響佈局時,瀏覽器將使用重繪對元素進行更新,此時因爲只須要UI層面的從新像素繪製,所以 損耗較少

  • 迴流(reflow): 當元素的尺寸、結構或觸發某些屬性時,瀏覽器會從新渲染頁面,稱爲迴流。此時,瀏覽器須要從新通過計算,計算後還須要從新頁面佈局,所以是較重的操做。會觸發迴流的操做:

    • 頁面初次渲染

    • 瀏覽器窗口大小改變

    • 元素尺寸、位置、內容發生改變

    • 元素字體大小變化

    • 添加或者刪除可見的 dom 元素

    • 激活 CSS 僞類(例如::hover)

    • 查詢某些屬性或調用某些方法:

      • clientWidth、clientHeight、clientTop、clientLeft
      • offsetWidth、offsetHeight、offsetTop、offsetLeft
      • scrollWidth、scrollHeight、scrollTop、scrollLeft
      • getComputedStyle()
      • getBoundingClientRect()
      • scrollTo()

迴流一定觸發重繪,重繪不必定觸發迴流。重繪的開銷較小,迴流的代價較高


Web 安全

題目:前端常見的安全問題有哪些?

Web 前端的安全問題,能回答出下文的兩個問題,這個題目就能基本過關了。開始以前,先說一個最簡單的攻擊方式 —— SQL 注入。

上學的時候就知道有一個「SQL注入」的攻擊方式。例如作一個系統的登陸界面,輸入用戶名和密碼,提交以後,後端直接拿到數據就拼接 SQL 語句去查詢數據庫。若是在輸入時進行了惡意的 SQL 拼裝,那麼最後生成的 SQL 就會有問題。可是如今稍微大型一點的系統,都不會這麼作,從提交登陸信息到最後拿到受權,要通過層層的驗證。所以,SQL 注入都只出如今比較低端小型的系統上。

XSS(Cross Site Scripting,跨站腳本攻擊)

這是前端最多見的攻擊方式,不少大型網站(如 Facebook)都被 XSS 攻擊過。

舉一個例子,我在一個博客網站正常發表一篇文章,輸入漢字、英文和圖片,徹底沒有問題。可是若是我寫的是惡意的 JS 腳本,例如獲取到document.cookie而後傳輸到本身的服務器上,那我這篇博客的每一次瀏覽都會執行這個腳本,都會把訪客 cookie 中的信息偷偷傳遞到個人服務器上來。

其實原理上就是黑客經過某種方式(發佈文章、發佈評論等)將一段特定的 JS 代碼隱蔽地輸入進去。而後別人再看這篇文章或者評論時,以前注入的這段 JS 代碼就執行了。JS 代碼一旦執行,那可就不受控制了,由於它跟網頁原有的 JS 有一樣的權限,例如能夠獲取 server 端數據、能夠獲取 cookie 等。因而,攻擊就這樣發生了。

XSS的危害

XSS 的危害至關大,若是頁面能夠隨意執行別人不安全的 JS 代碼,輕則會讓頁面錯亂、功能缺失,重則會形成用戶的信息泄露。

好比早些年社交網站常常爆出 XSS 蠕蟲,經過發佈的文章內插入 JS,用戶訪問了感染不安全 JS 注入的文章,會自動從新發布新的文章,這樣的文章會經過推薦系統進入到每一個用戶的文章列表面前,很快就會形成大規模的感染。

還有利用獲取 cookie 的方式,將 cookie 傳入入侵者的服務器上,入侵者就能夠模擬 cookie 登陸網站,對用戶的信息進行篡改。

XSS的預防

那麼如何預防 XSS 攻擊呢?—— 最根本的方式,就是對用戶輸入的內容進行驗證和替換,須要替換的字符有:

& 替換爲:&amp;
< 替換爲:&lt;
> 替換爲:&gt;
」 替換爲:&quot;
‘ 替換爲:&#x27;
/ 替換爲:&#x2f;
複製代碼

替換了這些字符以後,黑客輸入的攻擊代碼就會失效,XSS 攻擊將不會輕易發生。

除此以外,還能夠經過對 cookie 進行較強的控制,好比對敏感的 cookie 增長http-only限制,讓 JS 獲取不到 cookie 的內容。

CSRF(Cross-site request forgery,跨站請求僞造)

CSRF 是借用了當前操做者的權限來偷偷地完成某個操做,而不是拿到用戶的信息

例如,一個支付類網站,給他人轉帳的接口是https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf,而這個接口在使用時沒有任何密碼或者 token 的驗證,只要打開訪問就直接給他人轉帳。一個用戶已經登陸了http://buy.com,在選擇商品時,忽然收到一封郵件,而這封郵件正文有這麼一行代碼<img src="https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf"/>,他訪問了郵件以後,其實就已經完成了購買。

CSRF 原理示意圖:

Markdown

CSRF 的發生實際上是藉助了一個 cookie 的特性。咱們知道,登陸了http://buy.com以後,cookie 就會有登陸過的標記了,此時請求https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf是會帶着 cookie 的,所以 server 端就知道已經登陸了。而若是在 http://buy.com 去請求其餘域名的 API 例如http://abc.com/api時,是不會帶 cookie 的,這是瀏覽器的同源策略的限制。可是 —— 此時在其餘域名的頁面中,請求https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf,會帶着buy.comcookie ,這是發生 CSRF 攻擊的理論基礎

預防 CSRF 就是加入各個層級的權限驗證,例如如今的購物網站,只要涉及現金交易,確定要輸入密碼或者指紋才行。除此以外,敏感的接口使用POST請求而不是GET 也是很重要的。


何爲構建工具

「構建」也可理解爲「編譯」,就是將開發環境的代碼轉換成運行環境代碼的過程。開發環境的代碼是爲了更好地閱讀,而運行環境的代碼是爲了更快地執行,二者目的不同,所以代碼形式也不同。例如,開發環境寫的 JS 代碼,要經過混淆壓縮以後才能放在線上運行,由於這樣代碼體積更小,並且對代碼執行不會有任何影響。總結一下須要構建工具處理的幾種狀況:

  • 處理模塊化:CSS 和 JS 的模塊化語法,目前都沒法被瀏覽器兼容。所以,開發環境可使用既定的模塊化語法,可是須要構建工具將模塊化語法編譯爲瀏覽器可識別形式。例如,使用 webpack、Rollup 等處理 JS 模塊化。
  • 編譯語法:編寫 CSS 時使用 Less、Sass,編寫 JS 時使用 ES六、TypeScript 等。這些標準目前也都沒法被瀏覽器兼容,所以須要構建工具編譯,例如使用 Babel 編譯 ES6 語法。
  • 代碼壓縮:將 CSS、JS 代碼混淆壓縮,爲了讓代碼體積更小,加載更快。

進程與線程區別?JS 單線程帶來的好處?

本質上來講,兩個名詞都是 CPU工做時間片的一個描述

進程 描述了 CPU 在運行指令及加載和保存上下文所需的時間,放在應用上來講就表明了一個程序。線程 是進程中的更小單位,描述了執行一段指令所需的時間。

把這些概念拿到瀏覽器中來講,當你打開一個 Tab 頁時,其實就是建立了一個進程,一個進程中能夠有多個線程,好比渲染線程、JS 引擎線程、HTTP 請求線程等等。當你發起一個請求時,其實就是建立了一個線程,當請求結束後,該線程可能就會被銷燬。

若是在 JS 執行的時候 UI 線程還在工做,就可能致使不能安全的渲染 UI。這其實也是一個單線程的好處,得益於 JS 是單線程運行的,能夠達到節省內存,節約上下文切換時間,沒有鎖的問題的好處。

執行棧

能夠把執行棧認爲是一個 存儲函數調用的棧結構 ,遵循先進後出的原則。

instanceof 的原理是什麼?

instanceof 能夠正確的判斷對象的類型,由於內部機制是 經過判斷對象的原型鏈中是否是能找到類型的 prototype

原型 / 構造函數 / 實例

  • 原型(prototype): 一個簡單的對象,用於實現對象的 屬性繼承。能夠簡單的理解成對象的爹。在 Firefox 和 Chrome 中,每一個JavaScript對象中都包含一個__proto__ (非標準)的屬性指向它爹(該對象的原型),可obj.__proto__進行訪問。

  • 構造函數: 能夠經過new來 新建一個對象 的函數。

  • 實例: 經過構造函數和new建立出來的對象,即是實例。 實例經過__proto__指向原型,經過constructor指向構造函數。

舉個栗子,以 Object 爲例,咱們經常使用的 Object 即是一個構造函數,所以咱們能夠經過它構建實例。

// 實例
const instance = new Object()
複製代碼

則此時, 實例爲 instance ,構造函數爲 Object ,咱們知道,構造函數擁有一個 prototype 的屬性指向原型,所以原型爲:

// 原型
const prototype = Object.prototype
複製代碼

這裏咱們能夠來看出三者的關係:

實例.__proto__ === 原型

原型.constructor === 構造函數

構造函數.prototype === 原型

// 這條線實際上是是基於原型進行獲取的,能夠理解成一條基於原型的映射線
// 例如: 
// const o = new Object()
// o.constructor === Object   --> true
// o.__proto__ = null;
// o.constructor === Object   --> false
實例.constructor === 構造函數
複製代碼

關係圖以下:

Markdown

let shili = new Object();
let yuanxin = Object.prototype;
實例: shili
構造函數: Object 或 shili.constructor 或 yuanxin.constructor
原型:yuanxin (Object.prototype)  或 shili.__proto__
複製代碼

原型與原型鏈

其實每一個 JS 對象都有 __proto__ 屬性,這個屬性指向了原型

原型 也是一個對象,而且這個對象中包含了不少函數,咱們能夠得出一個結論:對於 obj 來講,能夠經過 __proto__ 找到一個原型對象,在該對象中定義了不少函數讓咱們來使用。

原型的 constructor 屬性指向構造函數,構造函數又經過 prototype 屬性指回原型,可是並非全部函數都具備這個屬性,Function.prototype.bind() 就沒有這個屬性。

其實 原型鏈 就是多個對象經過 __proto__ 的方式鏈接了起來。

  • Object 是全部對象的爸爸,全部對象均可以經過 proto 找到它
  • Function 是全部函數的爸爸,全部函數均可以經過 proto 找到它
  • 函數的 prototype 是一個對象
  • 對象的 proto 屬性指向原型, proto 將對象和原型鏈接起來組成了原型鏈

模塊化

模塊化就是 將文件按照功能分離,根據需求引入不一樣的文件中 。源於服務器端。

使用模塊化能夠給咱們帶來如下好處:

  • 解決命名衝突
  • 提供複用性
  • 提升代碼可維護性

Proxy

代理模式(英語:Proxy Pattern)是程序設計中的一種設計模式。

在MDN上對於 Proxy 的解釋是:

Proxy 對象用於定義基本操做的自定義行爲(如屬性查找,賦值,枚舉,函數調用等)

簡單來講: Proxy 對象就是可讓你去對JavaScript中的一切合法對象的基本操做進行自定義。而後用你自定義的操做去覆蓋其對象的基本操做。也就是當一個對象去執行一個基本操做時,其執行的過程和結果是你自定義的,而不是對象的。

Proxy的做用 :

對於代理模式 Proxy 的做用主要體如今三個方面:

  1. 攔截和監視外部對對象的訪問

  2. 下降函數或類的複雜度

  3. 在複雜操做前對操做進行校驗或對所需資源進行管理

路由原理

前端路由原理?兩種實現方式有什麼區別?

前端路由實現起來其實很簡單,本質就是 監聽 URL 的變化,而後匹配路由規則,顯示相應的頁面,而且無須刷新頁面 。目前前端使用的路由就只有兩種實現方式:

  • Hash 模式
  • History 模式

兩種模式對比

  • Hash 模式只能夠更改 # 後面的內容,History 模式能夠經過 API 設置任意的同源 URL
  • History 模式能夠經過 API 添加任意類型的數據到歷史記錄中,Hash 模式只能更改哈希值,也就是字符串
  • Hash 模式無需後端配置,而且兼容性好。History 模式在用戶手動輸入地址或者刷新頁面的時候會發起 URL 請求,後端須要配置 index.html 頁面用於匹配不到靜態資源的時候

Babel

Babel 是一個 JavaScript 編譯器

Babel 是一個工具鏈,主要用於在舊的瀏覽器或環境中將 ECMAScript 2015+ 代碼轉換爲向後兼容版本的 JavaScript 代碼:

  • 轉換語法
  • Polyfill 實現目標環境中缺乏的功能 (經過 @babel/polyfill)
  • 源代碼轉換 (codemods)

特性

  • Babel 能夠轉換 JSX 語法
  • Babel 能夠刪除類型註釋!請注意 Babel 不會進行類型檢查;你仍然能夠安裝使用 Flow 或者 TypeScript 來進行類型檢查。
  • 可插拔。Babel 是用插件構建的。你可使用現有插件編寫本身的轉換管道或編寫本身的插件。經過使用或建立 preset 輕鬆使用一組插件。
  • 可調試。支持 Source map ,所以你能夠輕鬆調試編譯過的代碼。
  • 壓縮性。Babel 嘗試使用盡量少的代碼而不依賴於龐大的運行時環境。

受控組件與非受控組件

受控組件

組件狀態由state控制。假設咱們如今有一個表單,表單中有一個input標籤,input的value值必須是咱們設置在constructor構造函數的state中的值,而後,經過onChange觸發事件來改變state中保存的value值,這樣造成一個循環的迴路影響。也能夠說是React負責渲染表單的組件仍然控制用戶後續輸入時所發生的變化。

非受控組件

組件狀態不禁state控制。其值能夠經過refs獲取。常見的有input(不添加valuedefaultvalueonChange()等)

非受控組件(codepen)

handleSubmit = event => {
    const val = this.refs.inputRef.value;
    alert('A name was submitted: ' + val);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref='inputRef'/>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
複製代碼

瀏覽器內核

函數 做用
Trident IE內核
Gecko Firefox瀏覽器內核
Webkit Safari瀏覽器內核
Presto Opera瀏覽器內核,最初是本身的Presto內核,後來是Webkit,如今是Blink內核
Chromium 統稱爲Chromium內核或Chrome內核,之前是Webkit內核,如今是Blink內核

js快速排序

const b = [1,4,6,3,7,4,6,3,2,9];
const quickSort = arr => {
  const len = arr.length;
  if (len <= 1) return arr;
  const s = Math.floor(len / 2);
  const temp = arr.splice(s, 1);
  let left=[],
      right=[];
  arr.forEach(i => i < temp ? left.push(i) : right.push(i));
  return quickSort(left).concat(temp, quickSort(right));  
}
console.log(quickSort(b)); // => [1, 2, 3, 3, 4, 4, 6, 6, 7, 9]
複製代碼

盒模型

頁面渲染時,dom 元素所採用的 佈局模型。可經過box-sizing進行設置。根據計算寬高的區域可分爲:

  • content-box (W3C 標準盒模型)
  • border-box (IE 盒模型)
  • padding-box (僅Firefox曾實現,且已在Firefox 50 版本中被刪除)

盒模型包括marginborderpaddingcontent

區別:

content-box 計算時content 不包含 borderpadding,而 border-boxcontent則包含 borderpadding

示例:

.box {
    width: 200px;
    height:100px;
    margin:10px;
    padding:5px;
    border:1px;
    box-sizing: ···
}
複製代碼

若設置 box-sizing: content-box;,則box寬度爲: 200 + (10 + 5 + 1) * 2 = 232px; ,content 部分寬度爲 200px;

若設置 box-sizing: border-box;,則box寬度爲: 200 + 10 * 2 = 220px; ,content 部分寬度爲 200 - (10 + 1) * 2 = 178px;

BFC

塊格式化上下文(Block Formatting Context,BFC) 是Web頁面的可視化CSS渲染的一部分,是塊盒子的佈局過程發生的區域,也是浮動元素與其餘元素交互的區域。

常見建立 BFC 方法:

  • 浮動元素(元素的 float 不是 none)
  • 絕對定位元素(元素的 position 爲 absolute 或 fixed)
  • 行內塊元素(元素的 display 爲 inline-block)
  • overflow 值不爲 visible 的塊元素
  • display 值爲 flow-root 的元素
  • 彈性元素(display爲 flex 或 inline-flex元素的直接子元素)
  • 網格元素(display爲 grid 或 inline-grid 元素的直接子元素

做用:

  • 讓浮動內容和周圍的內容等高
  • 防止外邊距塌陷

選擇器優先級

!important > 行內樣式 > #id > .class > tag > * > 繼承 > 默認

去除浮動影響,防止父級高度塌陷

  1. 經過增長尾元素清除浮動:after / <br> / clear: both
  2. 建立父級 BFC
  3. 父級設置高度

link 與 @import 的區別

  • link功能較多,能夠定義 RSS,定義 Rel 等做用,而@import只能用於加載 css
  • 當解析到link時,頁面會同步加載所引的 css,而@import所引用的 css 會等到頁面加載完才被加載
  • @import須要 IE5 以上才能使用 link可使用 js 動態引入,@import不行
相關文章
相關標籤/搜索