前端面經答案解析

20190821電話面試

1. react的setState後發生了什麼

  • 解析: setState處於equeueSetState()中,首先將particalState放入_pendingStateQueue中,調用enquequeUpdate()判斷是否爲isBratchingUpdates批量更新策略,若是是批量更新策略,則調用dirtyComponent()將組件放入dirtyComponent,若是不是批量更新策略,則直接調用bratchUpdate即react中的默認批量更新策略,此時會進入ReactDefaultBatchStrategy.batchedUpdates,(某個初始isBratchingUpdatesfalse),調用事務流(含wrapper,inital,close,調用過程爲inital,perform,close),transaction.perform(enQueueUpdate執行函數),此時將修改isBatchingUpdates爲true,再次回調時將進入dirtyComponent,事務流結束時將修改isBatchingUpates: false,同時執行flushBatchedUpdates將遍歷dirtyComponent內的組件,根據調用的前後順序執行updateComponent,進而更新propsstateupdateComponent中有一代碼Object.assign(nextState, typeof partical === 'function' ? partical.call(inst, nextState, props, context): partical)此代碼可解決屢次執行this.setState修改某個數值時值不如預期的狀況。不如預期的緣由: this.setState()已經在bratchedUpdates更新事務流中,故此時isBatchingUpdatestrue將會進入dirtyComponent,但若是爲timeout(callback,timeInt)將會不在batchedUpdates的事務流中,此時isBatchingUpdatesfalse將會進入transaction的事務流而後執行dirtyComponent,最後執行事務流流中的close函數,達到更新界面的效果(isBatchingUpdates: false, flushBratchUpdate); 結論: this.setState將會進入一個待更新的隊列,並不保證同步更新,僅經過回調函數可拿到setState修改後的值,setState一般會集齊一些組件狀態後更新組件,保證性能,代碼內涵即react的批量更新策略。
  • 參考地址:setState批量更新策略
  • 渲染界面
  • 圖片詳解
    生成realdom
    首次渲染
    state渲染界面詳解圖
  • 番外:經過babel將jsx生成VDom元素,即React.createElement()含type(標籤元素),attributes(屬性),children(標籤子節點);會在render函數被調用的時候執行,render調用返回一個element。格式以下
{
  type: 'div',
    props: {
      className: 'cn',
        children: [
          {
            type: function Header,
            props: {
                children: 'Hello, This is React'
            }
          },
          {
            type: 'div',
            props: {
                children: 'start to learn right now!'
            }
          },
          'Right Reserve'
      ]
  }
}
複製代碼
  • 首次渲染的時候,先去按照規則初始化element,接着ReactComponentComponentWrapper經過遞歸,最終調用ReactDOMComponent的mountComponent方法來幫助生成真實DOM節點。
  • 結論:從jsx到渲染成真實realdom;JSX代碼通過babel編譯以後變成React.createElement的表達式,這個表達式在render函數被調用的時候執行生成一個element。
  • updateComponent涉及三個生命週期函數(更新渲染):shouldComponentUpdate componentWillUpdate componentDidUpdate 1.計算nextState => shouldComponentUpdate 2.render獲得nextElement元素 => componentWillUpdate 3.preElement於nextElement進行diff算法更新界面 => componentdidUpdate

2. 拋開react的diff算法,怎麼實現dom對比

  • diff算法:
  • 基於假設: 1. 相同的組件組件具備相同的dom結構,不一樣的組件具備不一樣的dom結構
  1. 對於同一層次的一組子節點,它們能夠經過惟一的id區分 diff算法考慮三種狀況:
  • 節點類型不一樣
  • 節點類型相同 => dom元素直接比較須要改變的屬性 組件: 根據新節點的props去更新原來根節點的組件實例,觸發一個更新的過程,最後在對全部的child節點進行diff遞歸比較更新。
  • 子節點比較 => 無key按順序比較,有key根據惟一的id進行比較
  • tree diff component diff element diff
  • 結論:保持dom結構的穩定性,map時加上key
  • dfs深度優先遍歷算法,給每一個節點賦予惟一的key值,比較新舊兩顆dom樹中節點的type,props有何變化,根據惟一的key值在pathchs中記錄相應的節點變化,若是有子節點,遞歸深度優先遍歷算法,一樣記錄diapcth節點差別,最終經過diff算法獲得了新舊兩顆dom樹中的差別,在經過已生成的element對象,即vdom經過深度優先遍歷算法應用這種差別,最後將vdom渲染成真實的dom結構(mountComponent);

3. 實現頁面多ajax請求完成後渲染頁面 併發請求

  • 將異步請求封裝成promise,ajax回調中resolve()獲得異步請求後的數據,Promise.all([])發異步請求時經過async和await同步的方式寫異步代碼,同時保證了多個異步請求執行結束後渲染界面。
  • 或者將異步請求封裝爲promise直接發出兩個promise請求,await等待兩個promise的值都存在開始下一步。 或者generator函數等待兩個promise值,在第一個next()函數拿到promise值在then函數內調用next()拿到下一個異步請求的值,一樣實現併發操做拿到請求值。

4. set去重原理?(待全面分析)

  • 內部經過Map實現的,相同的key時會直接覆蓋掉故不會出現相同的Key

5. const

  • 常量不可修改,初始時必須賦值,造成塊做用域,不會成爲全局變量的屬性。

6. 移動端適配

  • 解決方案: 媒體查詢,media srcreen(device-width)、viewport、rem、flex彈性佈局
  • 媒體查詢根據查詢的屏幕寬度編寫不一樣的css樣式,調整頁面寬度時不用刷新頁面便可實現響應式佈局,各類設備維護一套代碼,缺點加載更多的css資源,圖片資源。
  • viewport視口 經過設置device-width 和initial-size實現css物理像素更據不一樣屏幕匹配真正的物理像素,設備像素比(dpr) = 物理像素/設備獨立像素,設置初始化的大小爲1/dpr即實現css中的px和移動端的px相同
  • rem即根據根元素的fontSize實現響應佈局,調整全部的字體的大小
  • 3種適配方式
1. media查詢,同上
2. rem適配,相對於根元素的大小,在理想視口的狀況下(width = device-width)即css像素隨着不一樣的佈局視口做出等比改變,可是css像素和物理像素不變。優勢:利用了理想視口 缺點:須要計算,出現小數點,字符顯示不完整
<script>
        let ele = document.createElement('style');
        let size = document.documentElement.clientWidth * 16 / 375; //佈局視口
        ele.innerHTML = 'html{font-size:'+ size +' px !important}';
        document.head.appendChild(ele);
</script>
<script>
            //rem適配
            let styleElem = document.createElement('style');
            let size = (document.documentElement.clientWidth * 16 * dpr) / 375;
            styleElem.innerHTML = 'html{font-size:' + size + 'px!important}';
            document.appendChild(styleElem);
            let metaElem = document.querySelector('meta[name=viewport]');
            let dpr = window.devicePixelRatio || 1; //像素比
            metaElem.conent =
                'initial-scale=' + 1 / dpr + ',maximun=' + 1 / dpr + ',minimun=' + 1 / dpr + 'user-scalable=no';
</script>

3. viewport 視口: 佈局視口(document.documentElement.clientWidth) 可視視口(document.innerWidth) 理想視口(screen.width)
經過1/dpr實現等比縮放,改變了佈局視口的大小,實現1px css像素對應1px 物理像素,同時保證rem的css知足佈局,font-size須要*dpr(縮小布局視口增大,css像素應實現等比改變)
 <script>
            //rem適配
            let styleElem = document.createElement('style');
            let size = (document.documentElement.clientWidth * 16 * dpr) / 375;
            styleElem.innerHTML = 'html{font-size:' + size + 'px!important}';
            document.appendChild(styleElem);
            let metaElem = document.querySelector('meta[name=viewport]');
            let dpr = window.devicePixelRatio || 1; //像素比
            metaElem.conent =
                'initial-scale=' + 1 / dpr + ',maximun=' + 1 / dpr + ',minimun=' + 1 / dpr + 'user-scalable=no';
        </script>
 /* 媒體查詢 */
            @media only screen and (device-pixel-ratio: 2) {
                #test {
                    font-size: 12 * 2 + 'px';
                    transform: scaleY(0.5);
                }
            }
4. 不一樣設備下佈局視口一致,css大小不變,但css像素對應物理設備像素改變
<script>
            let metaElem = document.createElement('meta');
            let scale = documnet.createElement.clientWidth / 750;
            metaElem.name = 'viewport';
            metaElme.content = `initial-scale=${scale}, maximun=${scale}, minimun=${scale}, user-scalable=no`;
            document.head.appendChild(metaElem);
        </script>
複製代碼
  • 理解概念: 物理像素 css像素 設備獨立像素(width=device-width)時 css像素和物理像素關聯起來 像素比 佈局視口 可視視口 理想視口 分辨率

7. css動畫

  • animation: animation-delay, animation-duration, animation-timing,animation-keyframes from to 或者百分比實現過渡效果,animation-direction,animation-iterator-count;
  • transition: 過渡效果,property,transition-delay,transition-duration,transition-timing-function(速度 => linear、easein(加速)、easeout(減速)、cubic-bezier(自定義速度模式))transition對於display屬性添加過渡效果無效應使用visibility
<style>
        /* transition解決display問題 */
        div>ul{
            visibility: hidden;
            /* display: none; */
            /* height: 0px; */
            opacity: 0;
            overflow: hidden;
            transition: visibility 0s, opacity 0.5s linear;
        }
        div:hover>ul{
            visibility: visible;
            /* display: block; */
            opacity: 1;
            /* height: auto; */
        }
    </style>
</head>
<body>
    <div>
        <ul>
            <li>fd</li>
            <li>fd</li>
            <li>fd</li>
            <li>fd</li>
        </ul>
    </div> 
