私人筆記,僅做方便手機閱讀使用javascript
私人筆記,僅做方便手機閱讀使用css
私人筆記,僅做方便手機閱讀使用html
<font color=red>我是紅色</font>
=> 我是紅色前端
$\color{red}{我是紅色}$
=> vue
使用帶!的引用(預覽時可能看不到效果)java
這是一段帶紅色感嘆號的引用 node
(待補充···)react
HTTP請求行、請求頭、請求體詳解webpack
常見的媒體格式類型以下:
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的內容格式。
經常使用的有:
默認參數
let、const
模板文字(${})
多行字符串(``)
for···of
解構賦值
箭頭函數
Promises
延展操做符(...)
import 和 export
最新的 ECMAScript 標準定義了 7 種數據類型:
6 種原始類型:
Boolean
Null
Undefined
Number
String
Symbol
(ECMAScript 6 新定義)和 Object
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
複製代碼
除了原始類型,ES 還有引用類型,typeof
識別出來的類型中,只有object
和 function
是引用類型,其餘都是值類型。
根據 JavaScript
中的變量類型傳遞方式,又分爲值類型和引用類型,值類型變量包括 Boolean
、String
、Number
、Undefined
、Null
,引用類型包括了 Object
類的全部,如 Date
、Array
、Function
等。在參數傳遞方式上,值類型是按值傳遞,引用類型是按共享傳遞。
函數 | 做用 | 示例 |
---|---|---|
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} |
定義:高階組件就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件。
分類:代理方式、繼承方式
做用:
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;
複製代碼
componentWillMount()
、componentWillUpdata()
、componentWillReceiveProps()
在 v16.3 版本後不推薦使用,在 v17 版本計劃刪除。
componentWillMount()
被static getDerivedStateFromProps()
取代;componentWillUpdata()
被getSnapshotBeforeUpdate()
取代;componentWillReceiveProps()
應避免使用
安裝階段
在建立組件的實例並將其插入DOM時,將按如下順序調用這些方法:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
更新
state的更改可能致使更新。從新渲染組件時,將按如下順序調用這些方法:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸載
從DOM中刪除組件時調用此方法:
componentWillUnmount()
static getDerivedStateFromProps()
→ shouldComponentUpdate()
→ render()
→ getSnapshotBeforeUpdate()
→ componentDidUpdate()
性能優化的原則是以更好的用戶體驗爲標準,具體就是實現下面的 目標:
優化的 方向 有兩個:
減小頁面體積,提高網絡加載
優化頁面渲染
性能優化怎麼作
上面提到的都是性能優化的單個點,性能優化項目具體實施起來,應該按照下面步驟推動:
性能優化是個長期的事情,不是一蹴而就的,應該本着 先摸底、再分析、後優化 的原則逐步來作。
內容方面:
減小 HTTP 請求 (Make Fewer HTTP Requests)
減小 DOM 元素數量 (Reduce the Number of DOM Elements)
使得 Ajax 可緩存 (Make Ajax Cacheable)
針對CSS:
把 CSS 放到代碼頁上端 (Put Stylesheets at the Top)
從頁面中剝離 JavaScript 與 CSS (Make JavaScript and CSS External)
精簡 JavaScript 與 CSS (Minify JavaScript and CSS)
避免 CSS 表達式 (Avoid CSS Expressions)
針對JavaScript :
腳本放到 HTML 代碼頁底部 (Put Scripts at the Bottom)
從頁面中剝離 JavaScript 與 CSS (Make JavaScript and CSS External)
精簡 JavaScript 與 CSS (Minify JavaScript and CSS)
移除重複腳本 (Remove Duplicate Scripts)
面向圖片(Image):
優化圖片
不要在 HTML 中使用縮放圖片
使用恰當的圖片格式
使用 CSS Sprites 技巧對圖片優化
1xx:1開頭的是信息狀態碼
2xx:2開頭的是請求成功
3xx:3開頭的是重定向
4xx:4開頭的是客戶端錯誤
5xx:5開頭的是服務器錯誤
優點:
React速度很快 :它並不直接對DOM進行操做,引入了一個叫作虛擬DOM的概念,安插在javascript邏輯和實際的DOM之間,性能好。
跨瀏覽器兼容 :虛擬DOM幫助咱們解決了跨瀏覽器問題,它爲咱們提供了標準化的API,甚至在IE8中都是沒問題的。
一切都是component :代碼更加模塊化,重用代碼更容易,可維護性高。
單向數據流 :Flux是一個用於在JavaScript應用中建立單向數據層的架構,它隨着React視圖庫的開發而被Facebook概念化。
同構、純粹的javascript :由於搜索引擎的爬蟲程序依賴的是服務端響應而不是JavaScript的執行,預渲染你的應用有助於搜索引擎優化。
兼容性好 :好比使用RequireJS來加載和打包,而Browserify和Webpack適用於構建大型應用。它們使得那些艱難的任務再也不讓人望而生畏。
缺點:
虛擬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插入慢。
Redux VS Reflux:
組件就是用戶界面,actions就是組件的動做,store用於執行actions的命令,並返回一個state對象給組件。組件經過state來更新界面。
而Redux的功能流程以下:
state就是數據,組件就是數據的呈現形式,action是動做,action是經過reducer來更新state的。
Redux VS Mobx
store
與多 store
。在 Redux
中,你將全部的 state
都放在一個全局的 store
。這個 store
對象就是你的單一數據源。另外一方面,多個 reducers
容許你修改不可變的 state
。 Mobx
則相反,它使用多 stores
。State
不可變。而Mobx
的 State
則能夠改變。輕量級的框架
雙向數據綁定 -- 好比你改變一個輸入框 Input 標籤的值,會自動同步更新到頁面上其餘綁定該輸入框的組件的值
組件化 -- 頁面上小到一個按鈕均可以是一個單獨的文件.vue,這些小組件直接能夠像樂高積木同樣經過互相引用而組裝起來
單向響應的數據流
指令
插件化
聲明式設計 -- React採用聲明範式,能夠輕鬆描述應用。
高效 -- React經過對DOM的模擬,最大限度地減小與DOM的交互。
靈活 -- −React能夠與已知的庫或框架很好地配合。
JSX -- JSX 是 JavaScript 語法的擴展。React 開發不必定使用 JSX ,但咱們建議使用它。
組件 -- 經過 React 構建組件,使得代碼更加容易獲得複用,可以很好的應用在大項目的開發中。
單向響應的數據流 -- React 實現了單向響應的數據流,從而減小了重複代碼,這也是它爲何比傳統數據綁定更簡單。
使用 Virtual DOM(虛擬 DOM經過 diff 比對,找到變動節點,從新渲染)
提供了響應式(Reactive)和組件化(Composable)的視圖組件。
將注意力集中保持在覈心庫,而將其餘功能如路由和全局狀態管理交給相關的庫。
使用Prop傳遞數據,prop 是單向綁定的,當父組件的屬性變化時,將傳導給子組件,可是不會反過來。子組件不該該直接改變prop的值。
都提供了路由、狀態管理器(React對應的Redux,Vue對應Vuex)等。
都提供合理的鉤子函數,可讓開發者定製化地去處理需求。
在組件開發中都支持mixins的特性。
性能上
React 和 Vue 在大部分常見場景下都能提供近似的性能。一般 Vue 會有少許優點,由於 Vue 的 Virtual DOM 實現相對更爲輕量一些。
在 React 應用中,當某個組件的狀態發生變化時,它會以該組件爲根,從新渲染整個組件子樹。如要避免沒必要要的子組件的重渲染,有相應的處理機制PureComponent(React.Component 與 React.PureComponent)。在 Vue 應用中,組件的依賴是在渲染過程當中自動追蹤的,因此係統能精確知曉哪一個組件確實須要被重渲染。
用 Vue 和 React 開發大多數應用的速度都是足夠快的。假如你要開發一個對性能要求比較高的數據可視化或者動畫的應用時,你須要瞭解到下面這點:在開發中,Vue 每秒最高處理 10 幀,而 React 每秒最高處理不到 1 幀。
HTML & CSS
在 React 中,一切都是 JavaScript。HTML 能夠用 JSX 來表達。Vue 的總體思想是擁抱經典的 Web 技術(採用template方式,好比v-on的各類修飾符,在 JSX 中實現對應的功能會須要多得多的代碼),事實上 Vue 也提供了render渲染函數,甚至支持 JSX。
在 React 中,如今的潮流也愈來愈多地將 CSS 也歸入到 JavaScript 中來處理(有其優越性,具體不詳說),經過依賴引入css模塊,而 Vue 可讓你在每一個單文件組件中徹底訪問 CSS,方便的規定css做用域,也可引入css模塊。
其餘
二者另外一個重要差別是,Vue 的路由庫和狀態管理庫都是由官方維護支持且與核心庫同步更新的。React 則是選擇把這些問題交給社區維護,所以建立了一個更分散的生態系統。但相對的,React 的生態系統相比 Vue 更加繁榮。
從二者提供的路由、狀態管理器等向上擴展來看,Vue、React作得都比較完善,從向下擴展來看,Vue就相似於 jQuery。你只要把以下標籤放到頁面就能夠運行:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
本地渲染。ReactNative 能使你用相同的組件模型編寫有本地渲染能力的 APP(iOS 和 Android)。能同時跨多平臺開發,對開發者是很是棒的。相應地,Vue 和Weex會進行官方合做,Weex 是阿里的跨平臺用戶界面開發框架,Weex 的 JavaScript 框架運行時用的就是 Vue。這意味着在 Weex 的幫助下,你使用 Vue 語法開發的組件不只僅能夠運行在瀏覽器端,還能被用於開發 iOS 和 Android 上的原生應用。固然在如今,Weex 還在積極發展,成熟度也不能和 ReactNative 相抗衡。
Vue.js在模板中提供了指令,過濾器等,能夠很是方便,快捷地操做DOM。
axios 是目前最經常使用的 http 請求庫,能夠用於瀏覽器和 node.js
Axios 的主要特性包括:
基於 Promise
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 這幾種,比較經常使用的有如下幾種:
同源策略:同源策略(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>
複製代碼
後端添加白名單
爲何要有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) :我發給你一張加密的身份證,之後你只要出示這張卡片,我就知道你必定是本身人。
在條件判斷時,除了 undefined
, null
, false
, NaN
, ''
, 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
複製代碼
關於[]
和{}
須要注意的點:
[]
和空對象{}
都是object
類型,所以直接用於if
判斷條件時就會被轉化爲 true
。Number
。[]
與布爾值false
比較,false
轉化爲0
,而空數組[]
也轉化爲0
,所以[] == false
的判斷獲得true
。{}
與布爾值false
比較,false
轉化爲0
,而空對象{}
轉化爲NaN
,因爲NaN
與任何數都不相等,所以{} == false
的判斷獲得false
。[] == [] //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
複製代碼
可是該方法也是有侷限性的:
$.extend([deep],target,Object1,Object2)
deep
爲布爾值,默認不傳爲false(若是傳值只能傳true),爲true時表示深拷貝
或者藉助Ramda
函數庫,Ramda.clone()
Promise 是異步編程的一種解決方案,用於抽象異步處理對象。能夠避免回調地獄。
Promise 對象有如下兩個特色:
對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)
、fulfilled(已成功)
和 rejected(已失敗)
。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。
一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從 pending
變爲 fulfilled
和從 pending
變爲 rejected
。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved
(已定型)。若是改變已經發生了,你再對 Promise
對象添加回調函數,也會當即獲得這個結果。
有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操做更加容易。
使用注意點:
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);
});
}
複製代碼
多個await
命令後面的異步操做,若是不存在繼發關係,最好讓它們同時觸發。
let foo = await getFoo();
let bar = await getBar();
複製代碼
上面代碼中,getFoo
和getBar
是兩個獨立的異步操做(即互不依賴),被寫成繼發關係。這樣比較耗時,由於只有getFoo
完成之後,纔會執行getBar
,徹底可讓它們同時觸發。
// 寫法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 寫法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
複製代碼
上面兩種寫法,getFoo
和 getBar
都是同時觸發,這樣就會縮短程序的執行時間。
await
命令只能用在async
函數之中,若是用在普通函數,就會報錯。主線程、執行棧、任務隊列:
event loop是一個執行模型,在不一樣的地方有不一樣的實現。
js是單線程的
js進程分爲宏任務與微任務,優先執行微任務,後執行宏任務(其實不許確,頁面加載先執行一個<script>
標籤,屬於宏任務),若微任務執行過程當中產生了新的微任務,則將改新產生的微任務置於隊列底部(例如隊列爲A -> B -> C,執行A過程當中產生了微任務A1,則將A1置於C後面A -> B -> C -> A1)
常見的微任務:promise
,process.nextTick
(Node獨有,以及其餘不太常見的本次不討論)
常見的宏任務:setTimeout
,setInterval
,setImmediate
(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>)
}
複製代碼
在介紹緩存的時候,咱們習慣將緩存分爲 強緩存 和 協商緩存 兩種。二者的主要區別是使用本地緩存的時候,是否須要向服務器驗證本地緩存是否依舊有效。
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('');
}
複製代碼
函數節流
是指 必定時間內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
要點以下:
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
DOM
和 CSSOM
整合造成 RenderTree
RenderTree
開始渲染和展現<script>
時,會執行並阻塞渲染當元素的樣式發生變化時,瀏覽器須要觸發更新,從新繪製元素。這個過程當中,有兩種類型的操做,即重繪與迴流。
重繪(repaint): 當元素樣式的改變不影響佈局時,瀏覽器將使用重繪對元素進行更新,此時因爲只須要UI層面的從新像素繪製,所以 損耗較少
迴流(reflow): 當元素的尺寸、結構或觸發某些屬性時,瀏覽器會從新渲染頁面,稱爲迴流。此時,瀏覽器須要從新通過計算,計算後還須要從新頁面佈局,所以是較重的操做。會觸發迴流的操做:
頁面初次渲染
瀏覽器窗口大小改變
元素尺寸、位置、內容發生改變
元素字體大小變化
添加或者刪除可見的 dom 元素
激活 CSS 僞類(例如::hover)
查詢某些屬性或調用某些方法:
迴流一定觸發重繪,重繪不必定觸發迴流。重繪的開銷較小,迴流的代價較高。
題目:前端常見的安全問題有哪些?
Web 前端的安全問題,能回答出下文的兩個問題,這個題目就能基本過關了。開始以前,先說一個最簡單的攻擊方式 —— SQL 注入。
上學的時候就知道有一個「SQL注入」的攻擊方式。例如作一個系統的登陸界面,輸入用戶名和密碼,提交以後,後端直接拿到數據就拼接 SQL 語句去查詢數據庫。若是在輸入時進行了惡意的 SQL 拼裝,那麼最後生成的 SQL 就會有問題。可是如今稍微大型一點的系統,都不會這麼作,從提交登陸信息到最後拿到受權,要通過層層的驗證。所以,SQL 注入都只出如今比較低端小型的系統上。
這是前端最多見的攻擊方式,不少大型網站(如 Facebook)都被 XSS 攻擊過。
舉一個例子,我在一個博客網站正常發表一篇文章,輸入漢字、英文和圖片,徹底沒有問題。可是若是我寫的是惡意的 JS 腳本,例如獲取到document.cookie而後傳輸到本身的服務器上,那我這篇博客的每一次瀏覽都會執行這個腳本,都會把訪客 cookie 中的信息偷偷傳遞到個人服務器上來。
其實原理上就是黑客經過某種方式(發佈文章、發佈評論等)將一段特定的 JS 代碼隱蔽地輸入進去。而後別人再看這篇文章或者評論時,以前注入的這段 JS 代碼就執行了。JS 代碼一旦執行,那可就不受控制了,由於它跟網頁原有的 JS 有一樣的權限,例如能夠獲取 server 端數據、能夠獲取 cookie 等。因而,攻擊就這樣發生了。
XSS的危害
XSS 的危害至關大,若是頁面能夠隨意執行別人不安全的 JS 代碼,輕則會讓頁面錯亂、功能缺失,重則會形成用戶的信息泄露。
好比早些年社交網站常常爆出 XSS 蠕蟲,經過發佈的文章內插入 JS,用戶訪問了感染不安全 JS 注入的文章,會自動從新發布新的文章,這樣的文章會經過推薦系統進入到每一個用戶的文章列表面前,很快就會形成大規模的感染。
還有利用獲取 cookie 的方式,將 cookie 傳入入侵者的服務器上,入侵者就能夠模擬 cookie 登陸網站,對用戶的信息進行篡改。
XSS的預防
那麼如何預防 XSS 攻擊呢?—— 最根本的方式,就是對用戶輸入的內容進行驗證和替換,須要替換的字符有:
& 替換爲:&
< 替換爲:<
> 替換爲:>
」 替換爲:"
‘ 替換爲:'
/ 替換爲:/
複製代碼
替換了這些字符以後,黑客輸入的攻擊代碼就會失效,XSS 攻擊將不會輕易發生。
除此以外,還能夠經過對 cookie 進行較強的控制,好比對敏感的 cookie 增長http-only限制,讓 JS 獲取不到 cookie 的內容。
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 原理示意圖:
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.com
的 cookie
,這是發生 CSRF 攻擊的理論基礎。
預防 CSRF 就是加入各個層級的權限驗證,例如如今的購物網站,只要涉及現金交易,確定要輸入密碼或者指紋才行。除此以外,敏感的接口使用POST請求而不是GET 也是很重要的。
「構建」也可理解爲「編譯」,就是將開發環境的代碼轉換成運行環境代碼的過程。開發環境的代碼是爲了更好地閱讀,而運行環境的代碼是爲了更快地執行,二者目的不同,所以代碼形式也不同。例如,開發環境寫的 JS 代碼,要經過混淆壓縮以後才能放在線上運行,由於這樣代碼體積更小,並且對代碼執行不會有任何影響。總結一下須要構建工具處理的幾種狀況:
本質上來講,兩個名詞都是 CPU工做時間片的一個描述。
進程 描述了 CPU 在運行指令及加載和保存上下文所需的時間,放在應用上來講就表明了一個程序。線程 是進程中的更小單位,描述了執行一段指令所需的時間。
把這些概念拿到瀏覽器中來講,當你打開一個 Tab 頁時,其實就是建立了一個進程,一個進程中能夠有多個線程,好比渲染線程、JS 引擎線程、HTTP 請求線程等等。當你發起一個請求時,其實就是建立了一個線程,當請求結束後,該線程可能就會被銷燬。
若是在 JS 執行的時候 UI 線程還在工做,就可能致使不能安全的渲染 UI。這其實也是一個單線程的好處,得益於 JS 是單線程運行的,能夠達到節省內存,節約上下文切換時間,沒有鎖的問題的好處。
能夠把執行棧認爲是一個 存儲函數調用的棧結構 ,遵循先進後出的原則。
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 === 構造函數
複製代碼
關係圖以下:
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 Pattern)是程序設計中的一種設計模式。
在MDN上對於 Proxy 的解釋是:
Proxy 對象用於定義基本操做的自定義行爲(如屬性查找,賦值,枚舉,函數調用等)。
簡單來講: Proxy 對象就是可讓你去對JavaScript中的一切合法對象的基本操做進行自定義。而後用你自定義的操做去覆蓋其對象的基本操做。也就是當一個對象去執行一個基本操做時,其執行的過程和結果是你自定義的,而不是對象的。
Proxy的做用 :
對於代理模式 Proxy 的做用主要體如今三個方面:
攔截和監視外部對對象的訪問
下降函數或類的複雜度
在複雜操做前對操做進行校驗或對所需資源進行管理
前端路由原理?兩種實現方式有什麼區別?
前端路由實現起來其實很簡單,本質就是 監聽 URL 的變化,而後匹配路由規則,顯示相應的頁面,而且無須刷新頁面 。目前前端使用的路由就只有兩種實現方式:
Babel 是一個 JavaScript 編譯器
Babel 是一個工具鏈,主要用於在舊的瀏覽器或環境中將 ECMAScript 2015+ 代碼轉換爲向後兼容版本的 JavaScript 代碼:
特性 :
組件狀態由state控制。假設咱們如今有一個表單,表單中有一個input標籤,input的value值必須是咱們設置在constructor構造函數的state中的值,而後,經過onChange觸發事件來改變state中保存的value值,這樣造成一個循環的迴路影響。也能夠說是React負責渲染表單的組件仍然控制用戶後續輸入時所發生的變化。
組件狀態不禁
state
控制。其值能夠經過refs
獲取。常見的有input(不添加value
、defaultvalue
、onChange()
等)
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內核 |
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
版本中被刪除)盒模型包括
margin
、border
、padding
、content
區別:
content-box
計算時content
不包含 border
與 padding
,而 border-box
的content
則包含 border
與 padding
。
示例:
.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
;
塊格式化上下文(Block Formatting Context,BFC) 是Web頁面的可視化CSS渲染的一部分,是塊盒子的佈局過程發生的區域,也是浮動元素與其餘元素交互的區域。
常見建立 BFC
方法:
做用:
!important
> 行內樣式 > #id
> .class
> tag
> *
> 繼承 > 默認
:after
/ <br>
/ clear: both