整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          2018春招前端面試:闖關(guān)記(精排精校) - 掘金技

          2018春招前端面試:闖關(guān)記(精排精校) - 掘金技術(shù)征文

          年末研發(fā)組解散失業(yè), 選擇回去學(xué)車了,也順利拿到了駕照,最近回歸大深圳,開始踏上漫漫的找工作之路。

          拉勾上吊一百年不匹配, BOSS直聘日夜沒反應(yīng)。

          題目范圍涵蓋我最近遇到的筆試題和面談的(CSS/JS/HTTP/Node/Hybrid/Vue/NG/React)

          emm,這里不列舉哪些公司了, 若是你完整的閱讀一遍,相信你有不少的收獲,謝謝閱讀

          • 問題截止日期(2018/3/23),我去面的創(chuàng)業(yè),中大型皆有。
          • 期間死在各種一面/二面/三面/四面皆有之,也拿到部分和推掉部分offer,還有一些后續(xù)不清楚的

          問題匯總,想到就寫

          Q: CSS 有哪些樣式可以給子元素繼承!

          • 可繼承的:font-size,font-weight,line-height,color,cursor等
          • 不可繼承的一般是會改變盒子模型的:display,margin、border、padding、height等

          更加全面的可以到引擎找

          Q: 行內(nèi)元素有哪些?塊級元素有哪些? 空(void)元素有那些?

          • 行內(nèi): input,span,a,img以及display:inline的元素
          • 塊級: p,div,header,footer,aside,article,ul以及display:block這些
          • void: br,hr

          Q: CSS3實現(xiàn)一個扇形

          • 思路跟畫實體三角形一個道理,只不過多了一個圓角屬性
          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1.0">
           <meta http-equiv="X-UA-Compatible" content="ie=edge">
           <title>扇形</title>
           <style>
           .sector {
           width: 0;
           height: 0;
           border-width: 50px;
           border-style: solid;
           border-color: #f00 transparent transparent;
           border-radius: 50px;
           }
           </style>
          </head>
          <body>
           <div class="sector"></div>
          </body>
          </html>
          復(fù)制代碼
          

          Q: box-sizing常用的屬性有哪些? 分別有啥作用?

          box-sizing有兩個值:content-box(W3C標(biāo)準(zhǔn)盒模型),border-box(怪異模型),

          這個css 主要是改變盒子模型大小的計算形式

          可能有人會問padding-box,這個之前只有 Firefox 標(biāo)準(zhǔn)實現(xiàn)了,目前50+的版本已經(jīng)廢除;

          用一個栗子來距離,一個div的寬高分別100px,border為5px,padding為5px

           <style>
           .test {
           box-sizing: content-box;
           border: 5px solid #f00;
           padding:5px;
           width: 100px;
           height: 100px;
           }
           </style>
           <div class="test"></div>
          <!--
          content-box的計算公式會把寬高的定義指向 content,border和 padding 另外計算,
          也就是說 content + padding + border=120px(盒子實際大小)
          而border-box的計算公式是總的大小涵蓋這三者, content 會縮小,來讓給另外兩者
          content(80px) + padding(5*2px) + border(5*2px)=100px
          -->
          復(fù)制代碼
          

          Q: 清除浮動的方式有哪些?比較好的是哪一種?

          常用的一般為三種.clearfix, clear:both,overflow:hidden;

          比較好是 .clearfix,偽元素萬金油版本,后兩者有局限性..等會再扯

           .clearfix:after {
           visibility: hidden;
           display: block;
           font-size: 0;
           content: " ";
           clear: both;
           height: 0;
           }
          <!--
          為毛沒有 zoom ,_height 這些,IE6,7這類需要 csshack 不再我們考慮之內(nèi)了
          .clearfix 還有另外一種寫法,
          -->
          .clearfix:before, .clearfix:after {
          	content:"";
          	display:table;
          }
          .clearfix:after{
          	clear:both;
          	overflow:hidden;
          }
          .clearfix{
           zoom:1;
          }
          <!--
          用display:table 是為了避免外邊距margin重疊導(dǎo)致的margin塌陷,
          內(nèi)部元素默認(rèn)會成為 table-cell 單元格的形式
          -->
          復(fù)制代碼
          

          clear:both:若是用在同一個容器內(nèi)相鄰元素上,那是賊好的,有時候在容器外就有些問題了, 比如相鄰容器的包裹層元素塌陷

          overflow:hidden:這種若是用在同個容器內(nèi),可以形成 BFC避免浮動造成的元素塌陷

          Q: CSS 中transition和animate有何區(qū)別? animate 如何停留在最后一幀!

          這種問題見仁見智,我的回答大體是這樣的..待我捋捋.

          transition一般用來做過渡的, 沒時間軸的概念, 通過事件觸發(fā)(一次),沒中間狀態(tài)(只有開始和結(jié)束)

          而animate則是做動效,有時間軸的概念(幀可控),可以重復(fù)觸發(fā)和有中間狀態(tài);

          過渡的開銷比動效小,前者一般用于交互居多,后者用于活動頁居多;

          至于如何讓animate停留在最后一幀也好辦,就它自身參數(shù)的一個值就可以了

          animation-fill-mode: forwards;
          <!--backwards則停留在首幀,both是輪流-->
          復(fù)制代碼
          

          讓我們來舉個栗子,.自己新建一個 html 跑一下,.

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1.0">
           <meta http-equiv="X-UA-Compatible" content="ie=edge">
           <title>Box-sizing</title>
           <style>
           .test {
           box-sizing: border-box;
           border: 5px solid #f00;
           padding: 5px;
           width: 100px;
           height: 100px;
           position:absolute;
           /*
           簡寫的姿勢排序
           @keyframes name : 動畫名
           duration 持續(xù)時間
           timing-function 動畫頻率
           delay 延遲多久開始
           iteration-count 循環(huán)次數(shù)
           direction 動畫方式,往返還是正向
           fill-mode 一般用來處理停留在某一幀
           play-state running 開始,paused 暫停 ,.
           更多的參數(shù)去查文檔吧..我就不一一列舉了
           */
           animation: moveChangeColor ease-in 2.5s 1 forwards running;
           }
           @keyframes moveChangeColor {
           from {
           top:0%;
           left:5%;
           background-color:#f00
           }
           to{
           top:0%;
           left:50%;
           background-color:#ced;
           }
           }
           </style>
          </head>
          <body>
           <div class="test"></div>
          </body>
          </html>
          復(fù)制代碼
          

          Q: 塊級元素水平垂直居中的方法

          我們要考慮兩種情況,定寬高和不定寬高的;

          方案 N 多種,我記得我很早寫過這類的筆記

          傳送門:網(wǎng)頁元素居中攻略記

          Q: 說說樣式權(quán)重的優(yōu)先級;

          !important > 行內(nèi)樣式 > id > class > tag

          樣式權(quán)重可以疊加, 比如 id>class

          Q: 對HTML語義化的理解

          簡言之:就是不濫用標(biāo)簽(比如 DIV)/隨意嵌套(比如 span>div) ,

          類的命名要合理, 利于瀏覽器解析乃至引擎收錄,也利于團(tuán)隊協(xié)作和維護(hù)

          Q: JS有幾種數(shù)據(jù)類型,其中基本數(shù)據(jù)類型有哪些!

          七種數(shù)據(jù)類型

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

          (ES6之前)其中5種為基本類型:string,number,boolean,null,undefined,

          ES6出來的Symbol也是原始數(shù)據(jù)類型 ,表示獨一無二的值

          Object 為引用類型(范圍挺大),也包括數(shù)組、函數(shù),

          Q: null和undefined的差異

          大體說一下,想要知其所以然請引擎搜索

          相同點:

          • 在 if判斷語句中,值都默認(rèn)為 false
          • 大體上兩者都是代表,具體看差異

          差異:

          • null轉(zhuǎn)為數(shù)字類型值為0,而undefined轉(zhuǎn)為數(shù)字類型為 NaN(Not a Number)
          • undefined是代表調(diào)用一個值而該值卻沒有賦值,這時候默認(rèn)則為undefined
          • null是一個很特殊的對象,最為常見的一個用法就是作為參數(shù)傳入(說明該參數(shù)不是對象)
          • 設(shè)置為null的變量或者對象會被內(nèi)存收集器回收

          Q: JS 的DOM 操作(Node節(jié)點獲取及增刪查改);

          • 獲取(太多了,有document.getElementById/ClassName/Name/TagName 等,或者 querySelector)
          // example
          // get Node
          var element=document.querySelector('#test');
          // 追加
          element.appendChild(Node);
          // 刪除
          element.removeChild(Node);
          // 查找
          element.nextSibling // 獲取元素之后的兄弟節(jié)點 , 會拿到注釋文本,空白符這些
          element.nextElementSibling // 等同, 獲取標(biāo)簽(不會拿到注釋文本這些)
          element.previousSibling // 和上面同理,往前找兄弟節(jié)點
          element.previousElementSibling
          // 改動,比如 屬性這些
          element.setAttribute(name, value); // 增加屬性
          element.removeAttribute(attrName); //刪除屬性
          // 來一個簡易的練習(xí)題,隨便一個網(wǎng)頁追加插入一塊DOM(非覆蓋:不能 innerHTML);
          /*
          <div id="test">
           <span>Hello, World</span>
          </div>
          */
          // 以上面的例子為例
          var test=document.createElement('div'); // 創(chuàng)建一個塊級元素
          test.setAttribute("id","test"); // 設(shè)置其id 屬性
          var span=document.createElement('span'); // 創(chuàng)建一個 span
          span.innerText="Hello,world"; // 插入 span 的文本內(nèi)容
          test.appendChild(span); // 組合節(jié)點
          element.appendChild(test); //追加到某個節(jié)點區(qū)域
          復(fù)制代碼
          

          Q:Javascript中,有一個函數(shù),執(zhí)行時對象查找時,永遠(yuǎn)不會去查找原型,這個函數(shù)是?

          hasOwnProperty,這個更多的是用來區(qū)分自身屬性和原型鏈上的屬性。

          Q: 給一個 DOM添加捕獲和冒泡的兩種寫法的事件點擊,誰先執(zhí)行?

          分情況分析:

          • 有拿到節(jié)點的,優(yōu)先捕獲,沒有才往上冒泡尋找
          • 若是通過node.addEventListener('event',callback,bubble or capture); 誰先調(diào)用誰先執(zhí)行

          stackoverflow 有相關(guān)的探討:

          • Event listeners registered for capturing phase not triggered before bubbling - why?

          Q: 談?wù)勀銓jax 的理解,以及用原生 JS 實現(xiàn)有哪些要點需要注意;

          ajax全稱是異步 javascript 和 XML,用來和服務(wù)端進(jìn)行數(shù)據(jù)交互的,讓無刷新替換頁面數(shù)據(jù)成了可能;

          至于有哪些要要點,來一個簡短的ajax請求

          var xhr=new XMLHttpRequest(); // 聲明一個請求對象
          xhr.onreadystatechange=function(){
           if(xhr.readyState===4){ // readyState 4 代表已向服務(wù)器發(fā)送請求
           if(xhr.status===OK){ // // status 200 代表服務(wù)器返回成功
           console.log(xhr.responseText); // 這是返回的文本
           } else{
           console.log("Error: "+ xhr.status); // 連接失敗的時候拋出錯誤
           }
           }
          }
          xhr.open('GET', 'xxxx');
          // 如何設(shè)置請求頭? xhr.setRequestHeader(header, value);
          xhr.setRequestHeader('Content-Type', 'application/json');
          xhr.send(null); // get方法 send null(亦或者不傳,則直接是傳遞 header) ,post 的 send 則是傳遞值
          復(fù)制代碼
          

          更為詳細(xì)的可以閱讀此處;

          • <<ajax 概念 by 阮一峰>>
          • <<XMLHttpRequest2 用法指南>>

          Q: JS 實現(xiàn)一個閉包函數(shù),每次調(diào)用都自增1;

          這里主要考察了閉包,函數(shù)表達(dá)式以及 IIFE(立即執(zhí)行表達(dá)式)

          var add=(function() {
           // 聲明一變量,由于下面 return所以變量只會聲明一次
           var count=0;
           return function() {
           return console.log(count++);
           };
          })();
          add(); // 0
          add(); // 1
          add(); // 2
          復(fù)制代碼
          

          Q: ['1','2','3'].map(parseInt) 輸出什么,為什么?

          ['1','2','3'].map(parseInt); // [1,NaN,NaN]
          // 刨析
          // map有三個參數(shù):數(shù)組元素,元素索引,原數(shù)組本身
          // parseInt有兩個參數(shù),元素本身以及進(jìn)制
          // 理清了這兩個就好辦了,
          // ['1','2','3'].map(parseInt); 等于如下
          ['1','2','3'].map(function(item,index,array){
           return parseInt(item,index); // 是不是一目了然
          });
          // parseInt("1",0);=> 1
          // parseInt("2",1);=> NaN
          // parseInt("3",2);=> NaN
          復(fù)制代碼
          

          Q:如何實現(xiàn)瀏覽器內(nèi)多個標(biāo)簽頁之間的通信?

          WebSocket、localstorge、cookies都可以。

          要考慮瀏覽器無痕模式的話用WebSocket會更好,不然功能基本失效或者報錯。

          Q:webSocket如何兼容低瀏覽器?

          最常見的就是輪詢XHR

          Q: 什么是window對象? 什么是document對象?

          window對象是指瀏覽器打開的窗口。

          document對象是HTML 文檔對象的一個只讀引用,window對象的一個屬性。

          Q: 對數(shù)組 ['2018-03-05', '2013-06-12','2019-03-12','2018-03-05','2014-02-22'] 去重且排序

          我這里用的是結(jié)合 ES6的,代碼量很短

          //很好理解, Set 具有值唯一性(但不是所有值,等會我拋出我的另外一篇文章)
          // 結(jié)合,解構(gòu),可以把可迭代(比如 arguments/nodelist 等)的轉(zhuǎn)為數(shù)組
          // sort 里面?zhèn)魅?兩個值比較,返回-1和1是因為1代表這個數(shù)大排后(相對),-1代表小(相對),0為相等
          let arr=[,new Set(['2018-03-05', '2013-06-12','2019-03-12','2018-03-05','2014-02-22'])].sort(function(a,b){
           return a<b ? -1:1; // 這里返回的是升序的,降序改下返回值就好了.所以是相對
          })
          // ["2013-06-12", "2014-02-22", "2018-03-05", "2019-03-12"]
          復(fù)制代碼
          

          對于數(shù)組去重的,有興趣的可以看下我這篇水文:

          • JS數(shù)組去重!!!一篇不怎么靠譜的"深度"水文

          Q: 對數(shù)組[1,2,3,4,5,'6',7,'8','a','b','z']進(jìn)行亂序

          // 我們依舊可以用上面的 sort 的原理實現(xiàn)亂序
          let tempArr=[1,2,3,4,5,'6',7,'8','a','b','z'].sort(function(){
           return Math.random() > 0.5 ? -1 : 1;
          })
          // 因為里面有隨機數(shù),所以答案沒有標(biāo)準(zhǔn)答案,我這邊跑了一次是輸出這個
          //["6", "z", 3, "b", 5, 2, 7, "8", "a", 1, 4]
          復(fù)制代碼
          

          上面和這道題逗涉及到數(shù)組順序的問題,想了解下為什么 a-b,a>b這類可以更改排序

          可以看看知乎對于這塊的探討: 傳送門:javascript排序return a-b?

          Q: 求[1, 10, 11, -1,'-5',12, 13, 14, 15, 2, 3, 4, 7, 8, 9]內(nèi)最大值與最小值之差

          // 來一個很粗糙的版本,只當(dāng)傳入是數(shù)組且可以隱性轉(zhuǎn)為數(shù)字的
          function MaxMinPlus(arr) {
           // 返回最大值與最小值之差
           return Array.isArray(arr) ? Math.max.apply(Math, arr) - Math.min.apply(Math, arr) : console.log('傳入的不是數(shù)組亦或者未能解決的錯誤')
          }
          // 結(jié)果是 20
          // 若是要完善的話,要考慮傳入的是非數(shù)組,
          //傳入字符串的時候要判斷,然后切割為數(shù)組..
          // 都要考慮進(jìn)去代碼量不短
          復(fù)制代碼
          

          Q: 請給Array實現(xiàn)一個方法,去重后返回重復(fù)的字符(新數(shù)組)

           var testArr=[1,6,8,3,7,9,2,7,2,4,4,3,3,1,5,3];
           Array.prototype.extraChar=function(){
           var cacheExtraChar=[]; // 緩存重復(fù)出現(xiàn)的字符
           var that=this; // 緩存 this;
           this.map(function(item,index){
           // 怎么理解這段代碼呢?
           // 就是向前往后查找一遍和從后往前查找一遍,不等就是沒有重復(fù)
           // 為什么還要判斷一遍緩存,是過濾緩存數(shù)組內(nèi)多次寫入
           (that.indexOf(item) !==that.lastIndexOf(item)) && cacheExtraChar.indexOf(item)===-1 ? cacheExtraChar.push(item) : -1;
           });
           return cacheExtraChar;
           }
          testArr.extraChar(); // [1, 3, 7, 2, 4]
          // 若是還需要排序就再排序下
          [1,6,8,3,7,9,2,7,2,4,4,3,3,1,5,3]
          .extraChar()
          .sort(function(a,b){return a-b}) // [1, 2, 3, 4, 7]
          復(fù)制代碼
          

          Q: 一個數(shù)組中 par中存放了多個人員的信息,每個人員的信息由 name 和 age 構(gòu)成({name:'張三',age:15}).請用 JS 實現(xiàn)年齡從小到大的排序;

          var par=[{age:5,name:'張三'},{age:3,name:'李四'},{age:15,name:'王五'},{age:1,name:'隨便'}]
          var parSort=par.sort(function(a,b){
           return a.age - b.age;
          })
          復(fù)制代碼
          

          Q: 判斷一個回文字符串和同字母異序字符串

          回文字符串

          就是正序倒序都是一樣的;

          同字母異序字符串

          字符串都一樣,但是位置可能不一定一樣,比如abcefd和dceabf=>return true

          后者的思路就是用排序把異序扭正

          普通版

          // 回文判斷 , 比如用 abcba
          var isPalindromes=function(params){
           params=params.toString().toLowerCase()
           return params===params.split('').reverse().join('');
          }
          // 同字母異序判定,比如`abcefd`和`dceabf`
          var isAnagram=function(str1, str2) {
           str1=str1.toString().toLowerCase();
           str2=str2.toString().toLowerCase();
           return str1.split('').sort().join('')===str2.split('').sort().join('')
          }
          復(fù)制代碼
          

          進(jìn)階版:多一些特殊字符

          若是我們要去除所有非字母數(shù)字的字符,則需要用到正則

          // 進(jìn)階版: isPalindromes('abc_ &b #@a')
          var isPalindromes=function(params){
           // 傳入?yún)?shù)先轉(zhuǎn)為字符串且全部轉(zhuǎn)為小寫,最后去除多余字符比較
           params=params.toString().toLowerCase().replace(/[\W_\s]/g,'');
           console.log(params)
           return params===params.split('').reverse().join('');
          }
          // 進(jìn)階版同字母異序: isAnagram('ab *&cef#d','!d@ce^abf')
          var isAnagram=function(str1, str2) {
           str1=str1.toString().toLowerCase().replace(/[\W_\s]/g,'');
           str2=str2.toString().toLowerCase().replace(/[\W_\s]/g,'');
           return str1.split('').sort().join('')===str2.split('').sort().join('')
          }
          復(fù)制代碼
          

          Q: JS 實現(xiàn)String.trim()方法;

          // 原生是有 trim()方法的.我們要模擬一個;
          String.prototype.emuTrim=function(){
           // 這條正則很好理解,就是把頭部尾部多余的空格字符去除
           return this.replace(/(^\s*)|(\s*$)/g,'');
          }
          ' fsaf fsdaf f safl lllll '.emuTrim(); //"fsaf fsdaf f safl lllll"
          復(fù)制代碼
          

          Q: JS 實現(xiàn)函數(shù)運行一秒后打印輸出0-9;給定如下代碼

          for(var i=0;i<10;i++){
           // TODO
          }
          復(fù)制代碼
          
          • 解法
          // 這道題涉及到作用域
          for(var i=0;i<10;i++){
           setTimeout((function(i){
           return function(){
           console.log(i);
           }
           })(i),1000);
          }
          復(fù)制代碼
          

          若是用到 ES6,那簡直不能再簡便了

          for(let i=0;i<10;i++){
           setTimeout(function(){
           console.log(i);
           },1000);
          }
          復(fù)制代碼
          

          Q: 實現(xiàn)對一個數(shù)組或者對象的淺拷貝和"深度"拷貝

          淺拷貝就是把屬于源對象的值都復(fù)制一遍到新的對象,不會開辟兩者獨立的內(nèi)存區(qū)域;

          深度拷貝則是完完全全兩個獨立的內(nèi)存區(qū)域,互不干擾

          • 淺拷貝
          // 這個 ES5的
          function shallowClone(sourceObj) {
           // 先判斷傳入的是否為對象類型
           if (!sourceObj || typeof sourceObj !=='object') {
           console.log('您傳入的不是對象!!')
           }
           // 判斷傳入的 Obj是類型,然后給予對應(yīng)的賦值
           var targetObj=sourceObj.constructor===Array ? [] : {};
           // 遍歷所有 key
           for (var keys in sourceObj) {
           // 判斷所有屬于自身原型鏈上的 key,而非繼承(上游 )那些
           if (sourceObj.hasOwnProperty(keys)) {
           // 一一復(fù)制過來
           targetObj[keys]=sourceObj[keys];
           }
           }
           return targetObj;
          }
           // ES6 可以用 Object.assign(targeObj, source1,source2,source3) 來實現(xiàn)對象淺拷貝
          復(fù)制代碼
          
          • 深度拷貝
          // 就是把需要賦值的類型轉(zhuǎn)為基本類型(字符串這些)而非引用類型來實現(xiàn)
          // JOSN對象中的stringify可以把一個js對象序列化為一個JSON字符串,parse可以把JSON字符串反序列化為一個js對象
          var deepClone=function(sourceObj) {
           if (!sourceObj || typeof sourceObj !=='object') {
           console.log('您傳入的不是對象!!');
           return;
           }
           // 轉(zhuǎn)->解析->返回一步到位
           return window.JSON
           ? JSON.parse(JSON.stringify(sourceObj))
           : console.log('您的瀏覽器不支持 JSON API');
          };
          復(fù)制代碼
          
          • 深拷貝的考慮點實際上要復(fù)雜的多,詳情看看知乎怎么說

          Q: this對象的理解

          簡言之:誰調(diào)用指向誰,運行時的上下文確定,而非定義的時候就確定;

          強行綁定 this的話,可以用 call,apply,bind,箭頭函數(shù)來來改變this的指向

          這類的文章太多,自行搜索吧。

          Q: 看到你說到 bind,能用 JS簡單的模擬個么?

          Function.prototype.emulateBind=function (context) {
           var self=this;
           return function () {
           return self.apply(context);
           }
          }
          復(fù)制代碼
          

          這個實現(xiàn)很粗糙,更為詳細(xì)全面,考慮周全的(比如參數(shù)的處理什么的),自行谷歌.

          Q:JS 的作用域是什么?有什么特別之處么?

          作用域就是有它自身的上下文區(qū)域(比如函數(shù)內(nèi)),內(nèi)部會有變量聲明提升,函數(shù)聲明提升這些;

          函數(shù)聲明提升優(yōu)于變量聲明提升..

          作用域有全局作用域和塊級作用域(局部,比如用 let 或者單純花括號的);

          作用域會影響this的指向

          坐等補充,我回答的時候,面試大佬只是 嗯..恩,恩,也不知道具體如何

          Q: 怎么解決跨域問題,有哪些方法,

          我一般用這三種,cors,nginx反向代理,jsonp

          • jsonp : 單純的 get 一些數(shù)據(jù),局限性很大,就是利用script標(biāo)簽的src屬性來實現(xiàn)跨域。
          • nginx 反向代理: 主要就是用了nginx.conf內(nèi)的proxy_pass http://xxx.xxx.xxx,會把所有請求代理到那個域名,有利也有弊吧..
          • cors的話,可控性較強,需要前后端都設(shè)置,兼容性 IE10+ ,比如
          • Access-Control-Allow-Origin: foo.example // 子域乃至整個域名或所有域名是否允許訪問
          • Access-Control-Allow-Methods: POST, GET, OPTIONS // 允許那些行為方法
          • Access-Control-Allow-Headers: X-PINGOTHER, Content-Type // 允許的頭部字段
          • Access-Control-Max-Age: 86400 // 有效期

          Q: 對于想攜帶一些鑒權(quán)信息跨域如何走起?比如cookie!

          需要配置下 header Access-Control-Allow-Credentials:true ,具體用法看下面的nginxdemo

          當(dāng)然cros的配置不僅僅這些,還有其他一些,具體引擎吧,.

          若是我們要用 nginx或者 express 配置cors應(yīng)該怎么搞起? 來個簡易版本的

          • nginx
          location / {
           # 檢查域名后綴
           add_header Access-Control-Allow-Origin xx.xx.com;
           add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
           add_header Access-Control-Allow-Credentials true;
           add_header Access-Control-Allow-Headers DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type;
           add_header Access-Control-Max-Age 86400;
          }
          復(fù)制代碼
          
          • express, 當(dāng)然這貨也有一些別人封裝好的 cors中間件,操作性更強,
          let express=require('express');
          let app=express();
          //設(shè)置所有請求的頭部
          app.all('*', (req, res, next)=> {
           res.header("Access-Control-Allow-Origin", "xx.xx.com");
           res.header("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type");
           res.header("Access-Control-Allow-Credentials","true")
           res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
           next();
          });
          復(fù)制代碼
          

          有些還會跟你死磕,除了這些還有其他姿勢么,我說了一個HTML5的postMessage,

          因為真心沒用過,只是以前查閱的時候了解了下,只能大體點下

          這貨用于iframe 傳遞消息居多, 大體有這么兩步步

          • window打開一個實例,傳遞一個消息到一個x域名
          • x 域名下監(jiān)聽message事件,獲取傳遞的消息

          這貨的兼容性沒那么好,而且沒考慮周全下容易遭受 CSRF 攻擊

          Q: 對于XSS 和 CSRF 如何防范

          這里就不說概念性的東西了

          • XSS的防范
          • 我能想到的就是轉(zhuǎn)義<>這些造成代碼直接運行的的標(biāo)簽..輪詢或者正則替換
          • 而面試官說這種的效率最低下,我回來仔細(xì)找了找相關(guān)資料好像沒有更優(yōu)方案,有的留言,
          • 若是有用到 cookie,設(shè)置為http-only,避免客戶端的篡改
          • CSRF的防范一般這幾種
          • 驗證碼,用戶體驗雖然不好,,但是很多場合下可以防范大多數(shù)攻擊
          • 驗證 HTTP Referer 字段,判斷請求來源
          • token加密解密,這種是目前很常用的手段了,

          任何防范都有代價的,比如驗證碼造成的體驗不好,token濫用造成的性能問題,輪詢替換造成的響應(yīng)時間等

          Q: 描述下cookie,sessionStorage,localStorage的差異..

          • cookie : 大小4KB 左右,跟隨請求(請求頭),會占用帶寬資源,但是若是用來判斷用戶是否在線這些挺方便
          • sessionStorage和localStorage大同小異,大小看瀏覽器支持,一般為5MB,數(shù)據(jù)只保留在本地,不參與服務(wù)端交互.
          • sessionStorage的生存周期只限于會話中,關(guān)閉了儲存的數(shù)據(jù)就沒了.
          • localStorage則保留在本地,沒有人為清除會一直保留

          Q: javascript的原型鏈你怎么理解?

          原型鏈算是 JS 內(nèi)一種獨有的機制,

          所有對象都有一個內(nèi)置[[proto]]指向創(chuàng)建它的原型對象(prototype)

          原型鏈的基本用來實現(xiàn)繼承用的

          Q: javascript里面的繼承怎么實現(xiàn),如何避免原型鏈上面的對象共享

          我在寫的時候,用了兩種,一個是 ES5和 ES6的方案

          • ES5:寄生組合式繼承:通過借用構(gòu)造函數(shù)來繼承屬性和原型鏈來實現(xiàn)子繼承父。
           function ParentClass(name) {
           this.name=name;
           }
           ParentClass.prototype.sayHello=function () {
           console.log("I'm parent!" + this.name);
           }
           function SubClass(name, age) {
           //若是要多個參數(shù)可以用apply 結(jié)合 ,解構(gòu)
           ParentClass.call(this, name);
           this.age=age;
           }
           SubClass.prototype=Object.create(ParentClass.prototype);
           SubClass.prototype.constructor=SubClass;
           SubClass.prototype.sayChildHello=function (name) {
           console.log("I'm child " + this.name)
           }
           let testA=new SubClass('CRPER')
           // Object.create()的polyfill
           /*
           function pureObject(o){
           //定義了一個臨時構(gòu)造函數(shù)
           function F() {}
           //將這個臨時構(gòu)造函數(shù)的原型指向了傳入進(jìn)來的對象。
           F.prototype=obj;
           //返回這個構(gòu)造函數(shù)的一個實例。該實例擁有obj的所有屬性和方法。
           //因為該實例的原型是obj對象。
           return new F();
           }
           */
          復(fù)制代碼
          
          • ES6: 其實就是ES5的語法糖,不過可讀性很強..
           class ParentClass {
           constructor(name) {
           this.name=name;
           }
           sayHello() {
           console.log("I'm parent!" + this.name);
           }
           }
           class SubClass extends ParentClass {
           constructor(name) {
           super(name);
           }
           sayChildHello() {
           console.log("I'm child " + this.name)
           }
           // 重新聲明父類同名方法會覆寫,ES5的話就是直接操作自己的原型鏈上
           sayHello(){
           console.log("override parent method !,I'm sayHello Method")
           }
           }
           let testA=new SubClass('CRPER')
          復(fù)制代碼
          

          Q: ES6+你熟悉么,用過哪些特性?

          • 箭頭函數(shù)
          • 類及引入導(dǎo)出和繼承( class/import/export/extends)
          • 字符串模板
          • Promise
          • let,const
          • async/await
          • 默認(rèn)參數(shù)/參數(shù)或變量解構(gòu)裝飾器
          • Array.inclueds/String.padStart|String.padEnd/Object.assign

          Q: let 和 const 有啥差異?

          • let 會產(chǎn)生塊級作用域,不會造成變量提升,無法重新聲明(但可以重新賦值);
          • const
          • 是常量,若是基本數(shù)據(jù)類型,具有不變性(無法重新賦值改動)
          • 引用值可以調(diào)整內(nèi)部值(可能設(shè)計的時候沒有考慮周全!

          Q: async和await的用途?

          • 讓 promise 的異步變成同步運行成了可能,await 可以等到 promise 執(zhí)行完畢

          Q: 箭頭函數(shù)的this指向誰?

          肯定很多小伙伴會說指向局部方法內(nèi)!!答案是錯誤的,

          箭頭函數(shù)所改變的并非把 this 局部化,而是完全不把 this 綁定到里面去;

          就是 this 是取自外部的上下級作用域(但是又不是常規(guī) function的語法糖)..

          因為箭頭函數(shù)里并不支持 var self=this 或者 .bind(this) 這樣的寫法。

          Q: 問的時候你用過靜態(tài)方法,靜態(tài)屬性,私有變量么?

          靜態(tài)方法是ES6之后才有這么個玩意,有這么些特點

          • 方法不能給 this引用,可以給類直接引用
          • 靜態(tài)不可以給實例調(diào)用,比如 let a=new ParentClass=> a.sayHello() 會拋出異常
          • 父類靜態(tài)方法,子類非static方法沒法覆蓋父類
          • 靜態(tài)方法可以給子類繼承
          • 靜態(tài)屬性可以繼承也可以被修改

          看下面的代碼..

           class ParentClass {
           constructor(name) {
           this.name=name;
           }
           static sayHello() {
           console.log("I'm parent!" + this.name);
           }
           static testFunc(){
           console.log('emm,Parent test static Func')
           }
           }
           class SubClass extends ParentClass {
           constructor(name) {
           super(name);
           }
           sayChildHello() {
           console.log("I'm child " + this.name)
           }
           static sayHello() {
           console.log("override parent method !,I'm sayHello Method")
           }
           static testFunc2() {
           console.log(super.testFunc() + 'fsdafasdf');
           }
           }
           ParentClass.sayHello(); // success print
           let a=new ParentClass('test');
           a.sayHello() // throw error
           SubClass.sayHello(); // 同名 static 可以繼承且覆蓋
           SubClass.testFunc2(); // 可以繼承
           let testA=new SubClass('CRPER');
          復(fù)制代碼
          

          私有變量可以用WeakMap模擬,也能用語義化的下劃線,亦或者symbol,

          所以回來只是找了下相關(guān)的資料,發(fā)現(xiàn)有一個比較好的模擬方案,就是WeakMap;

          WeakMap可以避免內(nèi)存泄露,當(dāng)沒有被值引用的時候會自動給內(nèi)存寄存器回收了.

          const _=new WeakMap(); // 實例化,value 必須為對象,有 delete,get,has,set四個方法,看名字都知道了
          class TestWeakMap {
           constructor(id, barcode) {
           _.set(this, { id,barcode });
           }
           testFunc() {
           let { id,barcode }=_.get(this); // 獲取對應(yīng)的值
           return { id,barcode };
           }
          }
          復(fù)制代碼
          

          當(dāng)然你也可以用Symbol來實現(xiàn)一個私有變量,這也是一個好法子

          Q: 談?wù)勀銓?Promise 的理解? 和 ajax 有關(guān)系么?

          Promise和ajax沒有半毛錢直接關(guān)系.promise只是為了解決"回調(diào)地獄"而誕生的;

          平時結(jié)合 ajax是為了更好的梳理和控制流程,這里我們簡單梳理下..

          Promise有三種狀態(tài),Pending/resolve()/reject();

          一些需要注意的小點,如下

          • 在 Pending 轉(zhuǎn)為另外兩種之一的狀態(tài)時候,狀態(tài)不可在改變..
          • Promise的 then為異步.而(new Promise())構(gòu)造函數(shù)內(nèi)為同步
          • Promise的catch不能捕獲任意情況的錯誤(比如 then 里面的setTimout內(nèi)手動拋出一個Error)
          • Promise的then返回Promise.reject()會中斷鏈?zhǔn)秸{(diào)用
          • Promise的 resolve若是傳入值而非函數(shù),會發(fā)生值穿透的現(xiàn)象
          • Promise的catch還是then,return的都是一個新的 Promise(在 Promise 沒有被中斷的情況下)

          Promise 還有一些自帶的方法,比如race,all,前者有任一一個解析完畢就返回,后者所有解析完畢返回,

          實現(xiàn)一個延時的 promise 函數(shù), 可以用async和await

          const delay=(time)=> new Promise((resolve,reject)=>{
           setTimeout(resolve,time)
          })
          // test
          let testRun=async function(){
           console.log(1);
           await delay(2000);
           console.log('我兩秒后才觸發(fā)',3)
          }
          // 1=> Promise=> 3
          復(fù)制代碼
          

          以下這段代碼的運行結(jié)果是什么?

          var test=new Promise((resolve,reject)=>{
           resolve();
          });
          test
           .then(data=> {
           // promise start
           console.log('promise first then : ', data);
           return Promise.resolve(1); // p1
           })
           .then(data=> {
           // promise p1
           console.log('get parent(p1) resolve data : ', data);
           return Promise.reject(new Error('哎呀,中斷了,你能奈我何!')); // p2
           })
           .then(data=> {
           // promise p2
           console.log('result of p2: ', data);
           return Promise.resolve(3); // p3
           })
           .catch(err=> {
           console.log('err: ', err);
           return false;
           });
          // promise first then : undefined
          // get parent(p1) resolve data : 1
          // err: Error: 哎呀,中斷了,你能奈我何!
          // 這里在 then 返回 Promise.reject()的時候已經(jīng)中斷了鏈?zhǔn)秸{(diào)用.直接給 catch捕獲到
          復(fù)制代碼
          

          別急,假如你不管有沒有捕獲到錯誤,最后再執(zhí)行一個回調(diào)函數(shù)如何實現(xiàn)?

          這里說的就是類似try..catch..finally,給Promise實現(xiàn)一個 finally;

          // finally比較好加,按照現(xiàn)在社區(qū)的討論,finally的特點如下:
          // url : https://www.v2ex.com/t/205715
          //1. 不接收任何參數(shù),原來的value或者Error在finally里是收不到的
          //2. 處理后不影響原Promise的狀態(tài),該reject還是reject,該resolve還是resolve
          //3. 不影響Promise向后傳遞的傳,resolve狀態(tài)還是傳遞原來的value,reject狀態(tài)還是傳遞原來的Error
          Promise.prototype.finally=function (callback) {
           let P=this.constructor; // 這里拿到的是 Promise 的構(gòu)造函數(shù)
           //不管前面的 Promise 是fulfilled還是rejected,都會執(zhí)行回調(diào)函數(shù)callback。
           return this.then(
           value=> P.resolve(callback()).then(()=> value),
           reason=> P.resolve(callback()).then(()=> { throw reason })
           );
          };
          // 用法很簡單,就是可以傳入一個回調(diào)函數(shù)..
          // https://developers.google.com/web/updates/2017/10/promise-finally
          // 這個 url 中說了 node 及 chrome 的哪些版本已經(jīng)實現(xiàn)了 finally 及用法
          // ES 2018已經(jīng)把 finally 追加到 promise 的原型鏈中..
          復(fù)制代碼
          
          • <<Promise 必知必會(十道題)>>: 有助于你更加深刻的了解 promise 的運行情況
          • 關(guān)于 Promise 的 9 個提示
          • 更多的Promise 詳情可以參考<<JavaScript Promise迷你書(中文版)>>;

          Q: 談?wù)勀銓?TCP 的理解;

          Q: TCP 是在哪個OSI 的哪個層!通訊過程是全雙工還是半雙工(單工)?

          A: 傳輸層,全雙工

          Q: TCP的通訊的過程是怎么樣的!

          A: 整個過程是三次握手,四次揮手..

          Q: 你說的沒錯,說說整個過程如何?

          A: 舉個栗子,我把 TCP 比做兩個人用對講機溝通(大白話)..三次握手就是.A1(吼叫方,客戶端)想要呼叫 A2(控制室的某某,服務(wù)端)..

          A1對著對講機說"over over ,聽到請回答"(第一次,請求應(yīng)答) ,

          A2收到回應(yīng)"收到收到,你說"(第二次,確認(rèn)應(yīng)答)

          A1開始巴拉巴拉個不停而 A2沒拒絕(第三次,通訊建立)

          而四次揮手則是兩者確認(rèn)互相傾述完畢的過程..

          A1說:"控制室,報告完畢了"(第一次揮手)

          A2說:"知道了,那么你廢話說完就好好聽我指揮,.巴拉巴拉.."(第二次揮手)

          A1此時等待控制室說完畢,而控制室等回應(yīng)(第三次揮手)

          等到 A1回饋控制室確認(rèn)都知道完畢了..(第四次揮手),

          以上都是瞎掰,可能有些地方描述不當(dāng),笑笑就好了

          TCP沒有百分百建立成功的,會造成鏈接失敗的情況有很多..

          比如長時間沒應(yīng)答(A1吼了半天沒有反應(yīng)或者 A2應(yīng)答了而 A1不再鳥它)..亦或者丟包(對講機也沒了);

          TCP 協(xié)議相關(guān)的文章網(wǎng)上很多,若是要更加全面的了解該協(xié)議請自行引擎..

          我建議閱讀<<TCP-IP詳解卷1~卷3>>,這個是網(wǎng)絡(luò)圣經(jīng),很厚,我只看了一丟丟..

          Q: TCP 你了解了,那么 OSI 七層協(xié)議和五層網(wǎng)絡(luò)架構(gòu)應(yīng)該知道吧?

          對于這類的問題我也只能大體點了下,畢竟不是專攻網(wǎng)絡(luò)這塊的,

          OSI 七層涵蓋:物理層,數(shù)據(jù)鏈路層,網(wǎng)絡(luò)層,傳輸層,會話層,表示層,應(yīng)用層;

          五層模型就是"會話,表示,應(yīng)用層"同為一層;

          Q: DNS 的大體的執(zhí)行流程了解么,屬于哪個層級?工作在哪個層級?

          DNS 屬于應(yīng)用層協(xié)議, 至于TCP/UDP哪一層上面跑,看情況 , 大體的執(zhí)行流程是這樣的;

          DNS 默認(rèn)端口是53,走 UDP

          1. 優(yōu)先讀取瀏覽器緩存
          2. 其次系統(tǒng)的緩存
          3. 都沒有的情況下,找本地hosts文件(比如你寫了映射關(guān)系優(yōu)先尋找)
          4. 再沒有的情況找最近的域名解析服務(wù)器
          5. 再沒有則擴(kuò)大訪問,最終找到根服務(wù)器,還是沒有就失敗了..

          DNS 的解析的幾個記錄類型需要了解:

          • A: 域名直接到 IP
          • CNAME: 可以多個域名映射到一個主機,類似在 Github Page就用 CNAME 指向
          • MX: 郵件交換記錄,用的不多,一般搭建郵件服務(wù)器才會用到
          • NS: 解析服務(wù)記錄,可以設(shè)置權(quán)重,指定誰解析
          • TTL: 就是生存時間(也叫緩存時間),一般的域名解析商都有默認(rèn)值,也可以人為設(shè)置
          • TXT: 一般指某個主機名或域名的說明

          回來我找下相關(guān)的資料,有興趣的可以深入了解下,傳送門如下:

          • 梳理Linux下OSI七層網(wǎng)絡(luò)與TCP/IP五層網(wǎng)絡(luò)架構(gòu)
          • TCP/IP(六)應(yīng)用層(DNS和HTTP協(xié)議)
          • DNS域名解析解剖

          Q: HTTP 和 HTTPS 有何差異? 聽說過 SPDY 么?

          我只是粗淺的回答了下,

          HTTP相對于 HTTPS來說,速度較快且開銷較小(沒有 SSL/TSL) 對接,默認(rèn)是80端口;

          HTTP容易遭受域名劫持,而HTTPS相對來說就較為安全(加密),默認(rèn)端口為443。

          HTTP是明文跑在 TCP 上.而HTTPS跑在SSL/TLS應(yīng)用層之下,TCP上的

          Q: 那么 HTTPS中的TLS/SSL是如何保護(hù)數(shù)據(jù)的,

          一般有兩種形式,非對稱加密,生成公鑰和私鑰,私鑰丟服務(wù)器,公鑰每次請求去比對驗證;

          更嚴(yán)謹(jǐn)?shù)牟捎?CA(Certificate Authority),給密鑰簽名,.

          Q: 你說到對稱加密和非對稱加密,能說說整個流程如何運轉(zhuǎn)的么(HTTPS)

          • 對稱加密:
          • 雙方都有同樣的密鑰,每次通訊都要生成一個唯一密鑰,速度很快
          • 安全性較低且密鑰增長的數(shù)量極快
          • 非對稱加密(一般用 RSA)
          • 安全性很高,對資源消耗很大(CPU),目前主流的加密算法(基本用于交換密鑰或簽名,而非所有通訊內(nèi)容)
          • CA(數(shù)字簽名):
          • 這個是為了防止中間人給偷換了造成數(shù)據(jù)被竊取而誕生的
          • 用一些權(quán)威機構(gòu)頒布的算法來簽名,權(quán)威機構(gòu)做中間人,通訊過程都會跟機構(gòu)核對一遍

          懂得真心不多,回來找了下相關(guān)資料,有興趣可以點擊看看;

          • 深入揭秘HTTPS安全問題&連接建立全過程
          • 深入理解 https 通信加密過程:口語化 " : 看了上面那篇文章來看下面,會清晰很多

          Q: SPDY 聽說過么.什么來的?

          谷歌推行一種協(xié)議(HTTP 之下SSL之上[TCP]),可以算是HTTP2的前身,有這么些優(yōu)點

          • 壓縮數(shù)據(jù)(HEADER)
          • 多路復(fù)用
          • 優(yōu)先級(可以給請求設(shè)置優(yōu)先級)

          而這些優(yōu)點基本 HTTP2也繼承下來了..

          Q: 你對 HTTP 的狀態(tài)嗎了解多少,

          這里列舉一丟丟常見的..

          • 1XX: 一般用來判斷協(xié)議更換或者確認(rèn)服務(wù)端收到請求這些
          • 100: 服務(wù)端收到部分請求,若是沒有拒絕的情況下可以繼續(xù)傳遞后續(xù)內(nèi)容
          • 101: 客戶端請求變換協(xié)議,服務(wù)端收到確認(rèn)
          • 2xx: 請求成功,是否創(chuàng)建鏈接,請求是否接受,是否有內(nèi)容這些
          • 200: (成功)服務(wù)器已成功處理了請求。
          • 201: (已創(chuàng)建)請求成功并且服務(wù)器創(chuàng)建了新的資源。
          • 202: (已接受)服務(wù)器已接受請求,但尚未處理。
          • 204: (無內(nèi)容)服務(wù)器成功處理了請求,但沒有返回任何內(nèi)容。
          • 3XX: 一般用來判斷重定向和緩存
          • 301: 所有請求已經(jīng)轉(zhuǎn)移到新的 url(永久重定向),會被緩存
          • 302: 臨時重定向,不會被緩存
          • 304: 本地資源暫未改動,優(yōu)先使用本地的(根據(jù)If-Modified-Since or If-Match去比對服務(wù)器的資源,緩存)
          • 4XX: 一般用來確認(rèn)授權(quán)信息,請求是否出錯,頁面是否丟失
          • 400: 請求出錯
          • 401: 未授權(quán),不能讀取某些資源
          • 403: 阻止訪問,一般也是權(quán)限問題
          • 404: 頁面丟失,資源沒找到
          • 408: 請求超時
          • 415: 媒介類型不被支持,服務(wù)器不會接受請求。
          • 5XX: 基本都是服務(wù)端的錯誤
          • 500: 服務(wù)端錯誤
          • 502: 網(wǎng)關(guān)錯誤
          • 504: 網(wǎng)關(guān)超時

          Q: HTTP的請求報文是怎么樣的,能大體的說下么?

          HTTP 的請求報文=請求行 + 請求頭 + 請求體;

          • 請求行: 這個好理解就是訪問的方法+ 協(xié)議+ 訪問的 URL 構(gòu)成
          • 請求頭: 這個也好理解,比如 accept,content-type,user-agent這類值鍵對,服務(wù)端可以直接讀取的
          • 請求體: 比如 POST 提交的一個表單,我們編碼后放在上面需要傳遞的

          想深入了解的具體引擎搜索

          Q: 請求報文知道,那你說說cookie是如何跟隨請求的?

          Cookie 就是保存在 HTTP 協(xié)議的請求或者應(yīng)答頭部(Cookie 是由服務(wù)端生成),這樣一路漂泊,

          Q: Cookie 隔離是什么,如何做;

          cookie 隔離就是降低 header 的數(shù)據(jù)包含,以達(dá)到加快訪問速度的目的

          方案: 靜態(tài)資源丟 CDN或者非主域來加載

          Q: 瀏覽器緩存和服務(wù)端的緩存控制你了解多少,說說看?

          • Last-Modified:
          • 第一次請求資源從服務(wù)器拉取的會自動帶上該屬性
          • 第二次請求會跟服務(wù)端比對If-Modified-Since的時間,沒變動則使用本地的(狀態(tài)304)
          • 結(jié)合Expires(過期時間:緩存的載止時間),跟隨請求一起發(fā)出..資源沒過期拿本地,否則重新請求
          • Cache-control 是 HTTP1.1的東西,判斷資源過期結(jié)合max-age來替代Expires[http 1.0]
          • Etag:
          • 第一次請求url 時候會給服務(wù)器上標(biāo)記(一串字符串)
          • 第二次請求時候會比對服務(wù)端的If-None-Match,沒有改動依舊拿緩存(304)

          Q: 幾個短而讓我印象深刻的題

          if(!("a" in window)){
           var a=10;
          }
          console.log(a); // undefined
          // !("a" i n window) , 返回 true
          /*
           var a;
          if(!("a" in window)){
           a=10;
          }
          */
          // 變種題
          (function(){
           var x=c=b={a:1}
          })()
          console.log(x.a); // error , x is not defined
          console.log(c,b) // {a: 1} {a: 1}
          復(fù)制代碼
          var count=0;
          console.log(typeof count==="number"); // true , 這個不用解釋了
          console.log(!!typeof count==="number"); // false
          // 這里涉及到就是優(yōu)先級和布爾值的問題
          // typeof count 就是字符串"number"
          // !!是轉(zhuǎn)為布爾值(三目運算符的變種),非空字符串布爾值為 true
          // 最后才===比較 , true==="number" , return false
          復(fù)制代碼
          (function(){
           var a=b=3;
          })()
          console.log(typeof a==="undefined"); // false
          console.log(typeof b==="undefined"); // false
          // 這里涉及的就是立即執(zhí)行和閉包的問題,還有變量提升,運算符執(zhí)行方向(=號自左向右)
          // 那個函數(shù)可以拆成這樣
          (function()
           var a; /* 局部變量,外部沒法訪問*/
           b=3; /* 全局變量,so . window.b===3 , 外部可以訪問到*/
           a=b;
          })()
          // 若是改成這樣,這道題應(yīng)該是對的
          console.log(typeof b==="number" && b===3
          ); // true
          復(fù)制代碼
          function foo(something){
           this.a=something;
          }
          var obj1={
           foo:foo
          };
          var obj2={};
          obj1.foo(2)
          console.log(obj1.a) // 2 ,此時的 this 上下文還在 obj1內(nèi),若是 obj1.foo 先保存當(dāng)做引用再執(zhí)行傳參,則上下文為 window
          obj1.foo.call(obj2,3); // 用 call 強行改變上下文為 obj2內(nèi)
          console.log(obj2.a); // 3
          var bar=new obj1.foo(4); // 這里產(chǎn)生了一個實例
          console.log(obj1.a); // 2
          console.log(bar.a); // 4; new的綁定比隱式和顯式綁定優(yōu)先級更高
          復(fù)制代碼
          function fn(){
           alert(a);
           var a=200;
           alert(a);
          }
          fn(); // undefined / 200 ; 涉及變量提升
          alert(a); // undefined
          var a;
          alert(a); // undefined
          var a=300;
          alert(a); // 300
          復(fù)制代碼
          var obj1={
           name:'obj1',
           fn:function(){
           console.log(this.name);
           }
          };
          var obj2={name:'obj2'};
          var obj3={name:'obj3'};
          // 這道題主要涉及的是 this 指向的問題..
          obj1.fn(); // obj1
          var newFn=obj1.fn;
          newFn(); // undefined, this 指向 window
          newFn.call(obj2);// obj2, this 指向 obj2
          obj3.fn=newFn;
          /*
          ? (){
           console.log(this.name);
           }
          */
          obj3.fn(); // 這里指向的是 obj3 .所以輸出 obj3
          復(fù)制代碼
          // 這道題來作為筆試題很繞,因為要回答的答案很多(腦海構(gòu)思)..反正我是遇到了..
          // 這道題主要考核的是對原型鏈繼承這塊的理解
          function Parent(){
           this.a=1;
           this.b=[1,2,this.a];
           this.c={demo:5};
           this.show=function(){
           console.log(this.a + '' + this.c.demo + ':' + this.b)
           }
          }
          function Child(){
           this.a=2;
           this.change=function(){
           this.b.push(this.a);
           this.a=this.b.length;
           this.c.demo=this.a++;
           }
          }
          Child.prototype=new Parent();
          var parent=new Parent();
          var child1=new Child();
          var child2=new Child();
          child1.a=11;
          child2.a=12;
          // 這前面幾個還算簡單,繼續(xù)看下去
          parent.show(); // 15:1,2,1
          // 因為 Child 自身沒有 show 的方法,所以往原型鏈的上游找;
          // 找到父類的,this 因為沒更改,所以輸出結(jié)果如下
          child1.show(); // 115:1,2,1
          child2.show(); // 125:1,2,1
          child1.change(); // 改變一些數(shù)據(jù),沒有輸出
          child2.change(); // +1
          parent.show(); // 15:1,2,1
          child1.show(); // 55:1,2,1,11,12
          child2.show(); // 65:1,2,1,11,12
          復(fù)制代碼
          // 這道題也很繞,函數(shù)遞歸調(diào)用的
          function test(a,b){
           console.log(b);
           return {
           test:function(c){
           return test(c,a);
           }
          };
          // 這道題的理解,拆成這樣就好理解了
          /*function test(a,b){
           console.log("a:"+a,"b:"+b);
           return {
           test:function(c){
           console.log("a:"+a,"b:"+b,"c"+c);
           return test(c,a);
           }
           }
          }*/
          var a=test(100); // undefined, 這個是不言而喻的;
          a.test(200); // 100;
          a.test(300); // 100;
          var b=test(101).test(201).test(301); // undefined/101/201
          var c=test(102).test(202); // undefined / 102
          c.test(302); // 202
          復(fù)制代碼
          

          Q:有字符串 var test='abc345efgabcab'; 請根據(jù)提示實現(xiàn)對應(yīng)要求

          • 去掉字符串中的 a,b,c 字符 ,形成結(jié)果'345efg';
          test.replace(/[abc]/g,''); // "345efg"
          復(fù)制代碼
          
          • 將字符串的數(shù)字用括號括起來, 形成結(jié)果: abc[3][4][5]efg,.'
          test.replace(/\d/g,'[$&]'); // "abc[3][4][5]efgabcab"
          // 若是有分組則按照$1, $2, $3的形式進(jìn)行引用,而 $& 則表示的是整個正則表達(dá)式匹配的內(nèi)容。
          復(fù)制代碼
          
          • 將字符串中的每個數(shù)字的值分別乘以2,輸出:'abc6810,.'
          var temp=test.split('').map(function(item){
           return /^\d$/.test(item) ? item * 2 : item;
          }).join('');
          // "abc6810efgabcab"
          復(fù)制代碼
          

          Q: 使用不少于三種方式替換文本"dream"改成"package",提供字符串"I have a dream";

          • 正則替換
          // 這是最簡單的代碼量了..
          var str="I have a dream";
          str.replace(/dream/g,"package");
          // 不用正則也可以直接字符串替換
          str.replace("dream","package")
          復(fù)制代碼
          
          • 數(shù)組遍歷更改
          // 很直白的大腦回路
          var str="I have a dream";
          str.split(" ").map(function(item){
           return item==="dream" ? item="package":item;
          }).join(" ");
          復(fù)制代碼
          
          • 數(shù)組查詢切割法
          var str="I have a dream";
          var tempArr=str.split(" "); // ["I", "have", "a", "dream"]
          var removeIndex=tempArr.indexOf('dream'); // 3
          tempArr.splice(removeIndex,1,"package");
          var transStr=tempArr.join(" "); // "I have a package";
          復(fù)制代碼
          

          這類東東弄成數(shù)組還是挺好弄的

          這個是留言區(qū)小伙伴提供的方法..大同小異,如下;

          // 源代碼
          // 字符串也有數(shù)組的 slice 以及 concat 的方法..思路和數(shù)組差不多
          var str='I haved a dream';
          str.indexOf('dream') !==-1 ? str.slice(0,str.indexOf('dream')).concat('package'):str;
          復(fù)制代碼
          

          Q: 還有一道題目是涉及事件循環(huán),執(zhí)行優(yōu)先權(quán)的..

          就是 macrotask和microtask 相關(guān)的, 具體記不起來了,那時候給了答案雖然對了。

          要說出所以然,給秀了一臉,回來找了下相關(guān)的資料;

          • JavaScript 運行機制詳解:再談Event Loop
          • 深入理解事件循環(huán)和異步流程控制
          • 所有你需要知道的關(guān)于完全理解 Node.js 事件循環(huán)及其度量

          Q: 你對基礎(chǔ)算法這塊掌握的如何,.

          來,這紙給你,寫個快排試試,

          // 快排的大體思路是這樣的,
          // 找個中位值,從原數(shù)組切割出來,
          // 剩下的作為兩個數(shù)組,每次都去比較;
          // 直到遞歸的結(jié)果出來, 平均復(fù)雜度O(nlog n)
          function quickSort(arr) {
           //如果數(shù)組長度<=1,則直接返回
           if (arr.length <=1) {
           return arr;
           }
           // 中間位(基準(zhǔn))取長度的一半向下取整
           var pivotIndex=Math.floor(arr.length / 2);
           //把中間位從原數(shù)組切割出來, splice 會改變原數(shù)組!!!!
           var pivot=arr.splice(pivotIndex, 1)[0];
           //定義兩個空數(shù)組來存放比對后的值
           var left=[];
           var right=[];
           //比基準(zhǔn)小的放在left,比基準(zhǔn)大的放在right
           for (var i=0 , j=arr.length; i < j; i++) {
           if (arr[i] <=pivot) {
           left.push(arr[i]);
           } else {
           right.push(arr[i]);
           }
           }
           //遞歸下去 arr=[ left , pivot , right]
           // 怎么個遞歸法,就是比對后的數(shù)組還是會重復(fù)之前的取基準(zhǔn)再切開比較..直到最后沒有可以切了
           return quickSort(left).concat([pivot], quickSort(right));
          }
          復(fù)制代碼
          

          Q: 寫一個二分法查找

          // 二分法跟快排的思路差不多,對半比較
          // 這個只用于排序好數(shù)組內(nèi)的查詢,高低位都知道的情況下
          function binSearch(target, arr, start, end) {
           var start=start || 0; // 允許從什么位置開始,下標(biāo)
           var end=end || arr.length - 1; // 什么位置結(jié)束,下標(biāo)
           start >=end ? -1 : ''; // 沒有找到,直接返回-1
           var mid=Math.floor((start + end) / 2); // 中位下標(biāo)
           if (target==arr[mid]) {
           return mid; // 找到直接返回下標(biāo)
           } else if (target > arr[mid]) {
           //目標(biāo)值若是大于中位值,則下標(biāo)往前走一位
           return binSearch(target, arr, start, mid - 1);
           } else {
           //若是目標(biāo)值小于中位值,則下標(biāo)往后退一位
           return binSearch(target, arr, mid + 1, end);
           }
          }
          // binSearch(5,[1,2,3,4,5,6,7,8])=> 4
          // 無序的數(shù)組則需要先排序好數(shù)組,否則會堆棧溢出(死循環(huán))
          復(fù)制代碼
          

          這類的文章很多,有興趣的可以閱讀下面的一些文章

          傳送門:

          • <<十大經(jīng)典排序算法總結(jié)(JavaScript描述>>
          • JavaScript數(shù)據(jù)結(jié)構(gòu)和算法
          • javascript 常見排序算法

          Q: 設(shè)計模式你了解多少?

          • Javascript常用的設(shè)計模式詳解
          • js-design-pattern

          Q: 思維拓展題: 你有兩個玻璃球,有個100米的高樓,求玻璃球在哪個樓層扔下會碎(用的次數(shù)最少);

          問題的要點: 玻璃球碎(有限個數(shù)) ,確定樓層數(shù) , 最少次數(shù)=> 就是求最優(yōu)的公式

          面試大佬說這個還可以,那就暫且告一段落

          ,回來用萬能的搜索引擎找了下..最優(yōu)方案+最少次數(shù)需要考慮的東西很多,沒那么簡單

          傳送門: 知乎有人討論了這個問題;

          但是高數(shù)還老師了..這種帖子看的一臉懵逼,.抽空再好好研究下

          Q: 你對優(yōu)化這塊了解多少?

          大體常見的手段了解.

          客戶端著手

          • 壓縮代碼(JS/CSS),壓縮圖片
          • 合并一些小圖片(css sprite)
          • 若是打包的代碼盡可能切割成多個 chunk,減少單一 chunk過大
          • 靜態(tài)文件采用 cdn 引入
          • HTTP的緩存頭使用的合理
          • 減小第三方庫的依賴
          • 對于代碼應(yīng)該考慮性能來編寫,比如使用requestAnimationFrame繪制動畫,盡可能減少頁面重繪(DOM 改變)
          • 漸進(jìn)升級,引入preload這些預(yù)加載資源
          • 看情況用service worker來緩存資源(比如移動端打算搞 PWA)

          服務(wù)端著手

          • 帶寬,域名解析, 多域名解析等
          • 頁面做服務(wù)端渲染,減小對瀏覽器的依賴(不用客戶端解析)
          • 漸進(jìn)升級,比如引入 HTTP2(多路復(fù)用,頭部壓縮這些可以明顯加快加載速度)

          當(dāng)然,這是這些都是很片面的點到,實際工作中去開展要復(fù)雜的多;

          比如我們要多個維度去考慮的話,要去優(yōu)化 DOM 的繪制時間,資源的加載時間,域名解析這些;

          要全面的優(yōu)化一個項目是一個大工程,

          Q: MySQL有哪些索引類型? 索引的數(shù)據(jù)結(jié)構(gòu)儲存方式? MySQL和 MongoDB的差異

          MySQL索引類型:

          • 普通索引: 就普通的類型
          • 唯一索引: 代表索引的值唯一不重復(fù)(允許有空值),相對于上面多了個UNIQUE
          • 主鍵索引:(創(chuàng)建表的跟隨創(chuàng)建,唯一索引,不允許有空值)
          • 組合索引(就是將多個字段都建立到一個索引)

          索引有利有弊,用的好加快查詢速度,濫用索引會造成大量磁盤空間占用,維護(hù)性也會增多; 索引不會包含null的列;

          索引的數(shù)據(jù)結(jié)構(gòu)儲存方式,我只簡單了解過B-Tree

          至于MySQL 和 MongoDB的差異;

          前者是關(guān)系型數(shù)據(jù)庫, 后者非關(guān)系型數(shù)據(jù)庫(數(shù)據(jù)是以文檔的方式儲存,值為 key-value);

          MySQL應(yīng)用層面很廣,有事務(wù)系統(tǒng)這些,鏈表查詢這些都很方便.經(jīng)常作為很多系統(tǒng)的主力數(shù)據(jù)庫

          而MongoDB作為NoSQL,雖然有些層面不如 MySQL,但是應(yīng)用層面也挺廣, 比如結(jié)合前端做一些用戶的概要信息的維護(hù),一些緩存信息的維護(hù).

          em,.后端了解不多,也能點到即止,.大學(xué)的時候?qū)W過一些..都差不多還給老師,.

          Q: JS時間分段

          給定一個時間段和步長,枚舉該時間段內(nèi)步長的劃分

          例如:時間段3:00-5:00,步長為20分鐘

          那么返回的數(shù)組為

          ['3:00-3:20', '3:20-3:40',.]等

          這類問題,一般都要先梳理好思路再來寫;

          • 給定字符串時間段,切割,轉(zhuǎn)換為分鐘
          • 跨日及跨時問題
          // 這個東東我的小伙伴也寫出來了.我的是在它的解答方式上加以注釋和對參數(shù)的判斷做了考慮
          // 他的解法方案在他的 github 上 https://github.com/lyh2668/blog/issues/1 , by lyh2668
          // 方便一些小伙伴的理解,以下代碼包含ES6的姿勢(參數(shù)默認(rèn)值,剪頭函數(shù))
          let inputDateRange=(date, step=30, separator='-')=> {
           let startTime, endTime; // 開始時間和結(jié)束時間
           if (Object.prototype.toString.call(date)==='[object String]') {
           date=date.trim(); // 去除兩邊的空格
           var tempDate='';
           if (separator) {
           tempDate=date.split(separator);
           } else {
           if (date.indexOf('-') !==-1) {
           tempDate=date.split('-');
           } else if (date.indexOf('~')) {
           tempDate=date.split('~');
           } else {
           console.log('您傳入的也許不是一個時間段!!!');
           }
           }
           startTime=time2min(tempDate[0]); // 傳入的開始時間
           endTime=time2min(tempDate[1]); //傳入的結(jié)束時間
           } else if (Object.prototype.toString.call(date)==='[object Array]') {
           if (date.length===2) {
           startTime=time2min(date[0]); // 傳入的開始時間
           endTime=time2min(date[1]); //傳入的結(jié)束時間
           }
           } else {
           console.log('您傳入的也許不是一個時間段!!!');
           }
           // 傳入的 step 是否為數(shù)字,否則截圖數(shù)字部分轉(zhuǎn)化
           // 為什么和 NaN 比較(自身不等性),若是傳入的連正則都沒法識別,那只能給默認(rèn)值了
           Object.prototype.toString.call(step)==='[object Number]'
           ? (step=parseInt(step, 10))
           : parseInt(step.replace(/[W\s\b]/g, ''), 10)===NaN
           ? (step=parseInt(step.replace(/[W\s\b]/g, ''), 10))
           : (step=30);
           // 若是開始時間大于結(jié)束時間則結(jié)束時間往后追加一天
           startTime > endTime ? (endTime +=24 * 60) : '';
           let transformDate=[]; // 儲存轉(zhuǎn)換后的數(shù)組,時間分段
           // 開始遍歷判斷,用 while
           while (startTime < endTime) {
           // 如果開始時間+步長大于結(jié)束時間,則這個分段結(jié)束,否則結(jié)束時間是步長遞增
           let right=startTime + step > endTime ? endTime : startTime + step;
           transformDate.push(`${min2time(startTime)}-${min2time(right)}`);
           startTime +=step; // 步長遞增
           }
           return transformDate;
          };
          // 時間轉(zhuǎn)化為分鐘
          let time2min=time=> {
           // 獲取切割的
           time.indexOf(':') ? (time=time.trim().split(':')) : '';
           return time[0] * 60 + parseInt(time[1]); // 返回轉(zhuǎn)化的分鐘
          };
          // 分鐘轉(zhuǎn)會字符串時間
          let min2time=minutes=> {
           let hour=parseInt(minutes / 60); // 返回多少小時
           let minute=minutes - hour * 60; // 扣除小時后剩余的分鐘數(shù)
           hour >=24 ? (hour=hour - 24) : ''; // 若是大于等于24小時需要扣除一天得到所剩下的小時
           minute < 10 ? (minute='0' + minute) : ''; // 小于10的都要補零
           hour < 10 ? (hour='0' + hour) : ''; // 小于10的都要補零
           return `${hour}:${minute}`;
          };
          // test ,支持字符串傳入時間段
          inputDateRange('3:00-5:00','20d'); // ["03:00-03:20", "03:20-03:40", "03:40-04:00", "04:00-04:20", "04:20-04:40", "04:40-05:00"]
          // 亦或者數(shù)組傳入
          inputDateRange(['3:00','5:00'],'45df.3d'); // ["03:00-03:45", "03:45-04:30", "04:30-05:00"]
          // step 支持?jǐn)?shù)字亦或者帶特殊字符的數(shù)字
          inputDateRange(['6:00','8:00'],'55df.3d'); // ["06:00-06:55", "06:55-07:50", "07:50-08:00"]
          inputDateRange('3:00-5:00',60); // ["03:00-04:00", "04:00-05:00"]
          復(fù)制代碼
          
          • JS不靠譜系列之枚舉出時間段和對應(yīng)的分鐘數(shù)

          Q: Vue-Router的兩種模式主要依賴什么實現(xiàn)的

          • hash主要依賴location.hash來改動 URL,達(dá)到不刷新跳轉(zhuǎn)的效果.每次 hash 改變都會觸發(fā)hashchange事件(來響應(yīng)路由的變化,比如頁面的更換)
          • history主要利用了 HTML5的 historyAPI 來實現(xiàn),用pushState和replaceState來操作瀏覽歷史記錄棧

          Q: MVVM 和 MVC 的差異? 聽說過 MVP?

          這類的文章好多,三個開發(fā)模式的誕生都有前后,不是同時出現(xiàn)的.

          傳送門:

          • MVC,MVP 和 MVVM 的圖示
          • 淺析前端開發(fā)中的 MVC/MVP/MVVM 模式

          Q: 求100~999的所有"水仙花"數(shù), 就是三位數(shù)中各數(shù)字的立方和等于自身,比如153=1^3+5^3+3^3

          • 常規(guī)遍歷法
          function threeWaterFlower(rangeStart, rangeEnd) {
           var temp=[];
           rangeStart=rangeStart || 100;
           rangeEnd=rangeEnd || 999;
           for (var i=rangeStart; i <=rangeEnd; i++) {
           var t=i.toString().split('');
           Math.pow(t[0], 3) + Math.pow(t[1], 3) + Math.pow(t[2], 3)==i
           ? temp.push(i)
           : '';
           }
           return temp;
          }
          threeWaterFlower(100,999); // [153, 370, 371, 407]
          threeWaterFlower(); // [153, 370, 371, 407]
          復(fù)制代碼
          
          • 拓展寫法,ES6版+不定花數(shù),不折騰不舒服版本
          let manyWaterFlower=(rangeStart=100, rangeEnd=999, flower=3)=> {
           let temp=[];
           for (let i=rangeStart; i <=rangeEnd; i++) {
           let t=i
           .toString()
           .split('')
           .map(item=> Math.pow(item, flower))
           .reduce((cur,next)=> parseInt(cur)+parseInt(next));
           let transformT=parseInt(t, 10);
           transformT==i ? temp.push(i) : '';
           }
           return temp;
          }
          manyWaterFlower(); // [153, 370, 371, 407]
          manyWaterFlower(100,10000,4); // [1634, 8208, 9474]
          manyWaterFlower(100,10000,5); // [4150, 4151]
          復(fù)制代碼
          

          這種是窮舉遍歷,若是要快一點呢(考慮的周全一點呢),以及傳參范圍的矯正

          相信小伙伴都看得懂,我已經(jīng)盡量注釋了..

          let manyWaterFlower=(flower=3,rangeStart, rangeEnd )=> {
           let temp=[];// 緩存所有找到的花值
           // 這一段就是填充開始循環(huán)的范圍,處理完畢后轉(zhuǎn)為數(shù)字,推薦的開始值
           let flowerRecommandStart=Number(
           ''.padStart(flower, '0').replace(/^(\d{1})/g, '1')
           );
           let flowerRecommandEnd=Number(''.padStart(flower, '9'));
           // 判斷是否傳入開始值
           if (rangeStart) {
           rangeStart > flowerRecommandStart
           ? (rangeStart=flowerRecommandStart)
           : rangeStart;
           } else {
           rangeStart=flowerRecommandStart;
           }
           // 判斷是否有傳入結(jié)束值
           if (rangeEnd) {
           rangeEnd > flowerRecommandEnd ? (rangeEnd=flowerRecommandEnd) : rangeEnd;
           } else {
           rangeEnd=flowerRecommandEnd;
           }
           // 若是初始值大于結(jié)束值
           if (rangeStart > rangeEnd) {
           rangeEnd=flowerRecommandEnd;
           }
           for (let i=rangeStart; i <=rangeEnd; i++) {
           let t=i
           .toString()
           .split('')
           .map(item=> Math.pow(item, flower))
           .reduce((cur, next)=> parseInt(cur) + parseInt(next));
           let transformT=parseInt(t, 10);
           transformT==i ? temp.push(i) : '';
           }
           return temp;
          };
          console.time('manyWaterFlower');
          manyWaterFlower(4)
          console.timeEnd('manyWaterFlower');
          // VM34013:4 manyWaterFlower: 8.112060546875ms ,這個是跑出來的時間
          用上個例子的代碼,從100到9999的,我們跑一下看看
          console.time('manyWaterFlower');
          manyWaterFlower(100,9999,4)
          console.timeEnd('manyWaterFlower');
          // VM3135:4 manyWaterFlower: 10.51904296875ms
          // 我的 MBP 跑10花直接卡死,跑7花有點久,
          console.time('7 flower')
          manyWaterFlower(7);
          console.timeEnd('7 flower')
          // 7 flower: 6489.608154296875ms
          // 8 花 CPU 的風(fēng)扇狂叫,.
          console.time('8 flower')
          manyWaterFlower(8);
          console.timeEnd('8 flower')
          // VM644:3 8 flower: 68010.26489257812ms
          // 對了我們還沒有考慮數(shù)值溢出的問題..因為正整數(shù)在 JS 的范圍是有限的.
          // 有興趣的小伙伴可以自行完善
          復(fù)制代碼
          

          Q: 請使用遞歸算法在 TODO 注釋后實現(xiàn)通過節(jié)點 key 數(shù)組尋找 json 對象中的對應(yīng)值

          比如console.log(findNode(['a1', 'b2'], data))===data.a1.b2

          // 請使用遞歸算法在 TODO 注釋后實現(xiàn)通過節(jié)點 key 數(shù)組尋找 json 對象中的對應(yīng)值
          var data={
           a1: {
           b1: 1,
           b2: 2,
           b3: {
           b4: 5
           }
           },
           a2: {
           b1: 3,
           b2: 4
           }
          };
          function findNode(inPath, inData) {
           // TODO
           // 判斷傳入的是否是一個數(shù)組
           if (Array.isArray(inPath)) {
           // 當(dāng)長度為1的時候?qū)ふ以?key 是否有值,有則返回,無則返回-1
           if (inPath.length===1) {
           return inData[inPath[0]] ? inData[inPath[0]]: -1;
           }else{
           return findNode(inPath.slice(1), inData[inPath[0]]);
           }
           } else{
           console.log('您傳入的不是一個數(shù)組')
           }
          }
          console.log(findNode(['a1', 'b2'], data)); // 2
          console.log(findNode(['a1', 'b3','b4'], data)); // 5
          復(fù)制代碼
          
          • 來個拓展版?支持字符串或數(shù)組傳入;findNode('a1.b2',data)?
          var data={
           a1: {
           b1: 1,
           b2: 2,
           b3: {
           b4: 5
           }
           },
           a2: {
           b1: 3,
           b2: 4
           }
          };
          // 判斷格式
          function isType(params) {
           let type=Object.prototype.toString.call(params);
           if (type==='[object String]') {
           params=params.split('.');
           return params;
           }
           if (type==='[object Array]') {
           return params;
           }
          }
          function findNode(inPath, inData) {
           inPath=isType(inPath);
           // 判斷傳入的是否是一個數(shù)組
           if (Array.isArray(inPath)) {
           // 當(dāng)長度為1的時候?qū)ふ以?key 是否有值,有則返回,無則返回-1
           if (inPath.length===1) {
           return inData[inPath[0]] ? inData[inPath[0]]: -1;
           }else{
           return findNode(inPath.slice(1), inData[inPath[0]]);
           }
           } else {
           console.log('您傳入的不是一個數(shù)組');
           }
          }
          console.log(findNode(['a1', 'b2'], data)); // 2
          console.log(findNode('a1.b3.b4', data)); // 5
          復(fù)制代碼
          

          Q: webpack 是什么?webpack 常見的優(yōu)化手段有哪些;

          webpack 是一個資源處理工具,它的出現(xiàn)節(jié)省了我們的人力和時間; 可以對資源打包,解析,區(qū)分開發(fā)模式等等,

          常見的優(yōu)化手段:

          • 分離第三方庫(依賴),比如引入dll
          • 引入多進(jìn)程編譯,比如happypack
          • 提取公共的依賴模塊,比如commonChunkPlugin
          • 資源混淆和壓縮:比如UglifyJS
          • 分離樣式這些,減小bundle chunk的大小,比如ExtractTextPlugin
          • GZIP 壓縮,在打包的時候?qū)Y源對齊壓縮,只要部署的服務(wù)器能解析即可..減少請求的大小
          • 還有按需加載這些,一般主流的框架都有對應(yīng)的模塊懶加載方式.
          • 至于tree shaking目前webpack3/4已經(jīng)默認(rèn)集成

          Q: 從你輸入一個 URL 到頁面渲染的大體過程,

          大體過程是這樣的,想了解很細(xì)致的可以自行引擎;

          1. IP->DNS(瀏覽器=>系統(tǒng)緩存=>DNS 服務(wù)器)->域名解析完成(這一步不用太多解析吧)
          2. TCP 協(xié)議走完->HTTP(S) 協(xié)議->緩存->(分析請求頭)-> 回饋報文
          3. 請求文檔下來->DOM->CSSDOM->靜態(tài)資源下載->render(繪制文檔)->js 解析
          4. 用戶看到頁面

          Q: Vue 的組件的通訊手段有哪些..

          • 父-> 子: props
          • 子-> 父: on+emit
          • 父<>子: on.sync(語法糖)來的
          • 兄弟 : event bus | vuex

          Q: Vuex你怎么理解?

          vuex是一個狀態(tài)管理容器(你也可以理解為全局變量),數(shù)據(jù)的流向是是單向數(shù)據(jù)流,

          且數(shù)據(jù)并不具有持久化的特性(默認(rèn)情況下刷新就重置所有狀態(tài));

          里面的一些數(shù)據(jù)乃至方法,可以大致理解為 vue 的一些特性,比如

          VuexVuestatedatagettercomputedmutation/actionsmethods

          至于單向數(shù)據(jù)流(全局單例模式)怎么理解

          state只能給mutation(同步操作) 改動, action只能反饋給mutation,可以進(jìn)行異步操作(比如和后端交互拉取數(shù)據(jù)), state能觸發(fā) render,action能用dispatch分發(fā)..如圖

          結(jié)語

          還有一些題目記不起來了,就沒轍了,還有一些題目是看你個人發(fā)揮的,沒法寫,比如

          • Q: 讓你來為公司的一個項目做技術(shù)選型,你會怎么做,為什么?
          • Q: React,Angular,Vue的比較?
          • Q: 說說你對 VNode的理解,diff的過程;
          • Q: Vue的雙向綁定如何實現(xiàn),用了什么模式(訂閱模式),大體如何實現(xiàn)的。
          • Q: cmd/amd/commonjs的差異
          • Q: 小程序以及React Native的差異..等等

          面試的過程中磕磕碰碰才能發(fā)現(xiàn)自身的很多不足和需要去努力的方向.

          有不對之處請留言,會及時跟進(jìn)修正,謝謝各位大佬

          、canvas簡介

          ? <canvas> 是 HTML5 新增的,一個可以使用腳本(通常為JavaScript)在其中繪制圖像的 HTML 元素。它可以用來制作照片集或者制作簡單(也不是那么簡單)的動畫,甚至可以進(jìn)行實時視頻處理和渲染。

          ? 它最初由蘋果內(nèi)部使用自己MacOS X WebKit推出,供應(yīng)用程序使用像儀表盤的構(gòu)件和 Safari 瀏覽器使用。 后來,有人通過Gecko內(nèi)核的瀏覽器 (尤其是Mozilla和Firefox),Opera和Chrome和超文本網(wǎng)絡(luò)應(yīng)用技術(shù)工作組建議為下一代的網(wǎng)絡(luò)技術(shù)使用該元素。

          ? Canvas是由HTML代碼配合高度和寬度屬性而定義出的可繪制區(qū)域。JavaScript代碼可以訪問該區(qū)域,類似于其他通用的二維API,通過一套完整的繪圖函數(shù)來動態(tài)生成圖形。

          ? Mozilla 程序從 Gecko 1.8 (Firefox 1.5)開始支持 <canvas>, Internet Explorer 從IE9開始<canvas> 。Chrome和Opera 9+ 也支持 <canvas>。

          二、Canvas基本使用

          2.1 <canvas>元素

          <canvas id="tutorial" width="300" height="300"></canvas>
          

          ? <canvas>看起來和<img>標(biāo)簽一樣,只是 <canvas> 只有兩個可選的屬性 width、heigth 屬性,而沒有 src、alt 屬性。

          ? 如果不給<canvas>設(shè)置widht、height屬性時,則默認(rèn) width為300、height為150,單位都是px。也可以使用css屬性來設(shè)置寬高,但是如寬高屬性和初始比例不一致,他會出現(xiàn)扭曲。所以,建議永遠(yuǎn)不要使用css屬性來設(shè)置<canvas>的寬高。

          ###替換內(nèi)容

          ? 由于某些較老的瀏覽器(尤其是IE9之前的IE瀏覽器)或者瀏覽器不支持HTML元素<canvas>,在這些瀏覽器上你應(yīng)該總是能展示替代內(nèi)容。

          ? 支持<canvas>的瀏覽器會只渲染<canvas>標(biāo)簽,而忽略其中的替代內(nèi)容。不支持 <canvas> 的瀏覽器則 會直接渲染替代內(nèi)容。

          用文本替換:

          <canvas>
           你的瀏覽器不支持canvas,請升級你的瀏覽器
          </canvas>
          

          用 <img> 替換:

          <canvas>
           <img src="./美女.jpg" alt="">
          </canvas>
          

          結(jié)束標(biāo)簽</canvas>不可省

          與 <img>元素不同,<canvas>元素需要結(jié)束標(biāo)簽(</canvas>)。如果結(jié)束標(biāo)簽不存在,則文檔的其余部分會被認(rèn)為是替代內(nèi)容,將不會顯示出來。

          2.2 渲染上下文(Thre Rending Context)

          ? <canvas>會創(chuàng)建一個固定大小的畫布,會公開一個或多個 渲染上下文(畫筆),使用 渲染上下文來繪制和處理要展示的內(nèi)容。

          ? 我們重點研究 2D渲染上下文。 其他的上下文我們暫不研究,比如, WebGL使用了基于OpenGL ES的3D上下文 (“experimental-webgl”) 。

          var canvas=document.getElementById('tutorial');
          //獲得 2d 上下文對象
          var ctx=canvas.getContext('2d');
          

          2.3 檢測支持性

          var canvas=document.getElementById('tutorial');
          if (canvas.getContext){
           var ctx=canvas.getContext('2d');
           // drawing code here
          } else {
           // canvas-unsupported code here
          }
          

          2.4 代碼模板

          <html>
          <head>
           <title>Canvas tutorial</title>
           <style type="text/css">
           canvas {
           border: 1px solid black;
           }
           </style>
          </head>
          <canvas id="tutorial" width="300" height="300"></canvas>
          </body>
          <script type="text/javascript">
           function draw(){
           var canvas=document.getElementById('tutorial');
           if(!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           //開始代碼
           
           }
           draw();
          </script>
          </html>
          

          2.5 一個簡單的例子

          繪制兩個長方形。

          <html>
          <head>
           <title>Canvas tutorial</title>
           <style type="text/css">
           canvas {
           border: 1px solid black;
           }
           </style>
          </head>
          <canvas id="tutorial" width="300" height="300"></canvas>
          </body>
          <script type="text/javascript">
           function draw(){
           var canvas=document.getElementById('tutorial');
           if(!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.fillStyle="rgb(200,0,0)";
           //繪制矩形
           ctx.fillRect (10, 10, 55, 50);
           ctx.fillStyle="rgba(0, 0, 200, 0.5)";
           ctx.fillRect (30, 30, 55, 50);
           }
           draw();
          </script>
          </html>
          

          三、繪制形狀

          3.1 柵格(grid)和坐標(biāo)空間

          ? 如下圖所示,canvas元素默認(rèn)被網(wǎng)格所覆蓋。通常來說網(wǎng)格中的一個單元相當(dāng)于canvas元素中的一像素。柵格的起點為左上角(坐標(biāo)為(0,0))。所有元素的位置都相對于原點來定位。所以圖中藍(lán)色方形左上角的坐標(biāo)為距離左邊(X軸)x像素,距離上邊(Y軸)y像素(坐標(biāo)為(x,y))。

          ? 后面我們會涉及到坐標(biāo)原點的平移、網(wǎng)格的旋轉(zhuǎn)以及縮放等。

          3.2 繪制矩形

          ? <canvas> 只支持一種原生的 圖形繪制:矩形。所有其他圖形都至少需要生成一種路徑(path)。不過,我們擁有眾多路徑生成的方法讓復(fù)雜圖形的繪制成為了可能。

          canvast 提供了三種方法繪制矩形:

          fillRect(x, y, width, height)

          繪制一個填充的矩形

          strokeRect(x, y, width, height)

          繪制一個矩形的邊框

          clearRect(x, y, widh, height)

          清除指定的矩形區(qū)域,然后這塊區(qū)域會變的完全透明。

          說明:

          ? 這3個方法具有相同的參數(shù)。

          ? x, y:指的是矩形的左上角的坐標(biāo)。(相對于canvas的坐標(biāo)原點)

          ? width, height:指的是繪制的矩形的寬和高。

          function draw(){
           var canvas=document.getElementById('tutorial');
           if(!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.fillRect(10, 10, 100, 50); //繪制矩形,填充的默認(rèn)顏色為黑色
           ctx.strokeRect(10, 70, 100, 50); //繪制矩形邊框
           
          }
          draw();
          
          
          
          ctx.clearRect(15, 15, 50, 25);
          

          四、繪制路徑(path)

          ? 圖形的基本元素是路徑。

          ? 路徑是通過不同顏色和寬度的線段或曲線相連形成的不同形狀的點的集合。

          ? 一個路徑,甚至一個子路徑,都是閉合的。

          使用路徑繪制圖形需要一些額外的步驟:

          創(chuàng)建路徑起始點

          調(diào)用繪制方法去繪制出路徑

          把路徑封閉

          一旦路徑生成,通過描邊或填充路徑區(qū)域來渲染圖形。

          下面是需要用到的方法:

          beginPath()

          新建一條路徑,路徑一旦創(chuàng)建成功,圖形繪制命令被指向到路徑上生成路徑

          moveTo(x, y)

          把畫筆移動到指定的坐標(biāo)(x, y)。相當(dāng)于設(shè)置路徑的起始點坐標(biāo)。

          closePath()

          閉合路徑之后,圖形繪制命令又重新指向到上下文中

          stroke()

          通過線條來繪制圖形輪廓

          fill()

          通過填充路徑的內(nèi)容區(qū)域生成實心的圖形

          4.1 繪制線段

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath(); //新建一條path
           ctx.moveTo(50, 50); //把畫筆移動到指定的坐標(biāo)
           ctx.lineTo(200, 50); //繪制一條從當(dāng)前位置到指定坐標(biāo)(200, 50)的直線.
           //閉合路徑。會拉一條從當(dāng)前點到path起始點的直線。如果當(dāng)前點與起始點重合,則什么都不做
           ctx.closePath();
           ctx.stroke(); //繪制路徑。
          }
          draw();
          

          4.2 繪制三角形邊框

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.moveTo(50, 50);
           ctx.lineTo(200, 50);
           ctx.lineTo(200, 200);
           ctx.closePath(); //雖然我們只繪制了兩條線段,但是closePath會closePath,仍然是一個3角形
           ctx.stroke(); //描邊。stroke不會自動closePath()
          }
          draw();
          

          4.3 填充三角形

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.moveTo(50, 50);
           ctx.lineTo(200, 50);
           ctx.lineTo(200, 200);
           
           ctx.fill(); //填充閉合區(qū)域。如果path沒有閉合,則fill()會自動閉合路徑。
          }
          draw();
          

          4 繪制圓弧

          有兩個方法可以繪制圓弧:

          arc(x, y, r, startAngle, endAngle, anticlockwise):

          以(x, y)為圓心,以r為半徑,從 startAngle弧度開始到endAngle弧度結(jié)束。anticlosewise是布爾值,true表示逆時針,false表示順時針。(默認(rèn)是順時針)

          注意:

          這里的度數(shù)都是弧度。

          0弧度是指的x軸正方形

          radians=(Math.PI/180)*degrees //角度轉(zhuǎn)換成弧度

          arcTo(x1, y1, x2, y2, radius):

          根據(jù)給定的控制點和半徑畫一段圓弧,最后再以直線連接兩個控制點。

          圓弧案例1:

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.arc(50, 50, 40, 0, Math.PI / 2, false);
           ctx.stroke();
          }
          draw();
          

          圓弧案例2:

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.arc(50, 50, 40, 0, Math.PI / 2, false);
           ctx.stroke();
           ctx.beginPath();
           ctx.arc(150, 50, 40, 0, -Math.PI / 2, true);
           ctx.closePath();
           ctx.stroke();
           ctx.beginPath();
           ctx.arc(50, 150, 40, -Math.PI / 2, Math.PI / 2, false);
           ctx.fill();
           ctx.beginPath();
           ctx.arc(150, 150, 40, 0, Math.PI, false);
           ctx.fill();
          }
          draw();
          

          圓弧案例3:

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.moveTo(50, 50);
           //參數(shù)1、2:控制點1坐標(biāo) 參數(shù)3、4:控制點2坐標(biāo) 參數(shù)4:圓弧半徑
           ctx.arcTo(200, 50, 200, 200, 100);
           ctx.lineTo(200, 200)
           ctx.stroke();
           
           ctx.beginPath();
           ctx.rect(50, 50, 10, 10);
           ctx.rect(200, 50, 10, 10)
           ctx.rect(200, 200, 10, 10)
           ctx.fill()
          }
          draw();
          

          arcTo方法的說明:

          ? 這個方法可以這樣理解。繪制的弧形是由兩條切線所決定。

          ? 第 1 條切線:起始點和控制點1決定的直線。

          ? 第 2 條切線:控制點1 和控制點2決定的直線。

          ? 其實繪制的圓弧就是與這兩條直線相切的圓弧。

          4.5 繪制貝塞爾曲線

          4.5.1 什么是貝塞爾曲線

          ? 貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟(jì)埃曲線,是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線。

          ? 一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節(jié)點組成,節(jié)點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。

          ? 貝塞爾曲線是計算機圖形學(xué)中相當(dāng)重要的參數(shù)曲線,在一些比較成熟的位圖軟件中也有貝塞爾曲線工具如PhotoShop等。在Flash4中還沒有完整的曲線工具,而在Flash5里面已經(jīng)提供出貝塞爾曲線工具。

          ? 貝塞爾曲線于1962,由法國工程師皮埃爾·貝塞爾(Pierre Bézier)所廣泛發(fā)表,他運用貝塞爾曲線來為汽車的主體進(jìn)行設(shè)計。貝塞爾曲線最初由Paul de Casteljau于1959年運用de Casteljau演算法開發(fā),以穩(wěn)定數(shù)值的方法求出貝茲曲線。

          一次貝塞爾曲線(線性貝塞爾曲線)

          ? 一次貝塞爾曲線其實是一條直線。

          二次貝塞爾曲線

          三次貝塞爾曲線

          4.5.2 繪制貝塞爾曲線

          繪制二次貝塞爾曲線

          quadraticCurveTo(cp1x, cp1y, x, y):

          說明:

          ? 參數(shù)1和2:控制點坐標(biāo)

          ? 參數(shù)3和4:結(jié)束點坐標(biāo)

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.moveTo(10, 200); //起始點
           var cp1x=40, cp1y=100; //控制點
           var x=200, y=200; // 結(jié)束點
           //繪制二次貝塞爾曲線
           ctx.quadraticCurveTo(cp1x, cp1y, x, y);
           ctx.stroke();
           
           ctx.beginPath();
           ctx.rect(10, 200, 10, 10);
           ctx.rect(cp1x, cp1y, 10, 10);
           ctx.rect(x, y, 10, 10);
           ctx.fill();
           
          }
          draw();
          

          繪制三次貝塞爾曲線

          bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):

          說明:

          ? 參數(shù)1和2:控制點1的坐標(biāo)

          ? 參數(shù)3和4:控制點2的坐標(biāo)

          ? 參數(shù)5和6:結(jié)束點的坐標(biāo)

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.moveTo(40, 200); //起始點
           var cp1x=20, cp1y=100; //控制點1
           var cp2x=100, cp2y=120; //控制點2
           var x=200, y=200; // 結(jié)束點
           //繪制二次貝塞爾曲線
           ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
           ctx.stroke();
           ctx.beginPath();
           ctx.rect(40, 200, 10, 10);
           ctx.rect(cp1x, cp1y, 10, 10);
           ctx.rect(cp2x, cp2y, 10, 10);
           ctx.rect(x, y, 10, 10);
           ctx.fill();
          }
          draw();
          

          五、添加樣式和顏色

          ? 在前面的繪制矩形章節(jié)中,只用到了默認(rèn)的線條和顏色。

          ? 如果想要給圖形上色,有兩個重要的屬性可以做到。

          fillStyle=color

          設(shè)置圖形的填充顏色

          strokeStyle=color

          設(shè)置圖形輪廓的顏色

          備注:

          1. `color` 可以是表示 `css` 顏色值的字符串、漸變對象或者圖案對象。

          2. 默認(rèn)情況下,線條和填充顏色都是黑色。

          3. 一旦您設(shè)置了 `strokeStyle` 或者 `fillStyle` 的值,那么這個新值就會成為新繪制的圖形的默認(rèn)值。如果你要給每個圖形上不同的顏色,你需要重新設(shè)置 `fillStyle` 或 `strokeStyle` 的值。

          1.fillStyle

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           for (var i=0; i < 6; i++){
           for (var j=0; j < 6; j++){
           ctx.fillStyle='rgb(' + Math.floor(255 - 42.5 * i) + ',' +
           Math.floor(255 - 42.5 * j) + ',0)';
           ctx.fillRect(j * 50, i * 50, 50, 50);
           }
           }
          }
          draw();
          

          2.strokeStyle

          <script type="text/javascript">
           function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           for (var i=0; i < 6; i++){
           for (var j=0; j < 6; j++){
           ctx.strokeStyle=`rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`;
           ctx.strokeRect(j * 50, i * 50, 40, 40);
           }
           }
           }
           draw();
          

          /**

          返回隨機的 [from, to] 之間的整數(shù)(包括from,也包括to)

          */

           function randomInt(from, to){
           return parseInt(Math.random() * (to - from + 1) + from);
           }
          </script>
          

          3.Transparency(透明度)

          globalAlpha=transparencyValue

          ? 這個屬性影響到 canvas 里所有圖形的透明度,有效的值范圍是 0.0 (完全透明)到 1.0(完全不透明),默認(rèn)是 1.0。

          ? globalAlpha 屬性在需要繪制大量擁有相同透明度的圖形時候相當(dāng)高效。不過,我認(rèn)為使用rgba()設(shè)置透明度更加好一些。

          line style

          1. lineWidth=value

          線寬。只能是正值。默認(rèn)是1.0。

          起始點和終點的連線為中心,上下各占線寬的一半

          ctx.beginPath();
          ctx.moveTo(10, 10);
          ctx.lineTo(100, 10);
          ctx.lineWidth=10;
          ctx.stroke();
          ctx.beginPath();
          ctx.moveTo(110, 10);
          ctx.lineTo(160, 10)
          ctx.lineWidth=20;
          ctx.stroke()
          

          ###2. lineCap=type

          線條末端樣式。

          共有3個值:

          butt:線段末端以方形結(jié)束

          round:線段末端以圓形結(jié)束

          square:線段末端以方形結(jié)束,但是增加了一個寬度和線段相同,高度是線段厚度一半的矩形區(qū)域。

          var lineCaps=["butt", "round", "square"];
           for (var i=0; i < 3; i++){
           ctx.beginPath();
           ctx.moveTo(20 + 30 * i, 30);
           ctx.lineTo(20 + 30 * i, 100);
           ctx.lineWidth=20;
           ctx.lineCap=lineCaps[i];
           ctx.stroke();
           }
           ctx.beginPath();
           ctx.moveTo(0, 30);
           ctx.lineTo(300, 30);
           ctx.moveTo(0, 100);
           ctx.lineTo(300, 100)
           ctx.strokeStyle="red";
           ctx.lineWidth=1;
           ctx.stroke();
          

          3. lineJoin=type

          同一個path內(nèi),設(shè)定線條與線條間接合處的樣式。

          共有3個值round, bevel 和 miter:

          round

          通過填充一個額外的,圓心在相連部分末端的扇形,繪制拐角的形狀。 圓角的半徑是線段的寬度。

          bevel

          在相連部分的末端填充一個額外的以三角形為底的區(qū)域, 每個部分都有各自獨立的矩形拐角。

          miter(默認(rèn))

          通過延伸相連部分的外邊緣,使其相交于一點,形成一個額外的菱形區(qū)域。

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           var lineJoin=['round', 'bevel', 'miter'];
           ctx.lineWidth=20;
           for (var i=0; i < lineJoin.length; i++){
           ctx.lineJoin=lineJoin[i];
           ctx.beginPath();
           ctx.moveTo(50, 50 + i * 50);
           ctx.lineTo(100, 100 + i * 50);
           ctx.lineTo(150, 50 + i * 50);
           ctx.lineTo(200, 100 + i * 50);
           ctx.lineTo(250, 50 + i * 50);
           ctx.stroke();
           }
          }
          draw();
          

          4. 虛線

          用 setLineDash 方法和 lineDashOffset 屬性來制定虛線樣式. setLineDash 方法接受一個數(shù)組,來指定線段與間隙的交替;lineDashOffset屬性設(shè)置起始偏移量.

          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           
           ctx.setLineDash([20, 5]); // [實線長度, 間隙長度]
           ctx.lineDashOffset=-0;
           ctx.strokeRect(50, 50, 210, 210);
          }
          draw();
          

          備注:

          ? getLineDash():返回一個包含當(dāng)前虛線樣式,長度為非負(fù)偶數(shù)的數(shù)組。

          六、繪制文本

          繪制文本的兩個方法

          canvas 提供了兩種方法來渲染文本:

          fillText(text, x, y [, maxWidth])

          在指定的(x,y)位置填充指定的文本,繪制的最大寬度是可選的.

          strokeText(text, x, y [, maxWidth])

          在指定的(x,y)位置繪制文本邊框,繪制的最大寬度是可選的.

          var ctx;
          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           ctx=canvas.getContext("2d");
           ctx.font="100px sans-serif"
           ctx.fillText("天若有情", 10, 100);
           ctx.strokeText("天若有情", 10, 200)
          }
          draw();
          

          給文本添加樣式

          font=value

          當(dāng)前我們用來繪制文本的樣式。這個字符串使用和 CSS font屬性相同的語法. 默認(rèn)的字體是 10px sans-serif。

          textAlign=value

          文本對齊選項. 可選的值包括:start, end, left, right or center. 默認(rèn)值是 start。

          textBaseline=value

          基線對齊選項,可選的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默認(rèn)值是 alphabetic。

          direction=value

          文本方向。可能的值包括:ltr, rtl, inherit。默認(rèn)值是 inherit。

          七、繪制圖片

          ? 我們也可以在canvas上直接繪制圖片。

          7.1 由零開始創(chuàng)建圖片

          創(chuàng)建<img>元素

          var img=new Image(); // 創(chuàng)建一個<img>元素
          img.src='myImage.png'; // 設(shè)置圖片源地址
          

          腳本執(zhí)行后圖片開始裝載

          繪制img

          //參數(shù)1:要繪制的img 參數(shù)2、3:繪制的img在canvas中的坐標(biāo)

          ctx.drawImage(img,0,0); 
          

          注意:

          ? 考慮到圖片是從網(wǎng)絡(luò)加載,如果 drawImage 的時候圖片還沒有完全加載完成,則什么都不做,個別瀏覽器會拋異常。所以我們應(yīng)該保證在 img 繪制完成之后再 drawImage。

          var img=new Image(); // 創(chuàng)建img元素
          img.onload=function(){
           ctx.drawImage(img, 0, 0)
          }
          img.src='myImage.png'; // 設(shè)置圖片源地址
          

          7.2 繪制 img 標(biāo)簽元素中的圖片

          ? img 可以 new 也可以來源于我們頁面的 <img>標(biāo)簽

          <img src="./美女.jpg" alt="" width="300"><br>
          <canvas id="tutorial" width="600" height="400"></canvas>
          <script type="text/javascript">
           function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           var img=document.querySelector("img");
           ctx.drawImage(img, 0, 0);
           }
           document.querySelector("img").onclick=function (){
           draw();
           }
          </script>
          

          第一張圖片就是頁面中的<img>標(biāo)簽

          7.3 縮放圖片

          drawImage() 也可以再添加兩個參數(shù):

          ? drawImage(image, x, y, width, height)

          ? 這個方法多了2個參數(shù):width 和 height,這兩個參數(shù)用來控制 當(dāng)像canvas畫入時應(yīng)該縮放的大小。

          ctx.drawImage(img, 0, 0, 400, 200)

          7.4 切片(slice)

          drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

          ? 第一個參數(shù)和其它的是相同的,都是一個圖像或者另一個 canvas 的引用。

          其他8個參數(shù):

          ? 前4個是定義圖像源的切片位置和大小,

          ? 后4個則是定義切片的目標(biāo)顯示位置和大小。

          八、狀態(tài)的保存和恢復(fù)

          Saving and restoring state是繪制復(fù)雜圖形時必不可少的操作。

          save()和restore()

          ? save 和 restore 方法是用來保存和恢復(fù) canvas 狀態(tài)的,都沒有參數(shù)。

          ? Canvas 的狀態(tài)就是當(dāng)前畫面應(yīng)用的所有樣式和變形的一個快照。

          關(guān)于 save()

          Canvas狀態(tài)存儲在棧中,每當(dāng)save()方法被調(diào)用后,當(dāng)前的狀態(tài)就被推送到棧中保存。一個繪畫狀態(tài)包括:

          當(dāng)前應(yīng)用的變形(即移動,旋轉(zhuǎn)和縮放)

          strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值

          當(dāng)前的裁切路徑(clipping path)

          ?

          可以調(diào)用任意多次 save方法。(類似數(shù)組的push())

          關(guān)于restore()

          每一次調(diào)用 restore 方法,上一個保存的狀態(tài)就從棧中彈出,所有設(shè)定都恢復(fù)。(類似數(shù)組的pop())

          var ctx;
          function draw(){
           var canvas=document.getElementById('tutorial');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.fillRect(0, 0, 150, 150); // 使用默認(rèn)設(shè)置繪制一個矩形
           ctx.save(); // 保存默認(rèn)狀態(tài)
           ctx.fillStyle='red' // 在原有配置基礎(chǔ)上對顏色做改變
           ctx.fillRect(15, 15, 120, 120); // 使用新的設(shè)置繪制一個矩形
           ctx.save(); // 保存當(dāng)前狀態(tài)
           ctx.fillStyle='#FFF' // 再次改變顏色配置
           ctx.fillRect(30, 30, 90, 90); // 使用新的配置繪制一個矩形
           ctx.restore(); // 重新加載之前的顏色狀態(tài)
           ctx.fillRect(45, 45, 60, 60); // 使用上一次的配置繪制一個矩形
           ctx.restore(); // 加載默認(rèn)顏色配置
           ctx.fillRect(60, 60, 30, 30); // 使用加載的配置繪制一個矩形
          }
          draw();
          

          九、變形

          9.1 translate

          translate(x, y)

          ? 用來移動 canvas 的原點到指定的位置

          ? translate方法接受兩個參數(shù)。x 是左右偏移量,y 是上下偏移量,如右圖所示。

          在做變形之前先保存狀態(tài)是一個良好的習(xí)慣。大多數(shù)情況下,調(diào)用 restore 方法比手動恢復(fù)原先的狀態(tài)要簡單得多。又如果你是在一個循環(huán)中做位移但沒有保存和恢復(fù)canvas 的狀態(tài),很可能到最后會發(fā)現(xiàn)怎么有些東西不見了,那是因為它很可能已經(jīng)超出 canvas 范圍以外了。

          ? 注意:translate移動的是canvas的坐標(biāo)原點。(坐標(biāo)變換)

          var ctx;
          function draw(){
           var canvas=document.getElementById('tutorial1');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.save(); //保存坐原點平移之前的狀態(tài)
           ctx.translate(100, 100);
           ctx.strokeRect(0, 0, 100, 100)
           ctx.restore(); //恢復(fù)到最初狀態(tài)
           ctx.translate(220, 220);
           ctx.fillRect(0, 0, 100, 100)
          }
          draw();
          

          9.2 rotate

          rotate(angle)

          ? 旋轉(zhuǎn)坐標(biāo)軸。

          ? 這個方法只接受一個參數(shù):旋轉(zhuǎn)的角度(angle),它是順時針方向的,以弧度為單位的值。

          ? 旋轉(zhuǎn)的中心是坐標(biāo)原點。

          var ctx;
          function draw(){
           var canvas=document.getElementById('tutorial1');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.fillStyle="red";
           ctx.save();
           ctx.translate(100, 100);
           ctx.rotate(Math.PI / 180 * 45);
           ctx.fillStyle="blue";
           ctx.fillRect(0, 0, 100, 100);
           ctx.restore();
           ctx.save();
           ctx.translate(0, 0);
           ctx.fillRect(0, 0, 50, 50)
           ctx.restore();
          }
          draw();
          

          9.3 scale

          scale(x, y)

          ? 我們用它來增減圖形在 canvas 中的像素數(shù)目,對形狀,位圖進(jìn)行縮小或者放大。

          ? scale方法接受兩個參數(shù)。x,y分別是橫軸和縱軸的縮放因子,它們都必須是正值。值比 1.0 小表示縮 小,比 1.0 大則表示放大,值為 1.0 時什么效果都沒有。

          ? 默認(rèn)情況下,canvas 的 1 單位就是 1 個像素。舉例說,如果我們設(shè)置縮放因子是 0.5,1 個單位就變成對應(yīng) 0.5 個像素,這樣繪制出來的形狀就會是原先的一半。同理,設(shè)置為 2.0 時,1 個單位就對應(yīng)變成了 2 像素,繪制的結(jié)果就是圖形放大了 2 倍。

          9.4 transform(變形矩陣)

          transform(a, b, c, d, e, f)

          a (m11)

          ? Horizontal scaling.

          b (m12)

          ? Horizontal skewing.

          c (m21)

          ? Vertical skewing.

          d (m22)

          ? Vertical scaling.

          e (dx)

          ? Horizontal moving.

          f (dy)

          ? Vertical moving.

          var ctx;
          function draw(){
           var canvas=document.getElementById('tutorial1');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.transform(1, 1, 0, 1, 0, 0);
           ctx.fillRect(0, 0, 100, 100);
          }
          draw();
          

          十、合成

          ? 在前面的所有例子中、,我們總是將一個圖形畫在另一個之上,對于其他更多的情況,僅僅這樣是遠(yuǎn)遠(yuǎn)不夠的。比如,對合成的圖形來說,繪制順序會有限制。不過,我們可以利用 globalCompositeOperation 屬性來改變這種狀況。

          globalCompositeOperation=type
          

          var ctx;
           function draw(){
           var canvas=document.getElementById('tutorial1');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           
           ctx.fillStyle="blue";
           ctx.fillRect(0, 0, 200, 200);
           ctx.globalCompositeOperation="source-over"; //全局合成操作
           ctx.fillStyle="red";
           ctx.fillRect(100, 100, 200, 200);
           }
           draw();
          

          注:下面的展示中,藍(lán)色是原有的,紅色是新的。

          type `是下面 13 種字符串值之一:

          ##1. source-over(default)

          這是默認(rèn)設(shè)置,新圖像會覆蓋在原有圖像。

          ##2. source-in

          僅僅會出現(xiàn)新圖像與原來圖像重疊的部分,其他區(qū)域都變成透明的。(包括其他的老圖像區(qū)域也會透明)

          ##3. source-out

          僅僅顯示新圖像與老圖像沒有重疊的部分,其余部分全部透明。(老圖像也不顯示)

          ##4. source-atop

          新圖像僅僅顯示與老圖像重疊區(qū)域。老圖像仍然可以顯示。

          ##5. destination-over

          新圖像會在老圖像的下面。

          ##6. destination-in

          僅僅新老圖像重疊部分的老圖像被顯示,其他區(qū)域全部透明。

          ##7. destination-out

          僅僅老圖像與新圖像沒有重疊的部分。 注意顯示的是老圖像的部分區(qū)域。

          ##8. destination-atop

          老圖像僅僅僅僅顯示重疊部分,新圖像會顯示在老圖像的下面。

          ##9. lighter

          新老圖像都顯示,但是重疊區(qū)域的顏色做加處理

          ##10. darken

          保留重疊部分最黑的像素。(每個顏色位進(jìn)行比較,得到最小的)

          blue: #0000ff

          red: #ff0000

          所以重疊部分的顏色:#000000

          ##11. lighten

          保證重疊部分最量的像素。(每個顏色位進(jìn)行比較,得到最大的)

          blue: #0000ff

          red: #ff0000

          所以重疊部分的顏色:#ff00ff

          ##12. xor

          重疊部分會變成透明

          ##13. copy

          只有新圖像會被保留,其余的全部被清除(邊透明)

          #十一、裁剪路徑

          clip()

          ? 把已經(jīng)創(chuàng)建的路徑轉(zhuǎn)換成裁剪路徑。

          ? 裁剪路徑的作用是遮罩。只顯示裁剪路徑內(nèi)的區(qū)域,裁剪路徑外的區(qū)域會被隱藏。

          ? 注意:clip()只能遮罩在這個方法調(diào)用之后繪制的圖像,如果是clip()方法調(diào)用之前繪制的圖像,則無法實現(xiàn)遮罩。

          var ctx;
          function draw(){
           var canvas=document.getElementById('tutorial1');
           if (!canvas.getContext) return;
           var ctx=canvas.getContext("2d");
           ctx.beginPath();
           ctx.arc(20,20, 100, 0, Math.PI * 2);
           ctx.clip();
           
           ctx.fillStyle="pink";
           ctx.fillRect(20, 20, 100,100);
          }
          draw();
          

          十二、動畫

          動畫的基本步驟

          清空canvas

          再繪制每一幀動畫之前,需要清空所有。清空所有最簡單的做法就是clearRect()方法

          保存canvas狀態(tài)

          如果在繪制的過程中會更改canvas的狀態(tài)(顏色、移動了坐標(biāo)原點等),又在繪制每一幀時都是原始狀態(tài)的話,則最好保存下canvas的狀態(tài)

          繪制動畫圖形

          這一步才是真正的繪制動畫幀

          恢復(fù)canvas狀態(tài)

          如果你前面保存了canvas狀態(tài),則應(yīng)該在繪制完成一幀之后恢復(fù)canvas狀態(tài)。

          控制動畫

          我們可用通過canvas的方法或者自定義的方法把圖像會知道到canvas上。正常情況,我們能看到繪制的結(jié)果是在腳本執(zhí)行結(jié)束之后。例如,我們不可能在一個 for 循環(huán)內(nèi)部完成動畫。

          也就是,為了執(zhí)行動畫,我們需要一些可以定時執(zhí)行重繪的方法。

          一般用到下面三個方法:

          setInterval()

          setTimeout()

          requestAnimationFrame()

          ##案例1:太陽系

          let sun;
          let earth;
          let moon;
          let ctx;
          function init(){
           sun=new Image();
           earth=new Image();
           moon=new Image();
           sun.src="sun.png";
           earth.src="earth.png";
           moon.src="moon.png";
           let canvas=document.querySelector("#solar");
           ctx=canvas.getContext("2d");
           sun.onload=function (){
           draw()
           }
          }
          init();
          function draw(){
           ctx.clearRect(0, 0, 300, 300); //清空所有的內(nèi)容
           /*繪制 太陽*/
           ctx.drawImage(sun, 0, 0, 300, 300);
           ctx.save();
           ctx.translate(150, 150);
           //繪制earth軌道
           ctx.beginPath();
           ctx.strokeStyle="rgba(255,255,0,0.5)";
           ctx.arc(0, 0, 100, 0, 2 * Math.PI)
           ctx.stroke()
           let time=new Date();
           //繪制地球
           ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds())
           ctx.translate(100, 0);
           ctx.drawImage(earth, -12, -12)
           //繪制月球軌道
           ctx.beginPath();
           ctx.strokeStyle="rgba(255,255,255,.3)";
           ctx.arc(0, 0, 40, 0, 2 * Math.PI);
           ctx.stroke();
           //繪制月球
           ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds());
           ctx.translate(40, 0);
           ctx.drawImage(moon, -3.5, -3.5);
           ctx.restore();
           requestAnimationFrame(draw);
          }
          

          ##案例2:模擬時鐘

          提起圖標(biāo),大家可能第一個會想到PS、美工等詞語,但很多小圖標(biāo)現(xiàn)在根本都不需要再打開PS了。

          1、常見的括號( 前進(jìn)或后退“>” )

          .arrow{
            width:12rpx;
            height:12rpx; 
            border-top:1px solid #999;
            border-right:1px solid #999;
            transform:rotate(-45deg); 
            position:absolute; 
            right:10px; 
          }


          2、常見的關(guān)閉按鈕( “X” ),這里需要用到一個偽類

          .close {
                  display: inline-block;
                  width: 30px;
                  height: 4px;
                  background: #333;
                  transform: rotate(45deg);
              }
          
              .close::after {
                  content: '';
                  display: block;
                  width: 30px;
                  height: 4px;
                  background: #333;
                  transform: rotate(-90deg);
              }


          3、常見的勾選( “√” )

          .check {
              position: relative;
              display: inline-block;
              width: 25px;
              height: 25px;
              background: #333;
              border-radius: 25px;
          }
          .check::after {
              content: "";
              position: absolute;
              left: 5px;
              top: 8px;
              width: 50%;
              height: 25%;
              border: 2px solid #fff;
              border-radius: 1px;
              border-top: none;
              border-right: none;
              background: transparent;
              transform: rotate(-45deg);
          }


          4、常見的加號( “+” ),同樣需要利用偽類

          .add {
            width: 100px;
            height: 100px;
            color: #ccc;
            transition: color .25s;
            position: relative;
          }
          
           .add::before{
            content: '';
            position: absolute;
            left: 50%;
            top: 50%;
            width: 80px;
            margin-left: -40px;
            margin-top: -5px;
            border-top: 10px solid;
          }
          
          .add::after {
           content: '';
           position: absolute;
           left: 50%;
           top: 50%;
           height: 80px;
           margin-left: -5px;
           margin-top: -40px;
           border-left: 10px solid;
          }


          5、常見的波浪線( “~” ),同樣需要利用偽類

          .info::before {
          content: '';
          position: absolute;
          top: 30px;
          width: 100%;
          height: 0.25em;
          
          background:
           linear-gradient(
          135deg, 
           transparent, 
           transparent 45%, 
           #008000, 
           transparent 55%, 
           transparent 100%
           ),
          linear-gradient(
           45deg, 
           transparent, 
           transparent 45%, 
            #008000, 
           transparent 55%, 
           transparent 100%
          );
          background-size: 0.5em 0.5em;
          background-repeat: repeat-x, repeat-x;
          }


          5、常見的三角形

          .triangle-up {
          width: 0;
          height: 0;
          border-left: 50px solid transparent;
          border-right: 50px solid transparent;
          border-bottom: 100px solid red;
          }


          6、常見的扇形

          .sector {
          width: 0;
          height: 0;
          border-left: 50px solid transparent;
          border-right: 50px solid transparent;
          border-top: 100px solid #f00;
          border-radius: 50%;
          }


          7、仿微信對話框

          .alertDialog {
          /* 對話框:一個圓角矩形和一個小三角形 */
          width: 150px;
          height: 100px;
          background: #f00;
          border-radius: 10px;
          position: relative;
          }
          .alertDialog:before {
          content: "";
          width: 0;
          height: 0;
          position: absolute;
          left: -20px;
          top: 40px;
          border-top: 10px solid transparent;
          border-bottom: 10px solid transparent;
          border-right: 20px solid #f00;
          }


          8、鉆石圖標(biāo)

          .diamond {
          /* 鉆石:梯形和三角形組成 */
          width: 50px;
          height: 0;
          position: relative;
          border-bottom: 25px solid #f00;
          border-left: 25px solid transparent;
          border-right: 25px solid transparent;
          }
          .diamond:before {
          content: "";
          width: 0;
          height: 0;
          position: absolute;
          border-left: 50px solid transparent;
          border-right: 50px solid transparent;
          border-top: 70px solid #f00;
          left: -25px;
          top: 25px;
          }


          9、五角星圖標(biāo)

          .starFive {
           width: 0;
           height: 0;
           position: relative;
           border-left: 80px solid transparent;
           border-right: 80px solid transparent;
           border-bottom: 60px solid #f00;
           transform: rotate(35deg);
          }
          .starFive:before {
           content: "";
           position: absolute;
           width: 0;
           height: 0;
           border-left: 80px solid transparent;
           border-right: 80px solid transparent;
           border-bottom: 60px solid #f00;
           transform: rotate(-70deg);
           top: 3px;
           left: -80px;
          }
          .starFive:after {
           content: "";
           position: absolute;
           width: 0;
           height: 0;
           border-bottom: 60px solid #f00;
           border-right: 20px solid transparent;
           border-left: 20px solid transparent;
           transform: rotate(-35deg);
                  top: -40px;
                  left: -49px;
          }


          喜歡的可以加個關(guān)注,不定期發(fā)布更多CSS相關(guān)文章


          主站蜘蛛池模板: 少妇精品无码一区二区三区| 在线成人一区二区| 亚洲乱码一区av春药高潮| 麻豆精品人妻一区二区三区蜜桃 | 亚洲AV无码一区二区一二区| 色综合视频一区二区三区 | 无码人妻少妇色欲AV一区二区| 国产精品久久无码一区二区三区网 | 三上悠亚一区二区观看| 亚洲AV日韩AV一区二区三曲| 国模大尺度视频一区二区| 亚洲熟妇成人精品一区| 国产激情无码一区二区app| 秋霞日韩一区二区三区在线观看| 无码人妻一区二区三区免费n鬼沢| 亚洲国产综合无码一区二区二三区| 亚洲一区二区三区成人网站| 久久一区二区精品| 亚洲一区二区三区香蕉| 一区二区三区美女视频| 国产一区在线播放| 成人精品一区二区电影| 亚洲无删减国产精品一区| 亚洲熟女少妇一区二区| 日韩人妻无码一区二区三区综合部| 日本一区二区在线免费观看| 国产香蕉一区二区三区在线视频 | 国产91精品一区二区麻豆网站| 国产AV午夜精品一区二区入口| 中文字幕亚洲一区二区va在线| 亚洲欧洲一区二区三区| 视频在线观看一区| 变态调教一区二区三区| 国产成人高清亚洲一区久久| 国产精品av一区二区三区不卡蜜| 亚洲精品精华液一区二区| 国产日韩精品视频一区二区三区 | 国产精品一区二区综合| 成人免费一区二区无码视频| 无码人妻精品一区二区三区久久久 | 日本一区中文字幕日本一二三区视频|