</body>
複製代碼

8. 頁面白屏如何處理,想到的場景以及處理辦法

  • 白屏緣由:初始化 webview -> 請求頁面 -> 下載數據 -> 解析HTML -> 請求 js/css 資源 -> dom 渲染 -> 解析 JS 執行 -> JS 請求數據 -> 解析渲染 -> 下載渲染圖片
  • 解決辦法:
  1. 下降請求量:合併資源,減小 HTTP 請求數,minify / gzip 壓縮,webP,lazyLoad。javascript

  2. 加快請求速度:預解析DNS,減小域名數,並行加載,CDN 分發。css

  3. 緩存:HTTP 協議緩存請求,離線緩存 manifest,離線數據緩存localStorage。html

  4. 渲染:JS/CSS優化,加載順序,服務端渲染,pipeline。前端

  5. 前端後端渲染的區別 答案相似ajax的優缺點 前端渲染:優勢:1. 操做js實現界面數據改變,局部刷新界面,無需每次完整加載頁面java

  6. 懶加載,只需加載可視區的數據node

  7. 利用js實現一些酷炫效果,一些操做能夠在前端作,減輕服務器的壓力,整個網絡的數據量小react

  8. 實現了先後端分離,經過後端提供的接口獲取數據,渲染界面 缺點:1.沒法後退獲取上一次的狀態 2.需加載過多的js css資源致使首頁性能差3.暴露更多的數據交互邏輯,存在安全問題 後端渲染:1. 首頁性能好,直接顯示一個完整的頁面,發回一整個html頁面 2.勿需加載css js資源jquery

9. 說一些經常使用array的api

  • map(),splice(start,[deletecount],[insertValue]),slice(),find(),findIndex(),filter(),push(),pop(),shift(),unshift(),indexOf(),concat(),join(),reduce(),includes(),keys(),forEach(),reverse(),sort(),lastIndexOf(),fill(value, start, end) => 出現的最後一個索引,from()
  • substr(index, [len])可爲負index; subString(start,end) 不爲負index

