前言
去年年末研發組解散失業, so選擇回去學車了,也順利拿到了駕照
最近迴歸大深圳….開始踏上漫漫的找工作之路;
“拉勾上吊一百年不匹配!!!”,”BOSS直聘日夜沒反應!!!”
題目範圍涵蓋我最近遇到的筆試題和麵談的(CSS/JS/HTTP/Node/Hybrid/Vue/NG/React)
emm…..這裡不列舉哪些公司了, 若是你完整的閱讀一遍,相信你有不少的收穫,謝謝閱讀
- 截止目前(2018/3/23)總共彙總了140個問題(我去面的創業,中大型皆有)…
- 期間死在各種一面/二面/三面/四面皆有之,也拿到部分和推掉部分
offer
,還有一些後續不清楚的
問題彙總,想到就寫….
Q: CSS 有哪些樣式可以給子元素繼承!
- 可繼承的:
font-size
,font-weight
,line-height
,color
,cursor
等 - 不可繼承的一般是會改變盒子模型的:
display
,margin
、border
、padding
、height
等
更加全面的可以到引擎找
Q: 行內元素有哪些?塊級元素有哪些? 空(void)元素有那些?
- 行內:
input
,span
,a
,img
以及display:inline
的元素 - 塊級:
p
,div
,header
,footer
,aside
,article
,ul
以及display:block
這些 - void:
br
,hr
Q: CSS3實現一個扇形
- 思路跟畫實體三角形一個道理,只不過多了一個圓角屬性
<
!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>
複製程式碼
Q: box-sizing
常用的屬性有哪些? 分別有啥作用?
box-sizing
有兩個值:content-box(W3C標準盒模型)
,border-box(怪異模型)
,
這個css 主要是改變盒子模型大小的計算形式
可能有人會問padding-box
,這個之前只有 Firefox 標準實現了,目前50+的版本已經廢除;
用一個栗子來距離,一個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-->
複製程式碼
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 不再我們考慮之內了.clearfix 還有另外一種寫法...-->
.clearfix:before, .clearfix:after {
content:"";
display:table;
}.clearfix:after{
clear:both;
overflow:hidden;
}.clearfix{
zoom:1;
}<
!--用display:table 是為了避免外邊距margin重疊導致的margin塌陷,內部元素預設會成為 table-cell 單元格的形式-->
複製程式碼
clear:both
:若是用在同一個容器內相鄰元素上,那是賊好的…有時候在容器外就有些問題了,比如相鄰容器的包裹層元素塌陷
overflow:hidden
:這種若是用在同個容器內,可以形成 BFC
避免浮動造成的元素塌陷
Q: CSS 中transition
和animate
有何區別? animate
如何停留在最後一幀!
這種問題見仁見智,我的回答大體是這樣的..待我捋捋.
transition
一般用來做過渡的, 沒時間軸的概念, 通過事件觸發(一次),沒中間狀態(只有開始和結束)
而animate
則是做動效,有時間軸的概念(幀可控),可以重複觸發和有中間狀態;
過渡的開銷比動效小,前者一般用於互動居多,後者用於活動頁居多;
至於如何讓animate
停留在最後一幀也好辦,就它自身引數的一個值就可以了
animation-fill-mode: forwards;
<
!--backwards則停留在首幀,both是輪流-->
複製程式碼
讓我們來舉個例子….自己新建一個 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 持續時間 timing-function 動畫頻率 delay 延遲多久開始 iteration-count 迴圈次數 direction 動畫方式,往返還是正向 fill-mode 一般用來處理停留在某一幀 play-state running 開始,paused 暫停 .... 更多的引數去查文件吧..我就不一一列舉了 */ 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>
複製程式碼
Q: 塊級元素水平垂直居中的方法
我們要考慮兩種情況,定寬高和不定寬高的;
方案 N 多種,我記得我很早寫過這類的筆記
傳送門:網頁元素居中攻略記
Q: 說說樣式權重的優先順序;
!important
>
行內樣式 >
id
>
class
>
tag
樣式權重可以疊加, 比如 id>
class
Q: 對HTML語義化的理解
簡言之:就是不濫用標籤(比如 DIV)/隨意巢狀(比如 span>
div) ,
類的命名要合理, 利於瀏覽器解析乃至引擎收錄,也利於團隊協作和維護
Q: JS有幾種資料型別,其中基本資料型別有哪些!
七種資料型別
- Boolean
- Null
- Undefined
- Number
- String
- Symbol (ECMAScript 6 新定義)
- Object
(ES6之前)其中5種為基本型別:string
,number
,boolean
,null
,undefined
,
ES6出來的Symbol
也是原始資料型別 ,表示獨一無二的值
Object
為引用型別(範圍挺大),也包括陣列、函式,
Q: null
和undefined
的差異
大體說一下,想要知其所以然請引擎搜尋
相同點:
- 在
if
判斷語句中,值都預設為false
- 大體上兩者都是代表無,具體看差異
差異:
null
轉為數字型別值為0,而undefined
轉為數字型別為NaN(Not a Number)
undefined
是代表呼叫一個值而該值卻沒有賦值,這時候預設則為undefined
null
是一個很特殊的物件,最為常見的一個用法就是作為引數傳入(說明該引數不是物件)- 設定為
null
的變數或者物件會被記憶體收集器回收
Q: JS 的DOM 操作(Node節點獲取及增刪查改);
- 獲取(太多了,有
document.getElementById/ClassName/Name/TagName 等,或者 querySelector
)
// example// get Nodevar element = document.querySelector('#test');
// 追加element.appendChild(Node);
// 刪除element.removeChild(Node);
// 查詢element.nextSibling // 獲取元素之後的兄弟節點 , 會拿到註釋文字,空白符這些element.nextElementSibling // 等同, 獲取標籤(不會拿到註釋文字這些)element.previousSibling // 和上面同理,往前找兄弟節點element.previousElementSibling// 改動,比如 屬性這些element.setAttribute(name, value);
// 增加屬性element.removeAttribute(attrName);
//刪除屬性// 來一個簡易的練習題,隨便一個網頁追加插入一塊DOM(非覆蓋:不能 innerHTML);
/*<
div id="test">
<
span>
Hello, World<
/span>
<
/div>
*/// 以上面的例子為例var test = document.createElement('div');
// 建立一個塊級元素test.setAttribute("id","test");
// 設定其id 屬性var span = document.createElement('span');
// 建立一個 spanspan.innerText = "Hello,world";
// 插入 span 的文字內容test.appendChild(span);
// 組合節點element.appendChild(test);
//追加到某個節點區域複製程式碼
Q: 給一個 DOM
新增捕獲和冒泡的兩種寫法的事件點選,誰先執行?
分情況分析:
- 有拿到節點的,優先捕獲,沒有才往上冒泡尋找
- 若是通過
node.addEventListener('event',callback,bubble or capture)
;
誰先呼叫誰先執行
stackoverflow 有相關的探討:
Q: 談談你對ajax 的理解,以及用原生 JS 實現有哪些要點需要注意;
ajax
全稱是非同步 javascript 和 XML
,用來和服務端進行資料互動的,讓無重新整理替換頁面資料成了可能;
至於有哪些要要點,來一個簡短的ajax
請求
var xhr = new XMLHttpRequest();
// 宣告一個請求物件xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
// readyState 4 代表已向伺服器傳送請求 if(xhr.status === OK){
// // status 200 代表伺服器返回成功 console.log(xhr.responseText);
// 這是返回的文字
} else{
console.log("Error: "+ xhr.status);
// 連線失敗的時候丟擲錯誤
}
}
}xhr.open('GET', 'xxxx');
// 如何設定請求頭? xhr.setRequestHeader(header, value);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(null);
// get方法 send null(亦或者不傳,則直接是傳遞 header) ,post 的 send 則是傳遞值複製程式碼
更為詳細的可以閱讀此處;
Q: JS 實現一個閉包函式,每次呼叫都自增1;
這裡主要考察了閉包,函式表示式以及 IIFE(立即執行表示式)
var add = (function() {
// 宣告一變數,由於下面 return所以變數只會宣告一次 var count = 0;
return function() {
return console.log(count++);
};
})();
add();
// 0add();
// 1add();
// 2複製程式碼
Q: [‘1′,’2′,’3’].map(parseInt) 輸出什麼,為什麼?
['1','2','3'].map(parseInt);
// [1,NaN,NaN]// 刨析// map有三個引數:陣列元素,元素索引,原陣列本身// parseInt有兩個引數,元素本身以及進位制// 理清了這兩個就好辦了...// ['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複製程式碼
Q: 對陣列 [‘2018-03-05’, ‘2013-06-12′,’2019-03-12′,’2018-03-05′,’2014-02-22’] 去重且排序
我這裡用的是結合 ES6
的,程式碼量很短
//很好理解, Set 具有值唯一性(但不是所有值,等會我丟擲我的另外一篇文章)// 結合...解構,可以把可迭代(比如 arguments/nodelist 等)的轉為陣列// sort 裡面傳入 兩個值比較,返回-1和1是因為1代表這個數大排後(相對),-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"]複製程式碼
對於陣列去重的,有興趣的可以看下我這篇水文:
Q: 對陣列[1,2,3,4,5,'6',7,'8','a','b','z']
進行亂序
// 我們依舊可以用上面的 sort 的原理實現亂序let tempArr = [1,2,3,4,5,'6',7,'8','a','b','z'].sort(function(){
return Math.random() >
0.5 ? -1 : 1;
})// 因為裡面有隨機數,所以答案沒有標準答案,我這邊跑了一次是輸出這個//["6", "z", 3, "b", 5, 2, 7, "8", "a", 1, 4]複製程式碼
上面和這道題逗涉及到陣列順序的問題,想了解下為什麼 a-b
,a>
這類可以更改排序
b
可以看看知乎對於這塊的探討: 傳送門:javascript排序return a-b?
Q: 求[1, 10, 11, -1,'-5',12, 13, 14, 15, 2, 3, 4, 7, 8, 9]
內最大值與最小值之差
// 來一個很粗糙的版本,只當傳入是陣列且可以隱性轉為數字的function MaxMinPlus(arr) {
// 返回最大值與最小值之差 return Array.isArray(arr) ? Math.max.apply(Math, arr) - Math.min.apply(Math, arr) : console.log('傳入的不是陣列亦或者未能解決的錯誤')
}// 結果是 20// 若是要完善的話,要考慮傳入的是非陣列,//傳入字串的時候要判斷,然後切割為陣列..// 都要考慮進去程式碼量不短複製程式碼
Q: 請給Array
實現一個方法,去重後返回重複的字元(新陣列)
var testArr = [1,6,8,3,7,9,2,7,2,4,4,3,3,1,5,3];
Array.prototype.extraChar = function(){
var cacheExtraChar = [];
// 快取重複出現的字元 var that = this;
// 快取 this;
this.map(function(item,index){
// 怎麼理解這段程式碼呢? // 就是向前往後查詢一遍和從後往前查詢一遍,不等就是沒有重複 // 為什麼還要判斷一遍快取,是過濾快取陣列內多次寫入 (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]複製程式碼
Q: 一個陣列中 par中存放了多個人員的資訊,每個人員的資訊由 name
和 age
構成({name:'張三',age:15
}
).請用 JS 實現年齡從小到大的排序;
}
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;
})複製程式碼
Q: 判斷一個迴文字串和同字母異序字串
- 迴文字串就是正序倒序都是一樣的;
- 同字母異序字串則是字串都一樣,但是位置可能不一定一樣,比如
abcefd
和dceabf
=>return true
後者的思路就是用排序把異序扭正..
普通版
// 迴文判斷 , 比如用 abcbavar 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('')
}複製程式碼
進階版:多一些特殊字元
若是我們要去除所有非字母數字的字元,則需要用到正則
// 進階版: isPalindromes('abc_ &
b #@a')var isPalindromes = function(params){
// 傳入引數先轉為字串且全部轉為小寫,最後去除多餘字元比較 params = params.toString().toLowerCase().replace(/[\W_\s]/g,'');
console.log(params) return params === params.split('').reverse().join('');
}// 進階版同字母異序: 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('')
}複製程式碼
Q: JS 實現String.trim()
方法;
// 原生是有 trim()方法的.我們要模擬一個;
String.prototype.emuTrim = function(){
// 這條正則很好理解,就是把頭部尾部多餘的空格字元去除 return this.replace(/(^\s*)|(\s*$)/g,'');
}' fsaf fsdaf f safl lllll '.emuTrim();
//"fsaf fsdaf f safl lllll" 複製程式碼
Q: JS 實現函式執行一秒後列印輸出0-9;
給定如下程式碼
for(var i=0;
i<
10;
i++){
// TODO
}複製程式碼
- 解法
// 這道題涉及到作用域for(var i=0;
i<
10;
i++){
setTimeout((function(i){
return function(){
console.log(i);
}
})(i),1000);
}複製程式碼
若是用到 ES6…那簡直不能再簡便了
for(let i=0;
i<
10;
i++){
setTimeout(function(){
console.log(i);
},1000);
}複製程式碼
Q: 實現對一個陣列或者物件的淺拷貝和”深度”拷貝
淺拷貝就是把屬於源物件的值都複製一遍到新的物件,不會開闢兩者獨立的記憶體區域;
深度拷貝則是完完全全兩個獨立的記憶體區域,互不干擾
- 淺拷貝
// 這個 ES5的function shallowClone(sourceObj) {
// 先判斷傳入的是否為物件型別 if (!sourceObj || typeof sourceObj !== 'object') {
console.log('您傳入的不是物件!!')
} // 判斷傳入的 Obj是型別,然後給予對應的賦值 var targetObj = sourceObj.constructor === Array ? [] : {
};
// 遍歷所有 key for (var keys in sourceObj) {
// 判斷所有屬於自身原型鏈上的 key,而非繼承(上游 )那些 if (sourceObj.hasOwnProperty(keys)) {
// 一一複製過來 targetObj[keys] = sourceObj[keys];
}
} return targetObj;
} // ES6 可以用 Object.assign(targeObj, source1,source2,source3) 來實現物件淺拷貝 複製程式碼
- 深度拷貝
// 就是把需要賦值的型別轉為基本型別(字串這些)而非引用型別來實現// JOSN物件中的stringify可以把一個js物件序列化為一個JSON字串,parse可以把JSON字串反序列化為一個js物件var deepClone = function(sourceObj) {
if (!sourceObj || typeof sourceObj !== 'object') {
console.log('您傳入的不是物件!!');
return;
} // 轉->
解析->
返回一步到位 return window.JSON ? JSON.parse(JSON.stringify(sourceObj)) : console.log('您的瀏覽器不支援 JSON API');
};
複製程式碼
Q: this物件的理解
簡言之:誰呼叫指向誰,執行時的上下文確定,而非定義的時候就確定;
強行繫結 this
的話,可以用 call
,apply
,bind
,箭頭函式….來修改this
的指向
這類的文章太多,自行搜尋吧….
Q: 看到你說到 bind
,能用 JS簡單的模擬個麼?
Function.prototype.emulateBind = function (context) {
var self = this;
return function () {
return self.apply(context);
}
}複製程式碼
這個實現很粗糙…更為詳細全面,考慮周全的(比如引數的處理什麼的)…自行谷歌.
Q:JS 的作用域是什麼?有什麼特別之處麼?
作用域就是有它自身的上下文區域(比如函式內),內部會有變數宣告提升,函式宣告提升這些;
函式宣告提升優於變數宣告提升..
作用域有全域性作用域和塊級作用域(區域性,比如用 let 或者單純花括號的);
作用域會影響this
的指向
坐等補充,我回答的時候,面試大佬只是 嗯..恩…恩…也不知道具體如何
Q: 怎麼解決跨域問題,有哪些方法…
我一般用這三種,cors
,nginx反向代理
,jsonp
jsonp
: 單純的 get 一些資料,侷限性很大…就是利用script標籤的src屬性來實現跨域。nginx 反向代理
: 主要就是用了nginx.conf
內的proxy_pass http://xxx.xxx.xxx
,會把所有請求代理到那個域名,有利也有弊吧..cors
的話,可控性較強,需要前後端都設定,相容性 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: 對於想攜帶一些鑑權資訊跨域如何走起?比如cookie
!
需要配置下 header Access-Control-Allow-Credentials:true
,具體用法看下面的nginx
demo
當然cros
的配置不僅僅這些,還有其他一些,具體引擎吧….
若是我們要用 nginx
或者 express
配置cors
應該怎麼搞起? 來個簡易版本的
- 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;
} 複製程式碼
- express, 當然這貨也有一些別人封裝好的
cors
中介軟體,操作性更強…
let express = require('express');
let app = express();
//設定所有請求的頭部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();
});
複製程式碼
有些還會跟你死磕,,除了這些還有其他姿勢麼…我說了一個HTML5的postMessage
….
..因為真心沒用過,只是以前查閱的時候瞭解了下..只能大體點下
這貨用於iframe
傳遞訊息居多, 大體有這麼兩步步
window
開啟一個例項,傳遞一個訊息到一個x域名- x 域名下監聽
message
事件,獲取傳遞的訊息
這貨的相容性沒那麼好,而且沒考慮周全下容易遭受 CSRF
攻擊
Q: 對於XSS
和 CSRF
如何防範
這裡就不說概念性的東西了
-
XSS的防範
- 我能想到的就是轉義
<
這些造成程式碼直接執行的的標籤..輪詢或者正則替換
>- 而面試官說這種的效率最低下,我回來仔細找了找相關資料好像沒有更優方案…有的留言…
- 若是有用到
cookie
,設定為http-only
,避免客戶端的篡改
- 我能想到的就是轉義
-
CSRF的防範一般這幾種
- 驗證碼,使用者體驗雖然不好,,但是很多場合下可以防範大多數攻擊
- 驗證 HTTP Referer 欄位,判斷請求來源
- token加密解密,這種是目前很常用的手段了…
任何防範都有代價的,比如驗證碼造成的體驗不好,token
濫用造成的效能問題,輪詢替換造成的響應時間等
Q: 描述下cookie
,sessionStorage
,localStorage
的差異..
cookie
: 大小4KB 左右,跟隨請求(請求頭),會佔用頻寬資源,但是若是用來判斷使用者是否線上這些挺方便sessionStorage
和localStorage
大同小異,大小看瀏覽器支援,一般為5MB,資料只保留在本地,不參與服務端互動.sessionStorage
的生存週期只限於會話中,關閉了儲存的資料就沒了.localStorage
則保留在本地,沒有人為清除會一直保留
Q: javascript
的原型鏈你怎麼理解?
原型鏈算是 JS 內一種獨有的機制,
所有物件都有一個內建[[proto]]
指向建立它的原型物件(prototype
)
原型鏈的基本用來實現繼承用的
Q: javascript
裡面的繼承怎麼實現,如何避免原型鏈上面的物件共享
我在寫的時候,用了兩種,一個是 ES5和 ES6的方案
- ES5:寄生組合式繼承:通過借用建構函式來繼承屬性和原型鏈來實現子繼承父。
function ParentClass(name) {
this.name = name;
} ParentClass.prototype.sayHello = function () {
console.log("I'm parent!" + this.name);
} function SubClass(name, age) {
//若是要多個引數可以用apply 結合 ...解構 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){
//定義了一個臨時建構函式 function F() {
} //將這個臨時建構函式的原型指向了傳入進來的物件。 F.prototype = obj;
//返回這個建構函式的一個例項。該例項擁有obj的所有屬性和方法。 //因為該例項的原型是obj物件。 return new 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')複製程式碼
Q: ES6+你熟悉麼,用過哪些特性?
- 箭頭函式
- 類及引入匯出和繼承(
class
/import
/export
/extends
) - 字串模板
- Promise
let
,const
async
/await
- 預設引數/引數或變數解構裝飾器
Array.inclueds
/String.padStart|String.padEnd
/Object.assign
Q: let 和 const 有啥差異?
let
會產生塊級作用域,不會造成變數提升,無法重新宣告(但可以重新賦值);const
- 是常量,若是基本資料型別,具有不變性(無法重新賦值改動)
- 引用值可以調整內部值(可能設計的時候沒有考慮周全!
Q: async
和await
的用途?
- 讓
promise
的非同步變成同步執行成了可能,await
可以等到promise
執行完畢
Q: 箭頭函式的this
指向誰?
肯定很多小夥伴會說指向區域性方法內!!答案是錯誤的…
箭頭函式所改變的並非把 this 區域性化,而是完全不把 this 繫結到裡面去;
就是 this 是取自外部的上下級作用域(但是又不是常規 function
的語法糖)..
因為箭頭函式裡並不支援 var self = this
或者 .bind(this)
這樣的寫法。
Q: 問的時候你用過靜態方法,靜態屬性,私有變數麼?
靜態方法是ES6之後才有這麼個玩意,有這麼些特點
- 方法不能給 this引用,可以給類直接引用
- 靜態不可以給例項呼叫,比如
let a = new ParentClass =>
a.sayHello() 會丟擲異常 - 父類靜態方法,子類非
static
方法沒法覆蓋父類 - 靜態方法可以給子類繼承
- 靜態屬性可以繼承也可以被修改
看下面的程式碼..
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');
複製程式碼
私有變數這個我沒答出來,只是說了下沒有private
這個關鍵字和基本用下劃線的人為區分
所以回來只是找了下相關的資料,發現有一個比較好的模擬方案,就是WeakMap
;
WeakMap
可以避免記憶體洩露,當沒有被值引用的時候會自動給記憶體暫存器回收了.
const _ = new WeakMap();
// 例項化,value 必須為物件,有 delete,get,has,set四個方法,看名字都知道了class TestWeakMap {
constructor(id, barcode) {
_.set(this, {
id,barcode
});
} testFunc() {
let {
id,barcode
} = _.get(this);
// 獲取對應的值 return {
id,barcode
};
}
}複製程式碼
當然你也可以用Symbol
來實現一個私有變數,這也是一個好法子
Q: 談談你對 Promise 的理解? 和 ajax 有關係麼?
Promise
和ajax
沒有半毛錢直接關係.promise
只是為了解決”回撥地獄”而誕生的;
平時結合 ajax
是為了更好的梳理和控制流程…這裡我們簡單梳理下..
Promise
有三種狀態,Pending/resolve()/reject();
一些需要注意的小點,如下
- 在
Pending
轉為另外兩種之一的狀態時候,狀態不可在改變.. Promise
的then
為非同步.而(new Promise()
)建構函式內為同步Promise
的catch
不能捕獲任意情況的錯誤(比如then
裡面的setTimout
內手動丟擲一個Error
)Promise
的then
返回Promise.reject()
會中斷鏈式呼叫Promise
的resolve
若是傳入值而非函式,會發生值穿透的現象Promise
的catch
還是then
,return
的都是一個新的Promise
(在 Promise 沒有被中斷的情況下)
Promise 還有一些自帶的方法,比如race
,all
,前者有任一一個解析完畢就返回,後者所有解析完畢返回…
實現一個延時的 promise 函式, 可以用
async
和await
const delay = (time)=>
new Promise((resolve,reject)=>
{
setTimeout(resolve,time)
})// testlet testRun = async function(){
console.log(1);
await delay(2000);
console.log('我兩秒後才觸發',3)
} // 1 =>
Promise = >
3複製程式碼
這段程式碼的執行結果是什麼?
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()的時候已經中斷了鏈式呼叫.直接給 catch捕獲到複製程式碼
別急,假如你不管有沒有捕獲到錯誤,最後再執行一個回撥函式如何實現?
這裡說的就是類似try..catch..finally
,給Promise
實現一個 finally;
// finally比較好加,按照現在社群的討論,finally的特點如下: // url : https://www.v2ex.com/t/205715 //1. 不接收任何引數,原來的value或者Error在finally裡是收不到的 //2. 處理後不影響原Promise的狀態,該reject還是reject,該resolve還是resolve //3. 不影響Promise向後傳遞的傳,resolve狀態還是傳遞原來的value,reject狀態還是傳遞原來的Error Promise.prototype.finally = function (callback) {
let P = this.constructor;
// 這裡拿到的是 Promise 的建構函式 //不管前面的 Promise 是fulfilled還是rejected,都會執行回撥函式callback。 return this.then( value =>
P.resolve(callback()).then(() =>
value), reason =>
P.resolve(callback()).then(() =>
{
throw reason
}) );
};
// 用法很簡單,就是可以傳入一個回撥函式..// https://developers.google.com/web/updates/2017/10/promise-finally// 這個 url 中說了 node 及 chrome 的哪些版本已經實現了 finally 及用法// ES 2018已經把 finally 追加到 promise 的原型鏈中..複製程式碼
- <
<
Promise 必知必會(十道題)>
>
: 有助於你更加深刻的瞭解 promise 的執行情況 - 關於 Promise 的 9 個提示
- 更多的Promise 詳情可以參考<
<
JavaScript Promise迷你書(中文版)>
>
;
Q: 談談你對 TCP 的理解;
Q: TCP 是在哪個OSI 的哪個層!通訊過程是全雙工還是半雙工(單工)?
A: 傳輸層,全雙工
Q: TCP的通訊的過程是怎麼樣的!
A: 整個過程是三次握手,四次揮手..
Q: 你說的沒錯,說說整個過程如何?
A: 舉個例子,我把 TCP 比做兩個人用對講機溝通(大白話)..三次握手就是.A1(吼叫方,客戶端)想要呼叫 A2(控制室的某某,服務端)..
A1對著對講機說”over over ,聽到請回答”(第一次,請求應答) …
A2收到回應”收到收到,你說”(第二次,確認應答)
A1開始巴拉巴拉個不停而 A2沒拒絕(第三次,通訊建立)
而四次揮手則是兩者確認互相傾述完畢的過程..
A1說:”控制室,報告完畢了”(第一次揮手)
A2說:”知道了…那麼你廢話說完就好好聽我指揮….巴拉巴拉..”(第二次揮手)
A1此時等待控制室說完畢,而控制室等回應(第三次揮手)
等到 A1回饋控制室確認都知道完畢了..(第四次揮手)…
以上都是瞎掰,可能有些地方描述不當,笑笑就好了
TCP
沒有百分百建立成功的,會造成連結失敗的情況有很多..
比如長時間沒應答(A1吼了半天沒有反應或者 A2應答了而 A1不再鳥它)..亦或者丟包(對講機也沒了);
TCP
協議相關的文章網上很多,若是要更加全面的瞭解該協議請自行引擎..
我建議閱讀<
<
TCP-IP詳解卷1~卷3>
>
,這個是網路聖經…很厚…我只看了一丟丟..
Q: TCP 你瞭解了,那麼 OSI 七層協議和五層網路架構應該知道吧?
對於這類的問題我也只能大體點了下,畢竟不是專攻網路這塊的…
OSI
七層涵蓋:物理層,資料鏈路層,網路層,傳輸層,會話層,表示層,應用層;
五層模型就是”會話,表示,應用層”同為一層;
Q: DNS
的大體的執行流程瞭解麼,屬於哪個層級?工作在哪個層級?
DNS
屬於應用層協議, 至於TCP/UDP
哪一層上面跑,看情況 , 大體的執行流程是這樣的;
DNS 預設埠是53,走 UDP
- 優先讀取瀏覽器快取
- 其次系統的快取
- 都沒有的情況下,找本地
hosts
檔案(比如你寫了對映關係優先尋找) - 再沒有的情況找最近的域名解析伺服器
- 再沒有則擴大訪問,最終找到根伺服器,還是沒有就失敗了..
DNS 的解析的幾個記錄型別需要了解:
A
: 域名直接到 IPCNAME
: 可以多個域名對映到一個主機,類似在Github Page
就用CNAME
指向MX
: 郵件交換記錄,用的不多,一般搭建郵件伺服器才會用到NS
: 解析服務記錄,可以設定權重,指定誰解析TTL
: 就是生存時間(也叫快取時間),一般的域名解析商都有預設值,也可以人為設定TXT
: 一般指某個主機名或域名的說明
回來我找下相關的資料,有興趣的可以深入瞭解下,傳送門如下:
Q: HTTP 和 HTTPS 有何差異? 聽說過 SPDY 麼?
我只是粗淺的回答了下…
HTTP
相對於 HTTPS
來說,速度較快且開銷較小(沒有 SSL/TSL) 對接,預設是80埠;
HTTP
容易遭受域名劫持,而HTTPS
相對來說就較為安全但開銷較大(資料以加密的形式傳遞),預設埠為443..
HTTP
是明文跑在 TCP
上.而HTTPS
跑在SSL/TLS
應用層之下,TCP
上的
Q: 那麼 HTTPS
中的TLS/SSL
是如何保護資料的…
一般有兩種形式,非對稱加密,生成公鑰和私鑰,私鑰丟伺服器,公鑰每次請求去比對驗證;
更嚴謹的採用 CA(Certificate Authority),給金鑰簽名….
Q: 你說到對稱加密和非對稱加密,能說說整個流程如何運轉的麼(HTTPS)
- 對稱加密:
- 雙方都有同樣的金鑰,每次通訊都要生成一個唯一金鑰,速度很快
- 安全性較低且金鑰增長的數量極快
- 非對稱加密(一般用 RSA)
- 安全性很高,對資源消耗很大(CPU),目前主流的加密演算法(基本用於交換金鑰或簽名,而非所有通訊內容)
- CA(數字簽名):
- 這個是為了防止中間人給偷換了造成資料被竊取而誕生的
- 用一些權威機構頒佈的演算法來簽名,權威機構做中間人,通訊過程都會跟機構核對一遍
懂得真心不多,回來找了下相關資料,有興趣可以點選看看;
- 深入揭祕HTTPS安全問題&
連線建立全過程 - 深入理解 https 通訊加密過程:口語化 ” : 看了上面那篇文章來看下面,會清晰很多
Q: SPDY
聽說過麼.什麼來的?
谷歌推行一種協議(HTTP
之下SSL之上[TCP]),可以算是HTTP2的前身,有這麼些優點
- 壓縮資料(HEADER)
- 多路複用
- 優先順序(可以給請求設定優先順序)
而這些優點基本 HTTP2也繼承下來了..
Q: 你對 HTTP 的狀態嗎瞭解多少…
這裡列舉一丟丟常見的..
- 1XX: 一般用來判斷協議更換或者確認服務端收到請求這些
- 100: 服務端收到部分請求,若是沒有拒絕的情況下可以繼續傳遞後續內容
- 101: 客戶端請求變換協議,服務端收到確認
- 2xx: 請求成功,是否建立連結,請求是否接受,是否有內容這些
- 200: (成功)伺服器已成功處理了請求。
- 201: (已建立)請求成功並且伺服器建立了新的資源。
- 202: (已接受)伺服器已接受請求,但尚未處理。
- 204: (無內容)伺服器成功處理了請求,但沒有返回任何內容。
- 3XX: 一般用來判斷重定向和快取
- 301: 所有請求已經轉移到新的 url(永久重定向),會被快取
- 302: 臨時重定向,不會被快取
- 304: 本地資源暫未改動,優先使用本地的(根據
If-Modified-Since or If-Match
去比對伺服器的資源,快取)
- 4XX: 一般用來確認授權資訊,請求是否出錯,頁面是否丟失
- 400: 請求出錯
- 401: 未授權,不能讀取某些資源
- 403: 阻止訪問,一般也是許可權問題
- 404: 頁面丟失,資源沒找到
- 408: 請求超時
- 415: 媒介型別不被支援,伺服器不會接受請求。
- 5XX: 基本都是服務端的錯誤
- 500: 服務端錯誤
- 502: 閘道器錯誤
- 504: 閘道器超時
Q: HTTP的請求報文是怎麼樣的,能大體的說下麼?
HTTP 的請求報文 = 請求行 + 請求頭 + 請求體;
- 請求行: 這個好理解就是訪問的方法+ 協議+ 訪問的 URL 構成
- 請求頭: 這個也好理解,比如
accept
,content-type
,user-agent
這類值鍵對,服務端可以直接讀取的 - 請求體: 比如 POST 提交的一個表單,我們編碼後放在上面需要傳遞的
想深入瞭解的具體引擎搜尋
Q: 請求報文知道,那你說說cookie
是如何跟隨請求的?
Cookie 就是儲存在 HTTP 協議的請求或者應答頭部(Cookie 是由服務端生成),這樣一路漂泊…
Q: Cookie 隔離是什麼,如何做;
cookie 隔離就是降低
header
的資料包含,以達到加快訪問速度的目的
方案: 靜態資源丟 CDN或者非主域來載入
Q: 瀏覽器快取和服務端的快取控制你瞭解多少,說說看?
Last-Modified
:- 第一次請求資源從伺服器拉取的會自動帶上該屬性
- 第二次請求會跟服務端比對
If-Modified-Since
的時間,沒變動則使用本地的(狀態304) - 結合
Expires(過期時間:快取的載止時間)
,跟隨請求一起發出..資源沒過期拿本地,否則重新請求
Cache-control
是HTTP1.1
的東西,判斷資源過期結合max-age
來替代Expires[http 1.0]
Etag
:- 第一次請求url 時候會給伺服器上標記(一串字串)
- 第二次請求時候會比對服務端的
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 definedconsole.log(c,b) // {a: 1
} {a: 1
}複製程式碼
var count = 0;
console.log(typeof count === "number");
// true , 這個不用解釋了console.log(!!typeof count === "number");
// false// 這裡涉及到就是優先順序和布林值的問題// typeof count 就是字串"number"// !!是轉為布林值(三目運算子的變種),非空字串布林值為 true// 最後才=== 比較 , true === "number" , return false複製程式碼
(function(){
var a = b = 3;
})()console.log(typeof a === "undefined");
// falseconsole.log(typeof b === "undefined");
// false// 這裡涉及的就是立即執行和閉包的問題,還有變數提升,運算子執行方向(=號自左向右)// 那個函式可以拆成這樣(function() var a;
/* 區域性變數,外部沒法訪問*/ b = 3;
/* 全域性變數,so . window.b === 3 , 外部可以訪問到*/ a = b;
})()// 若是改成這樣,這道題應該是對的console.log(typeof b === "number" &
&
b ===3);
// true複製程式碼
function foo(something){
this.a = something;
}var obj1 = {
foo:foo
};
var obj2 = {
};
obj1.foo(2)console.log(obj1.a) // 2 ,此時的 this 上下文還在 obj1內,若是 obj1.foo 先儲存當做引用再執行傳參,則上下文為 windowobj1.foo.call(obj2,3);
// 用 call 強行改變上下文為 obj2內console.log(obj2.a);
// 3var bar = new obj1.foo(4);
// 這裡產生了一個例項console.log(obj1.a);
// 2console.log(bar.a);
// 4;
new的繫結比隱式和顯式繫結優先順序更高複製程式碼
function fn(){
alert(a);
var a = 200;
alert(a);
}fn();
// undefined / 200 ;
涉及變數提升alert(a);
// undefinedvar a;
alert(a);
// undefinedvar a = 300;
alert(a);
// 300複製程式碼
var obj1= {
name:'obj1', fn:function(){
console.log(this.name);
}
};
var obj2 = {name:'obj2'
};
var obj3 = {name:'obj3'
};
// 這道題主要涉及的是 this 指向的問題..obj1.fn();
// obj1var newFn = obj1.fn;
newFn();
// undefined, this 指向 windownewFn.call(obj2);
// obj2, this 指向 obj2obj3.fn = newFn;
/*ƒ (){
console.log(this.name);
}*/obj3.fn();
// 這裡指向的是 obj3 .所以輸出 obj3複製程式碼
// 這道題來作為筆試題很繞,因為要回答的答案很多(腦海構思)..反正我是遇到了..// 這道題主要考核的是對原型鏈繼承這塊的理解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;
// 這前面幾個還算簡單,繼續看下去parent.show();
// 15:1,2,1// 因為 Child 自身沒有 show 的方法,所以往原型鏈的上游找;
// 找到父類的,this 因為沒更改,所以輸出結果如下child1.show();
// 115:1,2,1child2.show();
// 125:1,2,1child1.change();
// 改變一些資料,沒有輸出child2.change();
// +1parent.show();
// 15:1,2,1child1.show();
// 55:1,2,1,11,12child2.show();
// 65:1,2,1,11,12複製程式碼
// 這道題也很繞,函式遞迴呼叫的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/201var c = test(102).test(202);
// undefined / 102c.test(302);
// 202複製程式碼
Q:有字串 var test=’abc345efgabcab’;
請根據提示實現對應要求
- 去掉字串中的 a,b,c 字元 ,形成結果’345efg’;
test.replace(/[abc]/g,'');
// "345efg"複製程式碼
- 將字串的數字用括號括起來, 形成結果: abc[3][4][5]efg….’
test.replace(/\d/g,'[$&
]');
// "abc[3][4][5]efgabcab"// 若是有分組則按照$1, $2, $3的形式進行引用,而 $&
則表示的是整個正規表示式匹配的內容。複製程式碼
- 將字串中的每個數字的值分別乘以2,輸出:’abc6810….’
var temp = test.split('').map(function(item){
return /^\d$/.test(item) ? item * 2 : item;
}).join('');
// "abc6810efgabcab"複製程式碼
Q: 使用不少於三種方式替換文字"dream"
改成"package"
,提供字串"I have a dream"
;
- 正則替換
// 這是最簡單的程式碼量了..var str = "I have a dream";
str.replace(/dream/g,"package");
// 不用正則也可以直接字串替換str.replace("dream","package")複製程式碼
- 陣列遍歷更改
// 很直白的大腦回路var str = "I have a dream";
str.split(" ").map(function(item){
return item === "dream" ? item = "package":item;
}).join(" ");
複製程式碼
- 陣列查詢切割法
var str = "I have a dream";
var tempArr = str.split(" ");
// ["I", "have", "a", "dream"]var removeIndex = tempArr.indexOf('dream');
// 3tempArr.splice(removeIndex,1,"package");
var transStr = tempArr.join(" ");
// "I have a package";
複製程式碼
這類東東弄成陣列還是挺好弄的
這個是留言區小夥伴提供的方法..大同小異,如下;
// 原始碼// 字串也有陣列的 slice 以及 concat 的方法..思路和陣列差不多var str = 'I haved a dream';
str.indexOf('dream') !== -1 ? str.slice(0,str.indexOf('dream')).concat('package'):str;
複製程式碼
Q: 還有一道題目是涉及事件迴圈,執行優先權的..
就是 macrotask
和microtask
相關的, 具體記不起來了..那時候給了答案雖然對了.
要說出所以然,給秀了一臉..回來找了下相關的資料;
Q: 你對基礎演算法這塊掌握的如何….
來,這紙給你,寫個快排試試…
// 快排的大體思路是這樣的,// 找個中位值,從原陣列切割出來,// 剩下的作為兩個陣列,每次都去比較;
// 直到遞迴的結果出來, 平均複雜度O(nlog n)function quickSort(arr) {
//如果陣列長度<
=1,則直接返回 if (arr.length <
= 1) {
return arr;
} // 中間位(基準)取長度的一半向下取整 var pivotIndex = Math.floor(arr.length / 2);
//把中間位從原陣列切割出來, splice 會改變原陣列!!!! var pivot = arr.splice(pivotIndex, 1)[0];
//定義兩個空陣列來存放比對後的值 var left = [];
var right = [];
//比基準小的放在left,比基準大的放在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] // 怎麼個遞迴法,就是比對後的陣列還是會重複之前的取基準再切開比較..直到最後沒有可以切了 return quickSort(left).concat([pivot], quickSort(right));
}複製程式碼
Q: 寫一個二分法查詢
// 二分法跟快排的思路差不多,對半比較// 這個只用於排序好陣列內的查詢,高低位都知道的情況下function binSearch(target, arr, start, end) {
var start = start || 0;
// 允許從什麼位置開始,下標 var end = end || arr.length - 1;
// 什麼位置結束,下標 start >
= end ? -1 : '';
// 沒有找到,直接返回-1 var mid = Math.floor((start + end) / 2);
// 中位下標 if (target == arr[mid]) {
return mid;
// 找到直接返回下標
} else if (target >
arr[mid]) {
//目標值若是大於中位值,則下標往前走一位 return binSearch(target, arr, start, mid - 1);
} else {
//若是目標值小於中位值,則下標往後退一位 return binSearch(target, arr, mid + 1, end);
}
}// binSearch(5,[1,2,3,4,5,6,7,8]) =>
4// 無序的陣列則需要先排序好陣列,否則會堆疊溢位(死迴圈)複製程式碼
這類的文章很多,有興趣的可以閱讀下面的一些文章
傳送門:
Q: 設計模式你瞭解多少?
Q: 思維擴充題: 你有兩個玻璃球,有個100米的高樓,求玻璃球在哪個樓層扔下會碎(用的次數最少);
問題的要點: 玻璃球碎(有限個數) ,確定樓層數 , 最少次數 =>
就是求最優的公式
在這道題上給秀的一臉,我的第一次的思路
先折半,就變成[1-50][51-100], 那就是 1+50 = 51次 …
面試大佬說,你用了快排的思路就肯定不是最優的..
憋了許久,想到開平方 }” class=”equation” src=”https://juejin.im/equation?tex=%5Csqrt%5B3%5D%7B1000%7D”> , 那最多不超過30次;
至於第一次丟球的位置如何確定, 就是開平之後的值作為一個區間.
若 N 個球和 M 米的大廈…第一次丟球的高度區間就是這個了知乎有人討論了這個問題;
但是高數還老師了..這種帖子看的一臉懵逼….抽空再好好研究下
Q: 你對優化這塊瞭解多少?
大體常見的手段瞭解.
比如從客戶端著手的:
- 壓縮程式碼(JS/CSS),壓縮圖片
- 合併一些小圖片(
css sprite
) - 若是打包的程式碼儘可能切割成多個
chunk
,減少單一chunk
過大 - 靜態檔案採用 cdn 引入
- HTTP的快取頭使用的合理
- 減小第三方庫的依賴
- 對於程式碼應該考慮效能來編寫,比如使用
requestAnimationFrame
繪製動畫,儘可能減少頁面重繪(DOM 改變) - 漸進升級,引入
preload
這些預載入資源 - 看情況用
service worker
來快取資源(比如移動端打算搞 PWA)
比如從服務端著手:
- 頻寬,域名解析, 多域名解析等
- 頁面做服務端渲染,減小對瀏覽器的依賴(不用客戶端解析)
- 漸進升級,比如引入 HTTP2(多路複用,頭部壓縮這些可以明顯加快載入速度)
當然,這是這些都是很片面的點到…實際工作中去開展要複雜的多;
比如我們要多個維度去考慮的話,要去優化 DOM 的繪製時間,資源的載入時間,域名解析這些;
要全面的優化一個專案是一個大工程…
Q: MySQL有哪些索引型別? 索引的資料結構儲存方式? MySQL和 MongoDB的差異
MySQL索引型別:
- 普通索引: 就普通的型別
- 唯一索引: 代表索引的值唯一不重複(允許有空值),相對於上面多了個
UNIQUE
- 主鍵索引:(建立表的跟隨建立,唯一索引,不允許有空值)
- 組合索引(就是將多個欄位都建立到一個索引)
索引有利有弊,用的好加快查詢速度,濫用索引會造成大量磁碟空間佔用,維護性也會增多;
索引不會包含null
的列;
索引的資料結構儲存方式,我只簡單瞭解過B-Tree
至於MySQL 和 MongoDB的差異;
前者是關係型資料庫, 後者非關係型資料庫(資料是以文件的方式儲存,值為 key-value
);
MySQL
應用層面很廣,有事務系統這些,連結串列查詢這些都很方便.經常作為很多系統的主力資料庫
而MongoDB
作為NoSQL
,雖然有些層面不如 MySQL
,但是應用層面也挺廣,比如結合前端做一些使用者的概要資訊的維護,一些快取資訊的維護.
em….後端瞭解不多,也能點到即止….大學的時候學過一些..都差不多還給老師….
Q: JS時間分段
給定一個時間段和步長,列舉該時間段內步長的劃分
例如:時間段
3:00-5:00
,步長為20
分鐘那麼返回的陣列為
['3:00-3:20', '3:20-3:40'....]
等
這類問題,一般都要先梳理好思路再來寫;
- 給定字串時間段,切割,轉換為分鐘
- 跨日及跨時問題
// 這個東東我的小夥伴也寫出來了.我的是在它的解答方式上加以註釋和對引數的判斷做了考慮// 他的解法方案在他的 github 上 https://github.com/lyh2668/blog/issues/1 , by lyh2668// 方便一些小夥伴的理解,以下程式碼包含ES6的姿勢(引數預設值,剪頭函式)let inputDateRange = (date, step = 30, separator = '-') =>
{
let startTime, endTime;
// 開始時間和結束時間 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]);
//傳入的結束時間
} else if (Object.prototype.toString.call(date) === '[object Array]') {
if (date.length === 2) {
startTime = time2min(date[0]);
// 傳入的開始時間 endTime = time2min(date[1]);
//傳入的結束時間
}
} else {
console.log('您傳入的也許不是一個時間段!!!');
} // 傳入的 step 是否為數字,否則截圖數字部分轉化 // 為什麼和 NaN 比較(自身不等性),若是傳入的連正則都沒法識別,那隻能給預設值了 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);
// 若是開始時間大於結束時間則結束時間往後追加一天 startTime >
endTime ? (endTime += 24 * 60) : '';
let transformDate = [];
// 儲存轉換後的陣列,時間分段 // 開始遍歷判斷,用 while while (startTime <
endTime) {
// 如果開始時間+步長大於結束時間,則這個分段結束,否則結束時間是步長遞增 let right = startTime + step >
endTime ? endTime : startTime + step;
transformDate.push(`${min2time(startTime)
}-${min2time(right)
}`);
startTime += step;
// 步長遞增
} return transformDate;
};
// 時間轉化為分鐘let time2min = time =>
{
// 獲取切割的 time.indexOf(':') ? (time = time.trim().split(':')) : '';
return time[0] * 60 + parseInt(time[1]);
// 返回轉化的分鐘
};
// 分鐘轉會字串時間let min2time = minutes =>
{
let hour = parseInt(minutes / 60);
// 返回多少小時 let minute = minutes - hour * 60;
// 扣除小時後剩餘的分鐘數 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"]// 亦或者陣列傳入inputDateRange(['3:00','5:00'],'45df.3d');
// ["03:00-03:45", "03:45-04:30", "04:30-05:00"]// step 支援數字亦或者帶特殊字元的數字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"]複製程式碼
Q: Vue-Router
的兩種模式主要依賴什麼實現的
hash
主要依賴location.hash
來改動 URL,達到不重新整理跳轉的效果.每次hash
改變都會觸發hashchange
事件(來響應路由的變化,比如頁面的更換)history
主要利用了HTML5
的history
API 來實現,用pushState
和replaceState
來操作瀏覽歷史記錄棧
Q: MVVM 和 MVC 的差異? 聽說過 MVP?
這類的文章好多,三個開發模式的誕生都有前後,不是同時出現的.
傳送門:
Q: 求100~999的所有”水仙花”數, 就是三位數中各數字的立方和等於自身,比如153=1^3+5^3+3^3
- 常規遍歷法
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]複製程式碼
- 擴充寫法,ES6版+不定花數,不折騰不舒服版本
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]複製程式碼
這種是窮舉遍歷,若是要快一點呢(考慮的周全一點呢),以及傳參範圍的矯正
相信小夥伴都看得懂,我已經儘量註釋了..
let manyWaterFlower = (flower = 3,rangeStart, rangeEnd ) =>
{
let temp = [];
// 快取所有找到的花值 // 這一段就是填充開始迴圈的範圍,處理完畢後轉為數字,推薦的開始值 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;
} // 判斷是否有傳入結束值 if (rangeEnd) {
rangeEnd >
flowerRecommandEnd ? (rangeEnd = flowerRecommandEnd) : rangeEnd;
} else {
rangeEnd = flowerRecommandEnd;
} // 若是初始值大於結束值 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 的風扇狂叫....console.time('8 flower')manyWaterFlower(8);
console.timeEnd('8 flower')// VM644:3 8 flower: 68010.26489257812ms// 對了我們還沒有考慮數值溢位的問題..因為正整數在 JS 的範圍是有限的.// 有興趣的小夥伴可以自行完善複製程式碼
Q: 請使用遞迴演算法在 TODO 註釋後實現通過節點 key 陣列尋找 json 物件中的對應值
比如console.log(findNode([‘a1’, ‘b2’], data)) === data.a1.b2
// 請使用遞迴演算法在 TODO 註釋後實現通過節點 key 陣列尋找 json 物件中的對應值var data = {
a1: {
b1: 1, b2: 2, b3: {
b4: 5
}
}, a2: {
b1: 3, b2: 4
}
};
function findNode(inPath, inData) {
// TODO // 判斷傳入的是否是一個陣列 if (Array.isArray(inPath)) {
// 當長度為1的時候尋找該 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('您傳入的不是一個陣列')
}
}console.log(findNode(['a1', 'b2'], data));
// 2console.log(findNode(['a1', 'b3','b4'], data));
// 5複製程式碼
- 來個擴充版?支援字串或陣列傳入;
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);
// 判斷傳入的是否是一個陣列 if (Array.isArray(inPath)) {
// 當長度為1的時候尋找該 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('您傳入的不是一個陣列');
}
}console.log(findNode(['a1', 'b2'], data));
// 2console.log(findNode('a1.b3.b4', data));
// 5 複製程式碼
Q: webpack 是什麼?webpack 常見的優化手段有哪些;
webpack 是一個資源處理工具,它的出現節省了我們的人力和時間;
可以對資源打包,解析,區分開發模式等等…
常見的優化手段:
- 分離第三方庫(依賴),比如引入
dll
- 引入多程式編譯,比如
happypack
- 提取公共的依賴模組,比如
commonChunkPlugin
- 資源混淆和壓縮:比如
UglifyJS
- 分離樣式這些,減小
bundle chunk
的大小,比如ExtractTextPlugin
- GZIP 壓縮,在打包的時候對資源對齊壓縮,只要部署的伺服器能解析即可..減少請求的大小
- 還有按需載入這些,一般主流的框架都有對應的模組懶載入方式.
- 至於
tree shaking
目前webpack3/4已經預設整合
Q: 從你輸入一個 URL 到頁面渲染的大體過程…
大體過程是這樣的,想了解很細緻的可以自行引擎;
- IP->
DNS(瀏覽器=>
系統快取=>
DNS 伺服器)->
域名解析完成(這一步不用太多解析吧) - TCP 協議走完->
HTTP(S) 協議->
快取->
(分析請求頭)->
回饋報文 - 請求文件下來->
DOM->
CSSDOM->
靜態資源下載->
render(繪製文件)->
js 解析 - 使用者看到頁面
Q: Vue 的元件的通訊手段有哪些..
- 父->
子:props
- 子->
父:on
+emit
- 父<
>
子:on.sync
(語法糖)來的 - 兄弟 :
event bus
|vuex
Q: Vuex
你怎麼理解?
vuex
是一個狀態管理容器(你也可以理解為全域性變數),資料的流向是是單向資料流,
且資料並不具有持久化的特性(預設情況下重新整理就重置所有狀態);
裡面的一些資料乃至方法,可以大致理解為 vue 的一些特性,比如
Vuex | Vue |
---|---|
state | data |
getter | computed |
mutation/actions | methods |
至於單向資料流(全域性單例模式)怎麼理解
state
只能給mutation(同步操作)
改動,action
只能反饋給mutation
,可以進行非同步操作(比如和後端互動拉取資料),state
能觸發 render,action
能用dispatch
分發..如圖
結語
還有一些題目記不起來了,就沒轍了…還有一些題目是看你個人發揮的,沒法寫,比如
- Q: 讓你來為公司的一個專案做技術選型,你會怎麼做,為什麼?
- Q:
React
,Angular
,Vue
的比較?- Q: 說說你對
VNode
的理解,diff
的過程;- Q:
Vue
的雙向繫結如何實現,用了什麼模式(訂閱模式),大體如何實現的..- Q:
cmd
/amd
/commonjs
的差異- Q: 小程式以及
React Native
的差異..等等
面試的過程中磕磕碰碰才能發現自身的很多不足和需要去努力的方向.
有不對之處請留言,會及時跟進修正,謝謝各位大佬
掘金技術徵文活動連結: juejin.im/post/5aaf2a…
來源:https://juejin.im/post/5a998991f265da237f1dbdf9