10.數組並集

  • a集合包含b集合中的元素,具備互異性,即不重複性。
  • 經過includes方法,a.concat(b.filter(value) => !a.includes(value));
  • 經過Aarry.from()方法實現類數組的轉化,new Array.from(new Set(a.concat(b));
  • 經過filter, a.concat(b.filter(value)=> a.indexOf(value) === -1)

11. 數組右移x位

  • 經典的三次翻轉,先翻轉前0-x,再翻轉x-n-1,最後在總體翻轉0-n-1實現數組循環右移x位
//經典的三次反轉方法:實現數組循環右移k位
void Reverse(int* array, int p, int q)
{
	for (; p<q; p++, q--)
	{
		int temp = array[q];
		array[q] = array[p];
		array[p] = temp;
	}
}
 
void RightShift(int* array,int n, int k)
{
	k %= n;
	Reverse(array, 0, n - k - 1);
	Reverse(array, n - k, n - 1);
	Reverse(array, 0, n - 1);
}
複製代碼

12.給一個數組,給一個數x,找數組內想加等於x的數的索引

  • leetcode題直接上最優解
var sumNum = (arr,sum) => {
    for(var i = 0; i < arr.length; i++){
        let target = sum - arr[i];
        if(arr.indexOf(target) !== -1){
            if(i !== arr.indexOf(target)){
                return [i,arr.indexOf(target)]
            }
        }
    }
    return [];
   //map解法
   var map = new Map();
    for(var i = 0; i < arr.length; i++){
        let target = sum - arr[i];
        if(map.get(target) && map.get(target) != i){
            return [i, map.get(target) ]
        }else{
            map.set(arr[i], i);  
        }
    }
}
複製代碼

20190819電話面試

1.display屬性有哪些?

  • block,flex,none,inline,inline-block,table,grid,inline-grid
  • grid: grid-template-rows grid-template-columns grid-gap grid-template-areas grid-area grid-columns-start grid-columns-end justify-items:stretch align-item:stretch justify-self(本身的對齊方式)

2. position屬性有哪些?每個屬性的做用,使用?

  • fixed,relative,position,static

3. css中引入樣式有哪幾種方法,不考慮預處理和正常的三種方法,有沒有其餘的方法?

  • 行內式,內嵌式,(),連接式,導入式@import文件路徑
  • 優先級 !important > 行內式 > id > 類+僞類+屬性 > 標籤+僞元素 > 通配符 class 組經過就近原則 具體優先級經過計算得知 內聯1000 id 100 class 10 標籤 1 ;animation執行0s也能超過id選擇器
  • 僞類:用單冒號實現特定的效果如:active :focus :hover 僞元素用雙冒號::添加某個元素而實現相應的效果::after ::before

4. css單位,詳細說一下每一種的使用?

  • px: css像素 當width=device-width時,css像素和物理像素的關係等於dpr = 物理像素/設備獨立像素
  • em: 相對長度單位,相對當前行內字體大小
  • rem: 相對根元素的字體大小
  • vh: 視高:可視區域寬度或者高度,innerWidth/innerHeight
  • vw: 視寬
  • vmin vmax : vh或vw中的最小值或者最大值

5.React中的context原理? 一級Context API

  • 跨層級的組件通訊,相似共享一全局變量,採用生產者消費者模式,聲明context對象,申請訪問context對象的屬性;案例有react-redux react-router
  • 使用了Context破壞了組件間的依賴性,不利用組件複用和可維護性
  • 則組件間的通訊方式有:props context createRef() => this.createIns.current.property
  • context經過靜態屬性childContextTypes聲明一個context對象屬性,經過getChildContext返回context對象,有狀態組件使用context須要經過靜態contextTypes屬性聲明要獲取的context對象屬性,經過this.context獲取屬性值,無狀態組件經過靜態contextProps聲明context屬性,經過context得到屬性值;代碼以下
import React from 'react'
import PropTypes from 'prop-types'
class context extends React.component{
    //聲明context對象屬性
    static childContextProps = {
        backgroundcolor: PropTypes.string,
        color: PropTypes.func
    }
    //返回context對象
    getChildContext(){
        return{
            backgroundcolor: 'red',
            color: 'blue'
        }
    }
    render(){
        
    }
}
//有狀態組件使用context
class StateComponent extends React.Component{
    static contextTypes = {
        backgroundcolor: 'red'
    }
    render(){
        let {backgoundcolor, color} = this.context; //僅能訪問到backgroundcolor屬性   
    }
}
//無狀態組件使用context
function NoState(props, context){
    return context.color
}
NoState.contextProps = {
    color: PropTypes.func   
}
複製代碼
  • Context的API
let ContextComponent = React.createContext({
    backgroundcolor: 'red',
    color: 'blue'
});
//生產者
<ContextComponent.Provider value={backgroundcolor: 'red', color: 'blue'}>
</ContextComponent.Provider>
//消費者 經過Consumer 內部爲一函數得到context對象值
<ContextComponent.Consumer>
    (context) => {
        console.log(context.color);
    }
</ContextComponent.Consumer>
複製代碼

6.瀏覽器機制(DOM事件流,一開始答成了event loop後來面試官就讓我講一下宏微任務)

  • 瀏覽器機制主要是瀏覽器渲染機制,瀏覽器的功能主要是向服務器發起請求,展現請求的資源,能夠是html,pdf,圖片資源;整個瀏覽器能夠分爲如下幾個部分,用戶界面、瀏覽器引擎、內核(渲染引擎)、js引擎、網絡、用戶界面後端、數據存儲。

  • 內核即呈現引擎流程圖: 將請求的資源變爲dom樹 => 渲染dom樹 => 佈局dom樹 => 繪出dom樹;整個渲染過程js引擎執行完纔會執行渲染過程,這是因爲瀏覽器的處理用戶交互,若是是多線程,會致使更多複雜的同步問題。同時爲了更快的顯示到用戶界面同時解析成dom樹並渲染非先將dom樹生成完成在渲染。
  • 瀏覽器性能優化考慮點:js執行時間過長,操做dom都回致使頁面卡頓
  1. 減小js加載對dom渲染的影響,將js代碼加載邏輯放在html文件尾部,減小渲染引擎呈現工做的影響
  2. 避免重排,減小重繪(避免白屏,或者交互過程當中的卡頓)
  3. 減小dom的層級,減小渲染引擎工做過程當中的計算量
  4. 使用setTimeout() setInterval()來執行動畫視覺變化,致使丟失幀,致使卡頓。
  • 阻塞加載:
  1. css不阻塞dom解析,阻塞dom渲染,cssom和dom解析完成後纔開始渲染,link都是並行下載的,link後是script的話會阻塞js的執行,不會阻塞js的加載。由於js可能對html元素,css樣式做出修改,故會阻塞js的執行。所以此時將script放在link標籤前面。
  2. js阻塞dom解析,script標籤下載和執行完纔開始dom解析,可經過async defer改善加載和執行狀況。defer並行下載,DOMContentLoaded即dom接下完成前執行,執行順序和defer順序一致,async異步也是並行下載不阻塞dom解析,但解析完成後則執行,執行過程阻塞dom解析,defer和async只對外聯有效。
  3. link放頭部,script放body尾部優化性能。
  • 瀏覽器是多線程的 瀏覽器事件觸發線程 定時觸發器線程 異步http請求線程 ui線程 js線程
  • js引擎:js是腳本語言,運行則執行輸出結果,瀏覽器會做出優化先編譯後執行達到性能提高。js能操做dom節點,故Js加載完後纔會啓動瀏覽器渲染過程。Event Loop js是單線程的,全部同步任務在主線程上執行,造成一個執行 棧,當有異步事件的時候,該異步事件會掛起,同時告訴瀏覽器有這麼一件事,主線程繼續執行,當瀏覽器感知異步事件能夠執行時,會將異步事件回調函數放入事件隊列,當主線程中的執行棧執行完畢,就會讀取事件隊列依次執行,是一個循環的過程,有事件就執行,不然就不斷循環檢查,這個過程當中涉及到宏/微任務, 這裏涉及到宏/微任務隊列,每次執行棧完後會執行全部的微任務隊列中的任務,在執行下一個宏任務。即宏任務排隊執行,微任務插隊執行。不斷重複以上過程

  • 宏任務和微任務:都爲異步任務,但有不一樣的執行順序 -先執行微任務隊列在執行一個宏任務

  1. 宏任務:總體代碼script,setTimeout(), setInterval(),setImmediate(),ajax,事件回調(onclick())
  2. 微任務:原生Promise(有些實現的promise將then方法放入宏任務中,大部分是微任務,catch,finally),process.nextTick(node)
console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
打印順序:1 7 6 8 2 4 3 5 9 11  10 12
注意事件循環一個事件執行完成才執行下一個任務
複製代碼
  • js引擎結論:則是分析執行棧經過宏微任務進行執行。先宏後微任務
  • node.js 事件循環 不一樣於瀏覽器事件循環

  • node.js由chorme v8引擎進行解析,其本身實現了I/O操做的libuv,事件循環是根據不一樣的階段來執行,每一個階段維持相應的隊列,帶隊列爲空在進行下一個階段。
  • timer:執行定時器相關的回調 pending callback:執行一些系統回調,網絡錯誤等 idle callback:node內部使用 poll輪詢: 執行I/O的回調 check: 執行setImmediate回調 close: 執行socket.close回調
  • poll: I/O操做當該階段事件執行完後,檢查有沒有setImmediate()若有去check階段執行,若沒有則一直阻塞,同時檢測有無timer執行,如有timer存在則開始執行下一次事件循環。process.nextTick()是微任務,微任務在每一個階段執行完後再執行,故process.nextTick()可能形成starving 餓死,setImmediate()則不會。
  • node 環境中事件循環案例
setTimeout(()=>{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)

setTimeout(()=>{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)
瀏覽器:timer1  promise1 timer2 promise2
Node:timer1 timer2 promise1 promise2
複製代碼

const fs = require('fs')
const starttime = Date.now()
let endtime

fs.readFile('text.txt', () => {
  endtime = Date.now()
  console.log('finish reading time: ', endtime - starttime)
})

let index = 0

function handler () {
  if (index++ >= 1000) return
  console.log(`nextTick ${index}`)
  process.nextTick(handler)
  // console.log(`setImmediate ${index}`)
  // setImmediate(handler)
}

handler()
複製代碼

7.高級瀏覽器內核

  • webkit chorme safari
  • trident IE
  • Gecko mozllia firfox

8.理解dom事件流的三個階段

  • 事件流就是從頁面中接收事件的順序
    三個階段
  • 事件捕獲階段:父向子傳遞事件
  • 目標事件階段:更據註冊的捕獲/冒泡的前後順序執行
  • 事件冒泡階段:子向父傳遞事件 event.stopPragation()阻止冒泡,addEventListener true事件捕獲,false事件冒泡,attachEvent(event,callback)。
  • dom事件流:則是先事件捕獲-> 目標事件->執行事件冒泡
  • 事件委託:基於事件冒泡的思想將子元素事件委託給父元素事件
  • 阻止冒泡:js: event.stopPrapagtion()
  • 阻止默認行爲:默認行爲即event.cancelable:true,如a submit會自動跳轉自動提交,js: event.preventDefault() IE: return false jquery:return false阻止了事件默認行爲也阻止了事件冒泡,IE中的event在全局變量window上,而firfox僅爲臨時變量,在時間觸發時產生
  • event.target: 事件觸發者 event.currentTarget:事件監聽者 eventCurrentTarget === this即this始終和事件監聽者相等。
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<!--事件委託-->
var ulist = document.getElementsByTagName('ul');
ullist.onclick = function(e){
    if(e.target.nodeName.toLowerCase() === 'li'){
        console.log('li click!');
    }
}
複製代碼
  • 事件委託機制具備侷限性,如focus blur無冒泡機制,沒法實現事件委託
  • mousemove mouseout 有事件冒泡,可是隻能不斷經過位置去計算定位,對性能消耗高,所以也是不適合於事件委託的

9. ajax先後端通訊,除此之外另外一種通訊方式瞭解過麼?

  • 同源策略:協議 端口 域名/ip不一樣都會致使跨域,沒法進行資源交互,不能訪問cookie,localStorage,indexDB,不能操做dom節點,沒法發送ajax請求,是一種瀏覽器安全策略,隔離惡意文件
  • ajax 同源下的通訊方式 封裝ajax
var util = {};      
//獲取 Ajax 請求以後的 json     
util.json = function (options) {
    var opt = {
        url: '',
        type: 'get',
        data: {},
        success: function () {
        },
        error: function () {
        },
    };
    Object.assign(opt, options);
    //IE兼容性處理:瀏覽器特徵檢查。檢查該瀏覽器是否存在XMLHttpRequest這個api,沒有的話,就用IE的api             
    var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
    var data = opt.data,
    var type = opt.type.toUpperCase();
    var dataArr = [];
    if (opt.url) {   
        var url = opt.url;
    }
    for (var key in data) {
        dataArr.push(key + '=' + data[key]);
    } 
    if (type === 'GET') {
        url = url + '?' + dataArr.join('&');
        xhr.open(type, url.replace(/\?$/g, ''), true);
        xhr.send();
    }
    if (type === 'POST') {
        xhr.open(type, url, true);
        // 若是想要使用post提交數據,須要明確設置Request Header    
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhr.send(dataArr.join('&'));
    }
    xhr.onreadystatechange = function () {
        if (xhr.status === 200 || xhr.status === 304) {
            //304表示:用緩存便可。206表示獲取媒體資源的前面一部分                
            var res;
            if (opt.success && opt.success instanceof Function) {
                res = xhr.responseText;
                if (typeof res === 'string') {
                    //將字符串轉成json
                    res = JSON.parse(res);                           
                    opt.success.call(xhr, res);
                }
            }
        } else {
            if (opt.error && opt.error instanceof Function) {
                opt.error.call(xhr, res);
            }
        }
    };
}
注意兼容性,XMLHttpRequest兼容性,window.XMLHttpReques,不然 new ActiveXObject("Microsoft.XMLHTTP");解決異步請求對象兼容
post需設置請求頭 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
get請求url用?鏈接參數
複製代碼

優勢:不刷新頁面則更新數據;異步於服務器進行通訊(不打斷用戶操做,更迅速的相應能力);前端和後端負載均衡(將服務端的任務在客戶端處理,減輕服務器的帶寬負擔,節約空間和帶寬租用成本);數據和界面分離,先後端分離,提升開發效率。 缺點:破壞了back和History按鈕,沒法回到前一個頁面狀態,暴露了更多數據引起安全問題如(SQL注入攻擊和基於Credential安全漏洞);違背URL和資源定位的褚總,同一URL可獲得不一樣的內容 使用場景:基本的數據操做和數據過濾 不適用場景:大量的文本替換 導航和搜索ios

  • WebSorket 基於客戶端和服務端的雙向長鏈接通訊(可跨域) 特色:通常HTtp請求只能有客戶端主動發起,WebSocket技術,服務端可主動向客戶端推送信息,客戶端也能夠主動向服務器發送請求,屬於服務器推送技術的一種。經常使用方法:send(),onerror(),onmessage(),onclose(),close()
  • CORS 跨域資源共享 需在服務端和客戶端作嚮應配置,容許跨域的ajax請求
  • 後端:
  1. Access-Control-Allow-Origin: *|源,當容許跨域攜帶身份憑證時不能爲*
  2. Access-Control-Allow-Credentials:容許跨域攜帶身份憑證true,前端一樣須要設置xhr.withCredential=true 不然出錯
  3. Acess-Control-Allow-Max-Age: 非簡單請求(即POST,GET,HEAD)出外的預檢請求的有效期,有效期內統一請求不須要在進行發送預檢請求(Options)
  4. Acess-Control-Allow-Methods: 容許預檢請求的HTtp請求方法
  5. Acess-Control-Allow-Headers: 容許預檢請求的請求頭
  6. 預檢請求則是預先發出OPtions請求看可否進行跨域訪問,避免沒必要要的數據交互。
  • 前端:
  1. Origin:預檢請求或者實際請求的源
  2. Access-Control-Request-Method:實際請求方法
  3. Access-Control-Request-Headers:實際請求頭 HTTP頭部字段

簡單請求: Content-Type:類型 application/x-www-form-data text/plain multipart/form-data

  • 跨域解決方案:
  1. jsonp 利用Script標籤能夠跨域,傳遞一個callback函數,後端將值放入這個函數並返回,將執行這個callback函數進而拿到數據。只能處理get請求
  2. WebSocket 雙向長鏈接,解決跨域
  3. cors 跨域的ajax請求
  4. hash 經過監聽onhashchange進行數據交互
// B中的僞代碼
    window.onhashchange = function () {  //經過onhashchange方法監聽,url中的 hash 是否發生變化
        var data = window.location.hash;
    };
複製代碼
  1. postMessage()
// 在窗口B中監聽 message 事件
    Awindow.addEventListener('message', function (event) {   //這裏強調的是A窗口裏的window對象
        console.log(event.origin);  //獲取 :url。這裏指:http://A.com
        console.log(event.source);  //獲取:A window對象
        console.log(event.data);    //獲取傳過來的數據
    }, false);
複製代碼
  • jsonp模擬
//將對象轉化爲&鏈接後的字符串
function dataToUrl(data){
    let res = [];
    for(let key in data){
        res.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
    }
    return res.join('&');
}
function jsonp(url, data, success, errorCb, time){
    //callback參數
    let cb = 'cb' + Math.floor(Math.random() * 10);
    //設置callback = cb
    data.callback = cb;
    let flag = url.indexOf('?') > -1 ? '&' : '?';
    let script = document.createElement('script');
    script.setAttribute('src') = url + flag + dataToUrl(data);
    window[cb] = function(data){
        clearTimeout(script.timer);
        window[cb] = null;
        document.head.removeChild(script);
        success && success(data);
    }    
    if(time){
        script.timer = setTimeOut(function(){
            clearTimeout(script.timer);
            window[cb] = null;
            document.head.removeChild(script);
            errorCb && errorCb('超時訪問');
        },time);
    }
    document.head.appendChild(script);
}
//後端實現
onst http = require('http');
const { parse } = require('url');

// 假設這是在數據庫中找到的數據~
const data = {
  name: 'russ',
  age: 20,
  gender: 'male'
};

const server = http.createServer((req, res) => {
  const url = parse(req.url, true); // 解析 url

  // 只有路由爲 `/user` 的 GET 請求會被響應
  if (req.method === 'GET' && url.pathname === '/user') {
    const { callback } = url.query; // 獲取 callback 回調名稱
    
    if (callback) // 若是傳遞了 callback 參數,說明是 JSONP 請求
      return res.end(`${callback}(${JSON.stringify(data)})`);
    else // 沒傳遞 callback,直接當作不跨域的 GET 請求返回數據
      return res.end(JSON.stringify(data));
  }
  return res.end('Not Found'); // 不匹配的路由返回錯誤
});

server.listen(3000, () => {
  console.log('Server listening at port 3000...');
});
複製代碼
  • get 和 post的區別
  1. get參數放在url內,post參數放在請求報文body內
  2. get url因爲瀏覽器的限制,有長度限制,2k,post數據沒有長度限制
  3. get 數據url內可見,相對於post安全性較差,但http都是明文傳輸的,因此安全性都挺差的,https能保證安全性。
  4. get 請求可以被瀏覽器緩存,post請求沒法被瀏覽器自動緩存
  5. get 參數保存在瀏覽器歷史內,post參數不會保存在瀏覽器歷史
  6. get 只能ascii字符,post沒有限制,能夠是二進制數據
  7. get 和 post本質都是tcp連接,因爲http規定和瀏覽器/服務器限制,致使get和Post有必定的差別性。
  8. get只發送一次請求 返回200, post發送header 返回100 continue 後再次發送body返回200,即發送了2次請求,具體實現方式和瀏覽器相關。
  9. get 數據格式爲application/x-www-form-urlencoded post能夠爲 aplication/x-www-form-urlencoded multipart/form-data
  • post 和 put的區別
  1. post沒有冪等性, put有冪等性,即一次或者屢次請求,產生的效果是同樣,結果是一致,好比put上傳文件會對該文件進行更新覆蓋,結果無差別性。post提交相同數據時則會改變結果,建立資源。如每次請求金額增長100,post則能夠實現此效果。
  2. post主要是建立資源 put主要是更新資源
  • 封裝一個ajax
/**
 * 1.考慮兼容性 IE
 * 2.考慮請求方式
 * 3.掌握原生ajax的實現過程
 */
function ajax(optionsArg) {
    let options, xhr;
    options = {
        type: 'get',
        url: '',
        data: {},
        contentType: '',
        success: function() {},
        error: function() {}
    };
    Object.assign(options, optionsArg);
    //兼容IE
    xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(Microsoft.XMLHttp);
    let { url, type, data, success, error } = options;
    let dataArr = objectToUrl(data).join('&');
    if (type.toLowerCase() === 'get') {
        url = url.indexOf('?') === -1 ? url + '?' + dataArr : url + dataArr;
        xhr.open(type, url);
        xhr.send();
    }
    if (type.toLowerCase() === 'post') {
        xhr.open(type, url); //ture爲異步 false爲同步
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send(dataArr);
    }
    xhr.onreadystatechange = function() {
        if (xhr.readystate === 4 && ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) {
            //請求相應成功
            let res = JSON.parse(xhr.responseText); //轉化爲對象
            if (successs && typeof success == 'function') {
                success.call(undefined, res);
            }
        } else {
            if (error && typeof error === 'function') {
                error.call(undefined, xhr);
            }
        }
    };
}
//將對象轉化成等號相連的數據
function objectToUrl(data) {
    let res = [];
    for (let key in data) {
        res.push(key + '=' + data[key]);
    }
    return res;
}
複製代碼
  • 傳遞數據的方式主要有 application/x-www-form-urlencoded;multipart/form-data;raw;binary

10. Keep-Alive

  • 持久鏈接,經過content-length判斷數據是否接受完成,http1.1默認開啓connection: keep-alive transfer-encoding: chunked

11. 瀏覽器存儲以及各自的區別?如何用cookie實現session機制?

  • cookie localStorage sessionStorage session:服務端存儲

持久cookie 回話cookie cookie失效進行url重寫,將sessionId放入url中 cookie產生過程:1.生成cookie對象(給用戶建立一個惟一的cookie id 默認爲會話即cookie,設置MaxAge則爲持久性cookie 設置爲0則是讓瀏覽器刪除該cookie 將cookie放入http相應報頭,發送http相應報文) 2.設置存儲cookie 3.發送cookie 4.獲取cookie

  • cookie-parser: res.cookie() 屬性 max-age:(默認-1) expires(默認都是會話cookie,瀏覽器窗口關閉cookie失效) httpOnly(僅經過http傳輸獲取cookie) signed(簽名) domain(域名) path(訪問目錄,默認爲/當前根目錄) encoded
  • cookie簽名經過在cookieParser(sercret)給req設置.sercret對res.cookie(key,value,{signed: true})進行加密,後再req經過sercret解密cookie
  • cookie能夠跨二級名訪問(例外),cookie沒法跨域訪問

12. es6的新特性說一下

Array.prototype.fill(value,start,end) 返回新數組
Array.prototype.find((value,key,array)=>{},this)返回知足條件的第一個值不然爲undefined
Array.prototype.findIndex((value,key,array)=> {},this)返回知足第一個條件值的下標

13.箭頭函數

  1. 匿名函數 語法簡潔
  2. this指向不一樣於常規的幾種指向,是this中的例外,其this指向基於詞法做用域指向外層函數的this
  3. this指向優先級最高
  4. 不能使用new進行構造,沒有prototype屬性
  5. 沒有arguments參數
  6. 不能做爲generator函數,不能使用yield語法

14. var、let\const的區別

  • var 聲明提高 沒有塊級做用域
  • let 擁有塊級做用域 不會聲明提高 不能重複定義變量名
  • const 必須賦初值 擁有跨級做用域 沒法修改

15.async\await原理

/**
 * async await原理 特色:
 * 1. 同步的方式寫異步代碼,async非阻塞異步方式執行, 遇到await函數內部等待, async不阻塞
 * 2. 將自動動執行器和generator封裝在一個函數
 * 3. async 返回一個promise對象 await等待一個promise對象或者當即值等數據,且成對存在
 */
//自動執行器
function run(gen) {
    return new Promise(function(resolve, reject) {
        //生成一個iterator迭代器
        let it = gen();

        //next自動執行
        function next(fn) {
            let next;
            try {
                next = fn(); //獲得yield的結果
            } catch (err) {
                reject(err);
            }
            if (next.done) {
                resolve(next.value); // 執行完畢將返回值做爲resolve決議值
            }
            Promise.resolve(next.value).then(
                function(val) {
                    next(function() {
                        it.next(val);
                    }); // 決議後繼續執行
                },
                function(err) {
                    next(function() {
                        it.throw(err);
                    }); //拋出錯誤執行的時候進行捕獲
                }
            );
        }
        next(function() {
            return it.next(undefined);
        }); //首次執行無參數傳遞
    });
}
/**
 * thunk函數:
 * 1. 將回調的執行結果給回調函數,並返回回調函數的函數
 * 2. 將回調函數的全部權給了thunk函數
 * 3. 對函數的一次封裝 
 */
//例子
function thunk(fileName){
    return function(callback){
        return fs.readFile(fileName, callback); //執行結果給回調函數,返回這個回調函數的函數,回調全部權賦予thunk函數
    }
}
//yield等待的則是thunk函數和promise對象 方便自動自動執行 thunk函數的自動執行器
function run(gen){
    let it = gen();

    function next(val){
        let next = it.next(val);
        if(next.done){
            return next.value;
        }    
        next.value(next); //回調函數獲取值   
    }
    next(undefined); //傳遞數據
}
//promise的自動執行 這些都是簡化版 具體自動執行依據 async 函數 自動執行器+generator
function run(gen) {
    let it = gen();

    function next(val) {
        let res = it.next(val);
        (function handle(res) {
            if (res.done) {
                return res.value;
            }
            Promise.resolve(res.value).then(
                next,
                function(err) {
                    handle(it.throw(err)); //經過閉包的方式處理錯誤狀況 
                }
            );
        })(res);
    }
    next(undefined);
}
複製代碼

16.幾種繼承的區別,如何優化?

  • 原型鏈繼承:原型對象爲構造函數的實例 sub.prototype = new Super();缺點,引用類型的原型屬性被全部實例對象所共享 => 生成的原型對象綁定了相應的屬性,子類沒法向父類傳參
  • 構造函數繼承:Super.call(this,arguments) 方法和屬性在構造函數中定義,每次建立實例需建立一次方法,解決了引用類型被全部實例對象所共享,能夠傳遞參數,未利用原型繼承。
  • 組合繼承:原型式繼承和構造函數相結合,會兩次調用父類函數,會使子類原型對象上產生多餘的父類屬性,
  • 原型式繼承:Sub.prototype = Object.create(Super.prototype, {constructor: {value:"Sub"}) => Sub.prototype.proto = Super.prototype避免多餘屬性,沒有修改constructor屬性 ;等價於Object.setPrototypeOf(a, b); (a.proto = b)
  • 寄生組合式:即Object.create和構造函數相結合,可實現父類屬性和原型方法得繼承,且不產生多餘的屬性,此時須要修改子類的prototype.constructor屬性。
  • 靜態方法繼承:Ojbect.setPrototypeOf(Child, Parent),訪問靜態方法也從原型上去尋找。Object.setPrototypeOf原理和Object.create有相同之處,不過未改變constructor的值,由於爲直接修改Sub.prototype.proto = Super.prototype;
  • 代碼演示5中繼承方式
//父類
function Person(name, age, places) {
    this.name = name;
    this.age = age;
    this.places = places;
}
//原型方法 this指向的是實例對象 this = Parent.prototype 隱式綁定
Person.prototype.getName = function() {
    return this.name;
};
//靜態方法
Person.say = function() {
    return 'I am the super class!';
};
//子類
function Student(school, id) {
    this.school = school;
    this.id = id;
}
Student.prototype.getSchool = function() {
    return this.school;
};
//1. 原型鏈式繼承 全部實例共享原型上的引用屬性, 沒法傳遞參數
Student.prototype = new Person('wq', '45', ['cd', 'zy']);

let stu1 = new Student('sic', '12');
let stu2 = new Student('sic', '32');
stu1.places.push('none');
//全部實例共享引用類型的原型屬性
console.log(stu1.places, stu2.places);
console.log(stu1.name, stu2.name);
//2. 構造函數式繼承 能夠傳遞參數,每次建立實例都需執行方法,未實現原型繼承
function Jober(name, age) {
    Person.call(this, name, age);
}
let job1 = new Jober('jober', '22');
console.log(job1.name, job1.age);
//3. 組合式繼承  子類原型上有多餘的父類的屬性,父類函數執行了2次
function Farmer(name, age) {
    Person.call(this, name, age);
}
//實現原型方法繼承
Farmer.prototype = new Person();
let farmer1 = new Farmer('farmer', '89');
console.log(farmer1.name, farmer1.age, farmer1.getName());
//4. 寄生組合式繼承 Object.create + 構造式繼承/ sub.prototype.__proto__ = Super.prototype / Object.setPrototypeOf(sub.prototype, Super) / Object.setPrototypeOf(Sub, Super);
//Object.create原生實現
function objCreate(obj) {
    function F() {}
    F.prototype = obj;
    return new F(); //返回一個對象o, o.__proto__ = obj;
}

function Boss(name, age) {
    Person.call(this, name, age);
}
// Boss.prototype = Object.create(Person.prototype, {'constructor' : {'value': 'Sub'}});
// Boss.prototype.__proto__ = Person.prototype;
Object.setPrototypeOf(Boss.prototype, Person.prototype);
Object.setPrototypeOf(Boss, Person); //靜態方法實現原型繼承
let boss1 = new Boss('boss', '56');
console.log(boss1.name, boss1.age, boss1.getName());
console.log(Boss.say());
複製代碼

16-1 es5和 es6繼承的區別? this.生成順序不一樣

  • es5經過原型鏈式繼承或者構造函數式繼承實現的, 先生成子類的實例,再將父類得方法綁定在子類的this上
  • es6則是經過constructor裏的super() 先生成父類的實例,將子類的方法添加到this上,返回這個this,故使用this必須在constructor後使用。

16-2 function 和 class的區別?

  • function 和聲明提早, class不能聲明提早,class故存在暫時性死區
  • function可以重複定義,覆蓋以前定義的函數,class重複定義則會出錯
  • class的全部方法(實例方法,靜態方法)不能枚舉(enumerable:false),故不能使用Object.keys, 或for in 遍歷到相應屬性,可經過Object.getOwnPropertyNames()獲取對象自身上的全部屬性不包括Symbol屬性。
  • class全部方法沒有prototype屬性,不能new
  • class內部使用嚴格模式,沒法重寫類名,必須經過new才能調用。
  • TDZ的理解:暫時性死區,未聲明到聲明初始化等待的時間則爲TDZ,在塊做用域使用未聲明以及以後聲明的變量會出現暫時性死區。
var Foo = function() {
  this.foo = 21;
};

{
  const foo = new Foo(); // ReferenceError: Foo is not defined
  class Foo {
    constructor() {
      this.foo = 37;
    }
  }
}
function a(x = y, y) {
    console.log(x);
}
function b(x, y = x){
    console.log(y);
}
a(undefined, 1) //TDZ es6的默認賦值是根據let實現的
b(1, undefined) //正確
複製代碼

17. http緩存有哪些?優先級是?(協商緩存)

  • 都是從緩存獲取資源,強緩存不發出請求根據請求資源header判斷是否命中強緩存;協商緩存發出請求包含(if-modified-since/if-none-match)判斷是否命中協商緩存,返回200/304
  • 強緩存:在有效時間內,不會再請求服務器而直接從瀏覽器本地緩存獲取資源,對應的響應資源字段:expires(過時日期) cache-control:max-age (多少s後過時) no-cache no-store public private --- cache-control屬性
  • 協商緩存:不管是否變化或者是否過時都需重新發出請求,更據Etag值或者last-modified值判斷返回304仍是200,此請求不會返回資源數據,具體請求頭爲if-modified-since/if-none-match,向服務器請求last-modified/Etag值是否修改,資源沒有修改從瀏覽器緩存得到資源,不然更新資源。
  • 同時存在緩存頭優先級狀況
  • 強緩存 : cach-control優先級 > expires優先級
  • 對比緩存:Etag > last-modified
  • 強緩存 (200 from-cache) > 對比緩存(last-modified)
  • 加密資源使用強緩存,非加密資源使用對比緩存
  • 一、設置cache-control: public, max-age=0;記住,這裏的public是關鍵。由於默認值是private,表示其餘代理都不要緩存,只有服務器緩存,而max-age又爲0,因此每次都會發起200的請求。設置public的意思就是容許其餘各級代理緩存資源,所以若是資源沒改變會返回304。 二、直接設置max-age=1000。便是一秒以後內容過時,目的是觸發瀏覽器緩存。也能達到想要304的效果。 三、強緩存包含協商緩存,協商緩存必須在有強緩存時纔有效

18. html meta標籤

  • name: keywords, description, viewport,robot,athor,copyright,
  • http-equiv:至關於http請求頭,content-type,cache-control,expires,set-cookie
  • content: name和http-equiv配合使用content描述相應內容
  • 元數據具體對於網頁信息的描述,便於搜索引擎抓取數據

19. node EventEmitter事件驅動機制

  • 全部實現事件驅動的對象都是EventEmitter的實例,本質是觀察者模式,即發佈訂閱模式,註冊事件/觸發相應事件
//EventEmitter實例
const fs = require('fs');
const path = require('path');
const events = require('events');

//繼承events.EventEmitter實例
class MyEeventEmitter extends events.EventEmitter {}
let eventEmitterIns = new MyEeventEmitter();
//註冊事件
eventEmitterIns.addListener('read', function(data) {
    console.log(data);
});
eventEmitterIns.on('read', function(data) {
    console.log('這是觀察者模式');
});

//觸發事件 error first的方式
fs.readFile(path.resolve(__dirname,'date.html'), {encoding: 'utf-8'}, function(err, data){
    eventEmitterIns.emit('read', data); //觸發事件
});
複製代碼
  • EventEmitter模擬實現
/**
 * 相關api:
 * on(type, callback) addListener(type, callback) emit(type, data) removeListener(type, callback) removeAllListeners() setMaxListenter(number)
 * once(type,callback) 註冊的回調值執行一次,執行完後則刪除
 * listenerCount() //註冊監聽數量
 */
//本身實現一個EventEmitter()

class EventEmitter {
    constructor() {
        //對象屬性
        this.events = {};
        this.maxListeners = 10;
    }
    //原型方法
    //註冊回調 同一事件能夠有多個回調
    on(type, callback) {
        if (this.events[type]) {
            if (this.events[type] > this.maxListeners) {
                throw new TypeError('the listeners is full');
            }
            this.events[type].push(callback);
        } else {
            this.events[type] = [callback];
        }
    }
    //觸發事件
    emit(type, data) {
        this.events[type] && this.events[type].forEach(cb => cb.call(this, data));
        // for(let cb of this.events[type]){
        //     cb.call(this, data);
        // }
    }
    //只調用一次的回調函數 調用後則刪除註冊的事件
    once(type, callback) {
        let wrapper = data => {
            callback.call(this, data);
            this.removeListener(type, wrapper); //執行後則刪除該註冊事件
        };
        this.on(type, wrapper);
    }
    //刪除事件
    removeListener(type, callback) {
        this.events[type] && (this.events[type] = this.events[type].filter(cb => callback !== cb));
    }
    //刪除全部註冊事件
    removeAllListeners(type) {
        if (type) {
            delete this.events[type];
        } else {
            this.events = {};
        }
    }
    setMaxListeners(number) {
        this.maxListeners = number;
    }
    getMaxListeners() {
        return this.maxListeners;
    }
    listeners(type) {
        return this.events[type];
    }
}
複製代碼

20. bind在call/apply時沒法修改

function thisTest(){
    console.log(this.dog);
}
let obj1 = {
    dog: 'wei'
}
let obj2 = {
    dog: 'hello'
}
thisTest.bind(obj1).call(obj2);
//bind綁定的this沒法修改
複製代碼

21. delete運算符

  • 返回ture/false 刪除能夠configurable:true的屬性 var,let,const都不能刪除
  • 刪除自身的屬性,沒有的屬性刪除仍會返回false

前端每日一題

1. web安全與防護措施?

  • xss 跨站腳本攻擊 被動攻擊 須要引誘
  1. 執行供擊者代碼,如html,js,能夠經過注入代碼僞造表單,獲取用戶的私密信息,經過url方式獲取cookie信息
  2. 不要從url獲取數據,對url用戶輸入顯示到界面的數據進行轉義處理,將其轉爲普通字符,不爲特殊字符,執行一些沒必要要的行爲
  3. 字符集xss腳本設置meta charset = 'utf-8'字符集,避免其餘字符集攻擊
  4. 客戶端不要信任任何客戶端的數據,進行轉義和過濾處理 0 1
  5. Http請求頭 x-xss-contection對一些基本的xss進行過濾
  • csrf 跨站假裝請求 被動攻擊
  1. 用戶登陸網站後,不幸點擊進入一個危險網站,獲取登陸狀態信息,如cookie,向原網站發起請求,得到相應的登陸權限,進行用戶不知情的危險操做
  2. 多用Post請求,用戶足以發現一些僞造表單,使用驗證碼,對操做進行驗證,確保本人發出的操做,使用token(惟一隨機) 保存到用戶session 或 cookie,給表單添加token參數,token不一樣則驗證不經過。
  • SQL注入
  1. 經過執行數據庫命令拿到數據,利用對數據庫權限執行一些操做
  2. sql少使用數據sql拼接,使用參數化傳值(?); 給與用戶程序功能內的最小權限,sql注入時對數據庫的影響最小;對數據輸入進行過濾,轉義,避免對sql語句形成影響;不要暴露過多關於數據庫字段信息
  • 命令行注入
  1. 在調用shell命令的地方添加其餘shell命令,即os提供的命令,對文件系統服務器數據形成致命攻擊。
  2. 對用戶輸入進行轉義,過濾,過濾shell操做;避免拼接的shell命令
  • ddos攻擊 分佈式拒絕服務
  1. 不斷髮起http請求,致使資源過載,服務器不可服務
    1. 對url進行過濾 2. 對用戶進行封禁 3.判斷是機器(爬蟲)仍是人爲形成的 4.向相關機構反映
  • 流量劫持
  1. dns劫持 訪問的域名返回的是其餘域名的內容
  2. http劫持 篡改http報文首部或者主題信息,致使頁面顯示信息錯誤。 防護利用https

2. https?

  • http + ssl + tcl的協議,集加密 + 認證 + 數據的完整性的一種安全的通訊協議。
  • 加密:保證通訊的安全,結合對稱與非對稱加密的一種混合加密機制,http報文使用對稱加密,對稱加密的祕鑰使用非對稱加密,保證祕鑰的正確性。
  • 認證:須要向相關機構購買可信任的證書,確保服務器是合法的,可信任的,避免僞造的服務器或者客戶端。
  • 數據完整性:保證http報文不會被篡改,使用加密技術。
  • 端口爲443 http端口爲80

3. 實現Promise

//實現整個Promise的思路 共分爲4部走 實現Promise 實現原型方法 實現回調 實現靜態方法

//Promise構造階段
function Promise(fn) {
    if (!(this instanceof Promise)) throw new TypeError('promise must be used new promise'); //必須new才能使用
    if (typeof fn != 'function') throw new TypeError('the arguments must be the function'); //參數必須是函數
    this.state = 0; // 0: pending 1: fullfilled 2:rejected 3:fullfilled 可是決議值時promise故需展開得到決議值
    this.value = undefined; //決議值
    this.handled = false; //是否處理回調
    this.deffereds = []; //註冊的回調函數
    doResolve(this, fn);
}
function deResolve(self, fn) {
    //fn當即執行
    let done = false;
    try {
        fn(
            function(value) {
                //外部resolve reject執行該函數 由此可看出只決議一次
                if (done) return;
                done = true;
                resolve(self, value);
            },
            function(value) {
                if (done) return;
                done = true;
                reject(self, value);
            }
        );
    } catch (err) {
        if (done) return; //只決議一次
        done = true;
        reject(self, err);
    }
}
function reject(self, value) {
    self.state = 2;
    self.value = value;
    finale(self);
}
function resolve(self, value) {
    if (value === self) throw new TypeError('the promise value is not itself'); //決議值不能是本身
    //thenable檢查 函數或對象有then方法 則認爲是promise
    if (value && (typeof value == 'object' || typeof value === 'function')) {
        if (value instanceof promise) {
            //決議值時promise 則需展開
            self.state = 3;
            self.value = value;
            finale(self);
            return;
        } else {
            then = value.then;
            if (typeof then === 'function') {
                doResolve(self, then.bind(value)); //thenable promise則展開
            }
        }
    }
    self.state = 1;
    self.value = value;
    finale(self);
}
//異步回調
Promise.immediateFn =
    (typeof immediate === 'function' &&
        function(fn) {
            setImmediate(fn);
        }) ||
    function(fn) {
        setTimeout(fn, 0);
    };
//拿到狀態執行回調
function finale(self) {
    if (self.state === 2 && self.deferres.length === 0) {
        //拒絕狀態且無註冊回調則不處理
        Promise.immediateFn(function() {
            if (!self.handled) {
                console.log('the unhandle promise rejection');
            }
        });
    }
    for (let i = 0; i < self.deferreds.length; i++) {
        //執行回調
        handle(self, self.deferreds[i]);
    }
    self.deferreds = []; //處理完畢回調
}
//Promise 的原型方法
//返回一個新的promise 對象
function noop() {}
function Handle(self, onFullfilled, onRejected) {
    //註冊的回調構造函數
    this.onFullfilled = typeof onFullfilled == 'function' ? onFullfilled : null;
    this.onRejected = typeof onRejected == 'function' ? onRejected : null;
    this.promise = self;
}
Promise.prototype.then = function(onFullfilled, onRejected) {
    let pro = new Promise(noop); //未決議
    handle(this, new Handle(pro, onFullfilled, onRejected)); //註冊回調
    return pro;
};
Promise.prototype['catch'] = function(onRejected) {
    return this.then(null, onRejected);
};
//無論決議值成功失敗都將執行 且返回的promise的決議值爲原Promise的決議值
Promise.prototype['finally'] = function(callback) {
    //最後執行
    return this.then(
        function(value) {
            return Promise.resolve(callback()).then(function() {
                return value;
            });
        },
        function(value) {
            return Promise.resolve(callback()).then(function() {
                return value;
            });
        }
    );
};
//處理回調
function handle(self, deffered) {
    while (self.state === 3) {
        self = self.value; //展開其promise值 得到其決議值
    }
    if (self.state === 0) {
        self.deffereds.push(deffered);
        return;
    }
    self.handled = true;
    //異步執行回調
    Promise.immediateFn(function() {
        try {
            let callback = self.state === 1 ? deffered.onFullfilled : deffered.onRejected;
            if (callback === null) {
                //默認無註冊回調 將新promise的決議值變爲原promise的決議值
                self.state === 1 ? resolve(deffered.promise, self.value) : reject(defferd.promise, self.value);
            }
            let res = callback(self.value); //執行回調函數
            self.state === 1 ? resolve(deffered.promise, res) : reject(deffered.promise, res);
        } catch (err) {
            reject(deffered.promise, err);
        }
    });
}
//靜態方法
function isArrayLike(arr) {
    if (arr && typeof arr === 'object' && arr.length > 0 && isFinite(arr.length)) {
        return true;
    }
    return false;
}
//返回promise 參數爲類數組 競賽決議 數組爲空則永不決議
Promise.race = function(arr) {
    return new Promise(function(resolve, reject) {
        if (!isArrayLike(arr)) {
            throw new TypeError('the arguments must the array like');
        }
        let newArr = Array.from(arr);
        newArr.forEach(function(val) {
            Promise.resolve(val).then(resolve, reject);
        });
    });
};
Promise.all = function(arr) {
    return new Promise(function(resolve, reject) {
        if (!isArrayLike(arr)) {
            throw new TypeError('the arguments must the array like');
        }
        let newArr = Array.from(arr); //可將類數組 或迭代對象 變爲數組
        if (newArr.length === 0) resolve([]); //數組爲空當即決議
        let count = newArr.length,
            result = [];

        function res(index, value) {
            //判斷是否爲thenable
            if (value && (typeof value === 'object' || typeof value === 'function')) {
                let thenFunc = value.then;
                if (typeof thenFunc == 'function') {
                    Promise.resolve(value).then(function(val) {
                        res(index, val);
                    }, reject);
                }
            }
            result[index] = value;
            count--;
            if (!count) {
                resolve(result);
            }
        }
        for (let i = 0; i < newArr.length; i++) {
            result(i, newArr[i]); //一一對應獲取相應promise的結果
        }
    });
};
Promise.reject = function(val) {
    return new Promise(function(resolve, rejecte) {
        reject(val);
    });
};
//使其成爲真正的promise
Promise.resolve = function(val) {
    if (val instanceof Promise) {
        return val;
    }
    if (val && (typeof val === 'function' || typeof val === 'object')) {
        let thenFunc = val.then;
        if (typeof theFunc === 'function') {
            return new Promise(thenFunc.bind(val));
        }
    }
    return new Promise(function(resolve, reject) {
        resolve(val);
    });
};
複製代碼

4. MVC -> MVP -> MVVM

  1. MVC 優勢:1. Ui和業務邏輯都分離了,ui操做交給controller進行數據處理,調用model相應的接口,處理數據邏輯。經過觀察者(pub/sub)訂閱模式model數據反應到view上。2.能過作到多視圖同時更新。多個view訂閱同一個model; 缺點: 1. controller測試困難,須要有ui的支持,同時model數據的變化也須要有進行訂閱,反應到界面上。2. 不易組件化 view 依賴於model
  2. MVP(passive view) presenter(主持人)優勢:1.測試容易,prenster和view間經過接口進行數據交換,dom操做。model 和 presenter間經過觀察者模式進行數據交換。2. 利於組件化 缺點:1.須要實現許多手動操做,實現界面同步,管理比較麻煩
  3. MVVM(ViewModel) viewModel 即視圖模型,對數據的抽象,管理了許多狀態,實現了雙向數據綁定。即view經過viewModel同步到Model,model的數據也同步到view,viewModel經過binder實現view和相應model的綁定,而用戶不需去手動去處理同步問題。優勢:1. 提升代碼的維護性,提供雙向綁定,避免用戶過多的同步問題。2. 便於測試, 只要model正確,界面也會正確的展現。 缺點:1. 不適用於大型界面,須要管理較多的數據和狀態 2.界面須要進行相應的綁定,不易調試debug。(參考小程序的雙向綁定)

4. 徹底二叉樹的葉子節點數

  • n0 = n2 + 1 (推導度數) T = n - 1 = n2 * 2 + n1; n = n0 + n1 + n2
  • 徹底二叉樹 n1 = 0 或 1 故 n0 = (n + 1) / 2 或 n0 = n / 2;

5. 移動瀏覽器觸發的事件過程是?

  • touchstart touchmove touchend touchcancel

6. 數據結構中各類排序比較?

  • 不基於比較實現排序的是? 基數排序,桶排序 其他都是基於比較
  • 穩定的排序算法 冒泡 歸併 直接插入
  • 快排的時間複雜度 最好O(logn) 最壞 O(n)
  • 快排:最好:每次哨兵都能均分數組 則相似二分法 故遞歸調用棧爲O(logn) 最壞:每次哨兵爲身剩下數組的最大值或者最小值,即將數組分爲1個和剩下數組,則每一個數都要遞歸調用,空間複雜度爲O(n)
  • 二叉樹遞歸遍歷時間複雜度和空間複雜度都爲O(n)

7. Symbol 及基本類型

  • 不可枚舉,getOwnPropertyNames沒法獲得symbol的屬性值,僅經過getOwnPropertySymbols獲取Symbol值,獨一無二的值,參數爲字符串,即便相同的字符串也返回false,不能new,Symbol.for(str) 可獲得兩個爲true的symbol值

8. axios的原理?

9. css預處理的理解?

10. 關於javascript深淺拷貝?

//淺拷貝:只拷貝一層對象,經常使用方法有:手動實現拷貝一層Object.assign(target, ...source) slice() concat() ...展開運算符
function shollowClone(obj) {
    if (!isCanCloneType(obj)) {
        return obj;
    }
    let res = new obj.constructor(); //生成相應的拷貝對象
    for (let key in obj) {
        //訪問原型鏈上的屬性
        if (obj.hasOwnProperty(key)) {
            res[key] = obj[key];
        }
    }
    return res;
}
// let a = 3;
// let b = { data: { node: 'html' }, func: function() {} };
// let c = [7, 7, [77]];
// //暫未考慮特殊狀況 基本類型的對象 function RegExp map set
// console.log(shollowClone(a));
// console.log(shollowClone(b));
// let d = shollowClone(c);
// console.log(d);
// d[2][0] = 66; //只拷貝一層
// console.log(c);
//深拷貝:基本數據類型不用拷貝,只能拷貝對象(數組,函數, set, map都算對象), 且基本類型的對象拷貝 和 function,RegExp須要特殊考慮
//遞歸進行深拷貝
/**
 * 1. 考慮循環引用的問題,或者不一樣屬性引用同一屬性的問題,已經拷貝了的對象直接返回而不用在生成對象
 * 2. 考慮特殊狀況: 基本類型的對象,Boolean, Number, String, RegExp, Date, set, map, function/箭頭函數
 * 3. 不用遞歸作,將遞歸改變循環棧的形式 => 基本都是dfs深度優先遍歷
 */
let isCanCloneType = obj => (typeof obj == 'object' || typeof obj == 'function') && obj !== null; //代碼精簡
let calcType = obj => Object.prototype.toString.call(obj); // 返回對象的類型

let mapTypes = '[object Map]';
let setTypes = '[object Set]';
let numberTypes = '[object Number]';
let stringTyps = '[object String]';
let booleanTypes = '[object Boolean]';
let regexpTypes = '[object RegExp]';
let funcTypes = '[object Function]';
let symbolTypes = '[object Symbol]';
let dateTypes = '[object Date]';
//能夠處理的對象
let canTraverse = {
    '[object Object]': true,
    '[object Array]': true,
    '[object Map]': true,
    '[object Set]': true,
    '[object Arguments]': true
};
//深拷貝特殊類型的對象
let cloneSepecialObj = (obj, objTypes) => {
    let cons = obj.constructor;
    switch (objTypes) {
        case numberTypes:
            return new Object(Number.prototypeof.valueOf.call(obj));
        case stringTyps:
            return new Object(String.prototypeof.valueOf.call(obj));
        case booleanTypes:
            return new Object(Boolean.prototypeof.valueOf.call(obj));
        case symbolTypes:
            return new Object(Symbol.prototypeof.valueOf.call(obj));
        case dateTypes:
            return new cons(obj);
        case regexpTypes:
            handleRegExp(obj);
        case funcTypes:
            handleFunc(obj);
        default:
            return new cons(obj);
    }
};
function handleRegExp(obj) {
    const { source, flags } = obj; // flag: g i m(多行匹配) source:正則表達字符串
    return new obj.constructor(source, flags);
}
function handleFunc(obj) {
    if (!obj.prototype) {
        //若是是箭頭函數直接返回,由於箭頭函數每次都是新的值,而不會產生引用
        return obj;
    }
    let param, body, func;
    let paramReg = /(?<=\().*(?=\))\s*{/m; // ?<= 後行斷言 ?= 先行斷言
    let bodyReg = /(?<=\{)(.|\n)*(?=})/m; // ?<= 後行斷言 ?= 先行斷言
    func = obj.toString();
    param = paramReg.exec(func); //匹配成功第一項返回成功的值 匹配失敗返回null
    body = bodyReg.exec(func);
    if (!body) return null;
    if (param) {
        return new Function(...param[0], body[0]);
    } else {
        return new Function(body[0]);
    }
}
function deepClone(obj, map = new WeakMap()) {
    if (!isCanCloneType(obj)) {
        //判斷是否能夠拷貝
        return obj;
    }
    if (map.get(obj)) {
        return map.get(obj); //返回相對應的拷貝後對象
    }
    let res, objTypes;
    objTypes = calcType(obj);
    if (!canTraverse[objTypes]) {
        return cloneSepecialObj(obj, objTypes);
    }
    res = new obj.constructor(); //生成相應的對象
    map.set(obj, res); //保存對象 和其拷貝對象

    if (objTypes === mapTypes) {
        //若是是map則遍歷進行拷貝
        obj.forEach((val, key) => {
            res.set(deepClone(key, map), deepClone(val, map));
        });
    }
    if (objTypes === setTypes) {
        obj.forEach(val => {
            res.add(deepClone(val, map));
        });
    }
    //拷貝對象
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            res[key] = deepClone(obj[key], map);
        }
    }
    return res;
}
let a = 3;
console.log(deepClone(a));
let b = { data: { node: 'html' } };
//暫未考慮特殊狀況 基本類型的對象 function RegExp map set

let bb = deepClone(b);
bb.data.node = 'div';
console.log(b);
console.log(bb);

let c = [7, 7, [77]];
let d = deepClone(c);
d[2][0] = 66; //只拷貝一層
console.log(c);
console.log(d);
//map 強引用鍵值的對象 程序結束才垃圾回收 weakMap弱引用鍵值的對象,會則垃圾回收機制的時候進行回收
let map = new Map();
map.set(b, 'b');
map.set(c, 'c');
map.set('key', b);
let newMap = deepClone(map);
b.data.node = 'map';
console.log(map);
console.log(newMap);

//循環引用
var cir = { val: 3 };
cir.data = cir;
var newCir = deepClone(cir);
cir.data = { data: 4 };
console.log(cir);
console.log(newCir);
複製代碼

11. javascript高階函數?

  • 一個函數能夠接受另外一個函數做爲參數或者返回值爲一個函數,這種函數稱爲高階函數。

12. 什麼是函數的柯里化?

/**
 * 柯里化函數:部分求值,傳遞給函數部分參數,並返回一個函數來接收剩餘的參數,分步求值,提升函數的複用性
 */
//1.經過獲取全部參數最後執行函數實現
function curring(fn){
    let args = [].slice.call(arguments, 1); 
    let len = fn.length - args.length;
   return function temp(){
        len = len - arguments.length;
        args.push(...arguments);
        if(len > 0){
            return temp;
        }else{
            return fn.apply(this, args);
        }
   }
}
function add(a, b){
    return a + b;
}
//2.經過對原有函數進行包裝,最後傳遞參數進行執行
function subCurring(fn, ...args){ //對原有函數進行包裝
    return function(...newArgs){
        return fn.apply(this,args.concat(newArgs));
    }
}
function curring(fn, length){
    let len = length || fn.length;
    let args = [];
    return function (){
        len = len - arguments.length;
        args.push(...arguments);
        if(len > 0){
            return curring(subCurring.apply(this, [fn].concat(args)), len);
        }else{
            return fn.apply(this, args);
        }
    }
}
let foo = curring(add);
console.log(foo(1)(2));
複製代碼

13. React中高階組件是什麼?

  • 高階組件是參數爲組件,返回值爲組件的函數。實現了複用組件的邏輯,對傳入組件進行包裝,更改組件屬性,抽象state,劫持渲染子組件。經常使用的兩種高階組件方式有:屬性代理,反向繼承。
  • 屬性代理的做用:更改props:增刪改查;獲取refs即組件的實例,無狀態組件不可獲取;抽象state將包裝組件的state封裝的高階組件內,經過props進行父子組件傳值;將包裝組件和其餘組件進行包裝
  • 反向繼承:返回的組件繼承傳參的組件,劫持渲染:條件渲染,從新渲染樹; 操做state
  • 屬性代理
//更改props 增長 刪除 修改 讀取 要傳遞給包裝組件的props
//獲取refs實例
//抽象state
function Hoc(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.input = React.createRef(); //獲取實例
      this.changeFunc = this.changeFunc.bind(this);
      this.blur = this.blur.bind(this);
      this.state = {
        value: "抽象state, 複用組件邏輯",
        onChange: this.changeFunc,
        onBlur: this.blur
      };
    }
    blur() {
      alert(this.input.current.props.value);
    }
    changeFunc(event) {
      event.stopPropagation(); //阻止冒泡
      this.setState({
        //抽象state
        value: event.target.value
      });
    }
    render() {
      let style = {
        backgroundColor: "red",
        fontSize: 20
      };
      return (
        <WrappedComponent
          ref={this.input}
          {...this.props}
          {...this.state}
          style={style}
        />
      );
    }
  };
}
class WrappedComponent extends React.Component {
  render() {
    return <input type="input" {...this.props} />;
  }
}
function App() {
  let HocCom = Hoc(WrappedComponent);
  return <HocCom value="hello world!" />;
}
複製代碼
  • 反向繼承
import React from "react";
import { Component } from "react";
//反向繼承
//劫持渲染
class WrappedComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      time: 2019
    };
  }
  render() {
    return (
      <h3 login={false}>
        hello world!
        <p>paragraf</p>
        {this.state.time}
      </h3>
    );
  }
}
function Hoc(WrappedComponent) {
  return class Test extends WrappedComponent {
    render() {
      //根據 props條件渲染
      if (this.props.login) {
        return super.render();
      } else {
        return <p>沒有登陸</p>;
      }
    }
  };
}
//從新渲染樹 更改props更改子樹 從新渲染 重未更改包裝組件的props props是隻讀的不可更改
function HocII(WrappedComponentI) {
  return class Test extends WrappedComponentI {
    render() {
      let elementTree = super.render();
      let newProps = {
        style: { backgroundColor: "red" },
        value: "hello 反向繼承"
      };
      return (
        <div>
          {this.state.time}
          {React.cloneElement( elementTree, newProps, // <input value="hahahh" />
          elementTree.props.children )}
        </div>
      );
    }
  };
}

export default HocII(WrappedComponent);
複製代碼

14. javascript垃圾回收?

  • 定義: Js具備自動垃圾回收機制,即自動回收不使用的變量,全局變量,生命週期爲整個頁面,不會垃圾回收;局部變量除閉包外通常執行完畢後也會被垃圾回收,函數內部的全局變量不被回收
  • 垃圾回收方案:
  1. 標記清除:變量執行的時候標記爲進入執行 執行完後標記爲離開環境,回收離開環境的變量
  2. 引用計數:對引用的變量使用次數進行計數,回收使用次數爲0的變量
  • 內存泄漏 即沒法回收未使用的變量
  1. 函數內產生全局變量後且未使用的變量
function fn() {
   		name = "你我貸"
             }
   	     console.log(name) //name沒法回收
複製代碼
  1. 定時器沒有適當銷燬,保留整個定時器環境
  2. 對dom的引用致使已卸載的dom仍然沒法回收,有時父元素也沒法回收
  3. 閉包也會產生內存泄漏

15. 數組扁平化?

//[1, [2, 3, [4, 5]]]  ------>    [1, 2, 3, 4, 5] 遞歸法扁平化數組,求數組深度
function flatten(arr, res){
    let count = 1;
    for(let value of arr){
        if(Array.isArray(value)){
            count = flatten(value, res) + 1;
        }else{
            res.push(value);
        }
    }
    return count;
}
let res = [];
let count = flatten([1, [2, 3, [4, 5]],10], res);
console.log(res, count);
//法二 優先選擇
function flattenI(arr){
    let res = [];
    for(let value of arr){
        if(Array.isArray(value)){
            res = res.concat(flattenI(value));
        }else{
            res.push(value);
        }
    }
    return res;
}
console.log(flattenI([1, [2, 3, [4, 5]],10]));
//法三 reduce
function flattenII(arr){
    return arr.reduce(function(pre, current){
        return pre.concat(Array.isArray(current) ? flattenII(current) : current);
    },[]);
}
console.log(flattenII([1, [2, 3, [4, 5]],10]));
//法四 toString()/join()
function flattenIII(arr){
    return arr.toString().split(',').map(value => Number(value));
}
console.log(flattenIII([1, [2, 3, [4, 5]],10]));
function flattenIIII(arr){
    return arr.join(',').split(',').map(value => parseInt(value));
}
console.log(flattenIIII([1, [2, 3, [4, 5]],10]));
//...展開運算符,展開二維數組
function flattenIIIII(arr){
    while(arr.find(value => Array.isArray(value))){
        arr = [].concat(...arr);        
    }
    return arr;
}
console.log(flattenIIIII([1, [2, 3, [4, 5]],10]));
//展開方法
function faltWays(arr){
    return arr.flat(Infinity); // 扁平深度爲無窮 僅瀏覽器中存在該方法
}
//正則表達式匹配
function faltReg(arr){
    return JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(val => Number(val));
}
//JSON.stringify和正則表達式匹配最後轉化爲整形
function faltRegI(arr){
    let str = JSON.stringify(arr).replace(/(\[|\])/g, '');
    str = '[' + str + ']';
    return JSON.parse(str).map(val => parseInt(val));
}
複製代碼

16. 實現new方法

/**
 * new: 生成一個對象,對象進行原型鏈委託,this指向這個對象,若是返回值是對象(不是null)或者函數,則返回自身,不然返回生成的對象
 */
function newFunc(fn, ...args) {
    if (typeof fn != 'function') {
        throw new TypeError(`the  first param is not a function`);
    }
    let obj = Object.create(fn.prototype); //完成2步
    let res = fn.apply(obj, args);
    let isObject = typeof res == 'object' && res !== null;
    let isFunction = typeof res == 'function';
    return isObject || isFunction ? res : obj;
}

複製代碼
  • 遞歸作深拷貝
//遞歸作深拷貝
function deepClone(obj, map = new WeakMap()){
    if(!isCanCloneType(obj)){
        return obj;
    }
    let objTypes, root, stack = [];
    objTypes = calcType(obj);
    if(!canTraverse[objTypes]){
        return cloneSpecialObj(obj, objTypes);
    }
    root = new obj.constructor();
    stack[0] = { //初始化首棧 key data 表明當前父元素須要拷貝的下一個對象
        parent: root,
        key: undefined,
        data: obj
    }
    while(stack.length){
        let node = stack.pop();
        let {parent, key, data} = node;
        let res;
        if(key !== undefined){
            res = parent[key] = new data.constructor(); //若是存在下一層級,則進行關聯,指向同一地址,進行拷貝,沒有則拷貝在父元素上
        }else{
            res = parent;
        }
        if(map.get(data)){ //循環引用
            parent[key] = map.get(data);
            continue;
        }
        map.set(data, res);
        for(let key in data){
            if(obj.hasOwnProperty(key)){
               if(isCanCloneType(data[key])){
                    stack.push({
                        parent: res,
                        key: key,
                        data: data[key]
                    })
               } else{
                   res[key] = data[key];
               }
            }
        }
    }
    return root;
}
複製代碼

17. js嚴格模式和非嚴格模式區別

  1. 代碼檢查上會更嚴格,非嚴格模式能經過的,嚴格模式沒法經過,有時會報錯
  2. 嚴格模式更規範,嚴謹,便於維護和排查錯誤。
  3. 嚴格模式體現:
  • 不能使用未定義的變量,會報錯,正常狀況成爲全局變量
  • delete刪除configurable:false會報錯,正常狀況返回false
  • 嚴格模式下this指向undefined 正常狀況this指向window
  • 嚴格模式下arguments不會追蹤函數參數變量,正常狀況下會進行追蹤
  • 嚴格模式函數參數不能重名,會報錯
  • 嚴格模式不能使用with,(with欺騙詞法做用域,運行的時候才執行,js引擎沒法進性優化,影響程序性能,故不推薦使用with,with中變量和對象屬性同名時指向對象屬性)
({
x: 10,
foo: function () {
    function bar() {
        console.log(x); //undefined
        console.log(y); // 30
        console.log(this.x); //20
    }
    with (this) {
        var x = 20; // 同名屬性進行修改 聲明提早
        var y = 30; //局部變量值改變 聲明提早
        bar.call(this); 
    }
}
}).foo();
複製代碼
  • 嚴格模式eval會產生單獨的做用域,即局部做用域不會成爲全局變量

18. this指向,按優先級進行例舉

  1. this 詞法做用域指向外層函數的this
  2. new 指向生成的對象
  3. 顯示綁定 call/apply/bind 傳參指向this
  4. 隱式綁定:obj.a //指向Obj
  5. 默認綁定:函數直接執行,非嚴格模式指向全局變量window/global /嚴格模式指向undefined
  6. dom事件流中 onclick 或者 addEventListener(type, cb, false) this指定調用的dom元素 attachEvent():IE中指向window

19. 實現instanceOf

function ins(obj, obj2) {
    if (typeof obj2 !== 'function') {
        throw new TypeError('the second param must be function');
    }
    let obj1 = obj;
    while (obj1) {
        if (obj1.__proto__ === obj2.prototype) {
            return true;
        }
        obj1 = obj1.__proto__;
    }
    return false;
}
複製代碼
相關文章
相關標籤/搜索